diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 6d9be6b71d..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,24 +0,0 @@ -# Handle line endings automatically for files detected as text and leave all -# files detected as binary untouched. -* text=auto - -# Files and directories with the attribute export-ignore won’t be added to -# archive files. See http://git-scm.com/docs/gitattributes for details. -.gitattributes export-ignore -.gitignore export-ignore -/*.neon export-ignore -/.github export-ignore -/.php-cs-fixer.dist.php export-ignore -/.readthedocs.yaml export-ignore -/Makefile export-ignore -/box.json export-ignore -/doc/ export-ignore -/docker-compose.yml export-ignore -/docker/ export-ignore -/flake.* export-ignore -/lib/**/Tests/ export-ignore -/phpbench.json export-ignore -/phpunit.xml.dist export-ignore -/test.php export-ignore -/tests/ export-ignore -/rector.php export-ignore diff --git a/.github/.vimrc b/.github/.vimrc deleted file mode 100644 index 982b722c36..0000000000 --- a/.github/.vimrc +++ /dev/null @@ -1,7 +0,0 @@ -filetype off -let s:phpactorRootDir = expand(':p:h:h') -let &runtimepath .= ',' . expand(s:phpactorRootDir . '/vader.vim') -let &runtimepath .= ',' . s:phpactorRootDir -let &runtimepath .= ',' . expand(s:phpactorRootDir . '/after') -filetype plugin indent on -syntax enable diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 34fbbad24b..0000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: dantleech diff --git a/.github/build-phar.sh b/.github/build-phar.sh deleted file mode 100755 index 92bd0cbd19..0000000000 --- a/.github/build-phar.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -e - -composer install --no-dev -mkdir -p build -cd build - -wget -O box.phar https://github.com/box-project/box/releases/download/4.5.0/box.phar -php box.phar compile -c ../box.json - -cd - - diff --git a/.github/phpbench_regression_test.sh b/.github/phpbench_regression_test.sh deleted file mode 100755 index 87207a797c..0000000000 --- a/.github/phpbench_regression_test.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -set -e - -RETRY_THRESHOLD=${RETRY_THRESHOLD:-5} - -echo -e "\n\n" -echo -e "Benchmarking master branch" -echo -e "==========================\n\n" -git fetch origin master &> /dev/null -git checkout master &> /dev/null -mv composer.lock composer.lock.pr -composer install --quiet -vendor/bin/phpbench run --report=aggregate --progress=travis --retry-threshold=$RETRY_THRESHOLD --tag=master - -echo -e "\n\n" -echo -e "Benchmarking GITHUB_REF and comparing to master" -echo -e "==================================================\n\n" -git checkout - &> /dev/null -mv composer.lock.pr composer.lock -composer install --quiet -vendor/bin/phpbench run --report=aggregate --progress=travis --retry-threshold=$RETRY_THRESHOLD --ref=master diff --git a/.github/vim-plugin-test.sh b/.github/vim-plugin-test.sh deleted file mode 100755 index 69a1354092..0000000000 --- a/.github/vim-plugin-test.sh +++ /dev/null @@ -1,2 +0,0 @@ -export PHPACTOR_UNCONDITIONAL_TRUST=1 -vim -Nu .github/.vimrc -c 'Vader! tests/VimPlugin/*' diff --git a/.github/workflows/benchmark-pr.yml b/.github/workflows/benchmark-pr.yml deleted file mode 100644 index b234e6a60b..0000000000 --- a/.github/workflows/benchmark-pr.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Benchmark PR - -on: - pull_request_target: - types: - - opened - - synchronize - - reopened - -jobs: - benchmark-pr: - name: Benchmark (PR comparison) - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - coverage: none - php-version: "8.2" - tools: composer:v2 - - - name: Composer install - uses: ramsey/composer-install@v2 - with: - composer-options: "--no-scripts" - - - name: Check gh-pages branch exists - id: check-gh-pages - run: | - if git ls-remote --exit-code --heads origin gh-pages > /dev/null 2>&1; then - echo "exists=true" >> "$GITHUB_OUTPUT" - else - echo "exists=false" >> "$GITHUB_OUTPUT" - echo "⚠️ gh-pages branch does not exist yet — skipping PR benchmark comparison" - fi - - - name: Run PHPBench - if: steps.check-gh-pages.outputs.exists == 'true' - run: vendor/bin/phpbench run --progress=plain --report=github-action-benchmark --output=json > output.json - - - name: Compare against baseline - if: steps.check-gh-pages.outputs.exists == 'true' - uses: benchmark-action/github-action-benchmark@v1 - with: - name: Phpactor Benchmarks - tool: customSmallerIsBetter - output-file-path: output.json - gh-pages-branch: gh-pages - benchmark-data-dir-path: dev/bench - auto-push: false - github-token: ${{ secrets.GITHUB_TOKEN }} - comment-on-alert: true - alert-threshold: "200%" - fail-on-alert: false - diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml deleted file mode 100644 index bbfdcc096e..0000000000 --- a/.github/workflows/benchmark.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Benchmark - -on: - push: - branches: - - master - -jobs: - benchmark: - name: Benchmark - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - coverage: none - php-version: "8.2" - tools: composer:v2 - - - name: Composer install - uses: ramsey/composer-install@v2 - with: - composer-options: "--no-scripts" - - - name: Ensure gh-pages branch exists - run: | - if ! git ls-remote --exit-code --heads origin gh-pages > /dev/null 2>&1; then - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - empty_tree="$(git hash-object -t tree /dev/null)" - commit="$(git commit-tree "$empty_tree" -m 'Initial gh-pages branch')" - git push origin "$commit:refs/heads/gh-pages" - fi - - - name: Run PHPBench - run: vendor/bin/phpbench run --progress=plain --report=github-action-benchmark --output=json > output.json - - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 - with: - name: Phpactor Benchmarks - tool: customSmallerIsBetter - output-file-path: output.json - gh-pages-branch: gh-pages - benchmark-data-dir-path: dev/bench - auto-push: true - github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 413491effd..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,316 +0,0 @@ -name: "CI" - -on: - pull_request: - push: - branches: - - 'master' - -env: - fail-fast: true - TZ: "Europe/Paris" - -jobs: - phar: - name: Compile Phar - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set PHP 8.2 - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts --no-dev" - - - name: Tag with a dummy name - run: git tag test - - - name: Compile phpactor.phar - run: .github/build-phar.sh - - - name: Check existence of compiled .phar - run: test -e build/phpactor.phar && exit 0 || exit 10 - - name: Execute Phar - run: ./build/phpactor.phar - - - name: Archive phar - uses: actions/upload-artifact@v4 - with: - name: phar - path: build/phpactor.phar - - #- name: "Attach signature to Release" - # uses: actions/upload-release-asset@v1 - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # with: - # upload_url: ${{ github.event.release.upload_url }} - # asset_path: ./build/phpactor.phar.asc - # asset_name: phpactor.phar.asc - # asset_content_type: application/pgp-signature - - vim-tests: - name: "VIM Tests (${{ matrix.php-version }})" - - runs-on: "ubuntu-latest" - - strategy: - matrix: - php-version: - - '8.2' - - steps: - - - name: "Checkout code" - uses: "actions/checkout@v4" - - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" - php-version: "${{ matrix.php-version }}" - tools: composer:v2 - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts" - - - name: Install Dependencies - run: | - composer validate --strict - composer install --optimize-autoloader --classmap-authoritative - bin/phpactor --version - export PHPACTOR_UNCONDITIONAL_TRUST=1 - git clone https://github.com/junegunn/vader.vim.git - - - - name: "VIM tests fail with a TTL of 1.0 for some reason" - run: "./bin/phpactor config:set worse_reflection.cache_lifetime 5.0" - - - name: "Run VIM Tests" - run: ".github/vim-plugin-test.sh" - phpunit: - name: "PHPUnit (${{ matrix.php-version }})" - - runs-on: "ubuntu-latest" - - strategy: - matrix: - php-version: - - '8.2' - - '8.3' - - '8.4' - - '8.5' - - steps: - - - name: "Checkout code" - uses: "actions/checkout@v4" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" - php-version: "${{ matrix.php-version }}" - tools: composer:v2 - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts" - - - name: "Run PHPUnit" - run: | - php -dphar.readonly=0 \ - -dzend.assertions=1 \ - vendor/bin/phpunit --log-junit phpunit-junit.xml - mago: - name: "Mago integration (${{ matrix.php-version }})" - - runs-on: "ubuntu-latest" - - permissions: - contents: read - - strategy: - matrix: - php-version: - - '8.2' - - steps: - - - name: "Checkout code" - uses: "actions/checkout@v4" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" - php-version: "${{ matrix.php-version }}" - tools: composer:v2 - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts" - - - name: "Install Mago" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - composer require --dev --no-interaction "carthage-software/mago:^1.30" - vendor/bin/mago --version - - - name: "Run Mago integration tests" - run: 'PATH="$PWD/vendor/bin:$PATH" vendor/bin/phpunit --group language-server-mago' - phpstan: - name: "PHPStan (${{ matrix.php-version }})" - - runs-on: "ubuntu-latest" - - strategy: - matrix: - php-version: - - '8.2' - - steps: - - - name: "Checkout code" - uses: "actions/checkout@v4" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" - php-version: "${{ matrix.php-version }}" - tools: composer:v2 - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts" - - - name: "Run PHPStan" - run: "vendor/bin/phpstan analyse" - phpactor: - name: "Phpactor Self Lint" - - runs-on: "ubuntu-latest" - - strategy: - matrix: - php-version: - - '8.2' - - steps: - - - name: "Checkout code" - uses: "actions/checkout@v4" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" - php-version: "${{ matrix.php-version }}" - tools: composer:v2 - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts" - - - name: "Run Phpactor Analyse" - run: "bin/phpactor worse:analyse lib --ignore-failure" - php-cs-fixer: - name: "PHP-CS-Fixer (${{ matrix.php-version }})" - - runs-on: "ubuntu-latest" - - strategy: - matrix: - php-version: - - '8.2' - - steps: - - - name: "Checkout code" - uses: "actions/checkout@v4" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" - php-version: "${{ matrix.php-version }}" - tools: composer:v2 - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts" - - - name: "Run PHP-CS_Fixer" - run: "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --dry-run --diff" - phpbench: - name: "PHPBench smoke (${{ matrix.php-version }})" - - runs-on: "ubuntu-latest" - - strategy: - matrix: - php-version: - - '8.2' - - steps: - - - name: "Checkout code" - uses: "actions/checkout@v4" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" - php-version: "${{ matrix.php-version }}" - tools: composer:v2 - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts" - ignore-cache: "yes" - - - name: "Run PHPBench" - run: "vendor/bin/phpbench run --progress=plain --iterations=1 --dump-file=phpbench.xml" - docs: - name: "Lint Docs (${{ matrix.php-version }})" - - runs-on: "ubuntu-latest" - steps: - - - name: "Checkout code" - uses: "actions/checkout@v4" - - - name: "Make Docs" - run: "make docs" diff --git a/.github/workflows/release-phar.yml b/.github/workflows/release-phar.yml deleted file mode 100644 index e2d51b061a..0000000000 --- a/.github/workflows/release-phar.yml +++ /dev/null @@ -1,54 +0,0 @@ -on: - release: - types: - - created - -name: Append phpactor.phar to release - -jobs: - build: - name: Compile and upload Phar - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set PHP 8.2 - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - - - - name: "Composer install" - uses: "ramsey/composer-install@v2" - with: - composer-options: "--no-scripts --no-dev" - - - name: Compile phpactor.phar - run: .github/build-phar.sh - - - name: Check existence of compiled .phar - run: test -e build/phpactor.phar && exit 0 || exit 10 - - - name: "Upload PHAR to Release" - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ./build/phpactor.phar - asset_name: phpactor.phar - asset_content_type: application/octet-stream - - #- name: "Attach signature to Release" - # uses: actions/upload-release-asset@v1 - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # with: - # upload_url: ${{ github.event.release.upload_url }} - # asset_path: ./build/phpactor.phar.asc - # asset_name: phpactor.phar.asc - # asset_content_type: application/pgp-signature - diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d8493eee1a..0000000000 --- a/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -/extensions -/phpactor.schema.json -/lib/Extension/WorseReflection/stubs -/.phpbench - -# Composer -/vendor - -# Cache -/tests/Assets/Cache -/cache -/lib/Completion/cache -/lib/WorseReflection/Tests/Cache - -# Workspaces -/tests/Assets/Workspace -/**/Tests/Workspace - -# File frequently used for testing -/lib/Test.php -/.phpactor.yml -/.phpactor/* - -# Logging -/application.log - -/.php-cs-fixer.cache -/build -__pycache__ -/doc/_ext/__pycache__ -.phpunit.result.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php deleted file mode 100644 index 6f042d1e0e..0000000000 --- a/.php-cs-fixer.dist.php +++ /dev/null @@ -1,57 +0,0 @@ -in('lib') - ->in('tests') - ->exclude([ - 'Workspace', - 'Assets/Cache', - 'Assets/Projects', - 'Assets/Workspace', - 'InternalStubs', - ]) -; - -return (new Config()) - ->setRiskyAllowed(true) - ->setRules([ - '@PSR2' => true, - 'no_unused_imports' => true, - 'phpdoc_to_property_type' => true, - 'no_superfluous_phpdoc_tags' => [ - 'remove_inheritdoc' => true, - 'allow_mixed' => true, - ], - 'class_attributes_separation' => [ - 'elements' => [ - 'const' => 'only_if_meta', - 'property' => 'one', - 'trait_import' => 'only_if_meta', - ], - ], - 'ordered_class_elements' => true, - 'no_empty_phpdoc' => true, - 'phpdoc_trim' => true, - 'array_syntax' => ['syntax' => 'short'], - 'list_syntax' => ['syntax' => 'short'], - 'void_return' => true, - 'ordered_class_elements' => true, - 'single_quote' => true, - 'heredoc_indentation' => true, - 'global_namespace_import' => true, - 'no_trailing_whitespace' => true, - 'no_whitespace_in_blank_line' => true, - 'new_with_parentheses' => [ - 'anonymous_class' => true, - 'named_class' => true, - ], - 'multiline_promoted_properties' => [ - 'minimum_number_of_parameters' => 2 - ], - ]) - ->setFinder($finder) -; - diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index cd1ade57af..0000000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Read the Docs configuration file for Sphinx projects -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Set the OS, Python version and other tools you might need -build: - os: ubuntu-22.04 - tools: - python: "3.12" - # You can also specify other tool versions: - # nodejs: "20" - # rust: "1.70" - # golang: "1.20" - -# Build documentation in the "docs/" directory with Sphinx -sphinx: - configuration: doc/conf.py - # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs - # builder: "dirhtml" - # Fail on all warnings to avoid broken references - # fail_on_warning: true - -# Optionally build your docs in additional formats such as PDF and ePub -# formats: -# - pdf -# - epub - -# Optional but recommended, declare the Python requirements required -# to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -python: - install: - - requirements: requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 20c2104da2..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,1483 +0,0 @@ -Changelog -========= - -## 2026.06.23.0 - -Improvements: - -- Show the Phpactor version in the LSP `phpactor/status` response @dantleech -- `worse:analyse` writes its progress to STDERR, and its `--format=json` output - now reports the line, column, diagnostic code and a readable severity #3061 - @ajenbo - -Bug fixes: - -- Fix false positives in analyser ]#3063 @ajenbo -- Fix functions declared in the source code passed to the reflector being - reported as not found (e.g. every function declared and called in the same - file when running `worse:analyse`) #3061 @ajenbo. - - -## 2026.06.22.0 - -Features: - -- Code action / transformer to add missing `#[\Override]` attributes #2966 @ajenbo - -Improvements: - -- Respect cancellation for outsourced diagnostic processes #3058 @dantleech - -Bug fixes: - -- Handle premature cancellation of code-action that caused regression in VS - code @dantleech @zobo #3058 -- Require the posix extension and fail early with a descriptive error when a - required extension is missing (instead of crashing mid-session) @fain182 #3053 - -## 2026.06.25.0 - -- Mago diagnostics and lint extension @marjovanlier #3052 - -## 2026.05.30.2 - -- Do not show client warning when code action resolution process is killed - @dantleech #3051 - -## 2026.05.30.1 - -- Include missing prod depenency in composer `require` (deep-copy) @dantleech - - -## 2026.05.30.0 - -**NOTE** this relase drops support for PHP 8.1. The minium supported PHP version -is now 8.2. - -Features: - - - Index optimizer #3037 @dantleech - -Improvements: - - - Code-action resolution in separate process (avoid code action stacking and - blocking) #3048 @dantleech - - Include new line at end of generated `phpactor.json` #3047 @cweiske - - Resolve stubs in way consistent with other configurable paths #3040 - @dantleech - - Goto definition on first-class callable #3025 @przepompownia - -Bug fixes: - - - Explicitly specify byte order #3033 @dantleech / @zobo - - Fix null coalesce behavior on undefinfed variable @przepompownia - - Fix function call with unpacked array #3026 @yohanson - - Create trust directory recursively #3021 @kneemund - -Meta: - - - Bump to PHP 8.2 @dantleech - - Clean up since dropping 8.1 #3038 @przepompownia - - Introduce benchmark tracking / monitoring #3028 @ajenbo - - Ignore more unnecessary files with .gitattributes ~ - -## 2025.12.21.0 - -Features: - - - Additive stubs #2968 @dantleech - - Support for 1st-class callables @dantleech - - Support for the PHP 8.5 pipe operator @dantleech - -Improvements: - - - Restrict attribute completion using targets #2629 @przepompownia - - Use the Phan fork of the Tolerant Parser as a base #2946 @dantleech - - Show find references progress with LSP progress #2947 @dantleech - - Cache and highlighter refactoring #2883, #2992, #2991 @dantleech - -Bug fixes: - - - Do not enter infinite loop on self-referencing constants #2984 @mamazu - - Do not index files that exceed a configured size #2979 @mamazu - - Fix scoped property access variable renaming #2968 @dantleech - - Fix display of configuration warnings #2971 @dantleech - -## 2025.10.17.0 - -BREAKING - - - VIM plugin: Local `.phpactor.json` configuration files are no longer loaded by - default. If you use the VIM plugin you **must** explicitly trust the - configuration file with `:PhpactorTrust`. - -Features: - - - Support PHPStan editormode #2936 @mamazu - - (development) optional opentelemertry extension. - -Improvements: - - - Indexer: prioritize static include/exclude over dynamic paths #2927 @zonuexe - - Improved inlay type hints #2825 @dantleech - - Improve static analysis performance in some cases #2929 @dantleech - - Support for asymmetrical visiblity in parser #2926 @dantleech - - Ignore rector-stubs by default (frequently causes PHPUnit testcase - reflection issues) #2944 @dantleech - -Security: - - - Ask permission before loading project-level `.phpactor.json` @dantleech - -Bug fixes: - - - rename: Do not throw error if there is a reference to a now-non-existing file. @dantleech - - avoid infinite loop when looking up constant type #2913 @dantleech - -## 2025-07-25.0 - -Improvements: - - - Extract document highlighter to own module and add config option to - disable it @dantleech - - Fix false diagnostic for missing `__destruct` return type @przepompownia #2900 - - Add depth info to `worse:dump-ast` command @mamazo #2897 - - Ensure that stub locator results are cached in-memory @dantleech #2911 - - Upload PHAR as artifact on builds @drzraf #2915 - -Features: - - - Search filtering (as applicable to autocomplete, name importing etc) - @dantleech - -Bug fixes: - - - Require `ext-tokenizer` (fixes nixos distribution) @drupol - - Don't complete HEREDOC identifier @przepompownia #2909 - - Fix parameter type resolution priority @dantleech - - Fix completion rendering when snippets are disabled @mamazu #2898 - -## 2025.04.17.0 - -Improvements: - - - Do not suggest code action for missing return type if type is accurately - provided by docblock @dantleech - - Do not generate `void` return type on PHP 7.0 - -Bug fixes: - - - Support loading code templates when Phpactor included as a dependency - @zobo - -## 2025.03.28.0 - -Improvements: - - - Reference finding: Ask for confirmation to continue after soft timeout @dantleech #2856 - - PHAR fixes for Windows @zobo - - LSP - Support for inline values @zobo - - Code action prioritization @mamazu - -## 2025.02.21.0 - -Features: - - - String <=> Heredoc code action #2825 @mamazu - - Support new expression without parenthesis #2811 - - Support vscode evaluatable expressions #2905 @zobo - - Runtime support for PHP 8.4 #2829 - - Initial support for property hooks @dantleech #2833 - -Improvements: - - - Performance: Do not run Indexed reference finder if references handled by - Variable reference finder @dantleech - - Performance: Do needlessly re-index documents before searching for - references @dantleech - - Psalm: add `config` option to specify Psalm config @GDXbsv #2835 - - Completion for `@internal` tag #2827 @mamazu - - Add documentation for Nova Language Client #2830 @EmranMR - - Enable fill constructor code action on attributes #2810 @mamazu - - Require `ext-mbstring` extension to avoid off-by-one issues #2838 @dantleech - -Bug fix: - - - Handle zero modulo evaluation @dantleech - - Do not use FQNs for imported classes in generated docblocks #2843 @dantleech - -Documentation: - - - Add information for Zed editor @sethstha #2836 - -## 2024-11-28.1 - -Bug fixes: - -- Do not include the file scheme in when including/excluding files #2794 - -## 2024-11-28 - -Features: - - - Show codes for all diagnostics and allow them to be ignored @dantleech - #2781 - -Improvements: - - - Do not highlight entire class for fix class/namespace name diagnostic - #2728 @dantleech - - Tolerate code action provider failures #2761 @dantleech - - Limit number of methods that are documented on classes to improve - completion/resolve performance for large classes #2768 @dantleech - -Bug fixes: - - - Navigator: Fix attempt to create existing directories #2776 @bart-jaskulsi - - Fix goto constant within a trait #2784 @dantleech - - Preserve PHAR scheme when indexing PHAR stubs @dantleech #2754 - - Fix duplicated types when updating methods @mamazu #2779 - -## 2024-11-05 - -Bug fixes: - - - Docblock: support parsing quoted string literals as array valuyes #2730 - - Tell WorseReflection about new definitions from the stdin for the - diagnostics process #2723 - - Flush index on save (make latest changes available to diagnostic process) #2722 - - Fix bad contextual filtering #2715 @dantleech - - Take optional parameters into account with conditional types #2700 @dantleech - - Fix import position when `declare` is present #2698 @dantleech - - Fix NULL error in Docblock parser #2693 @dantleech - - Handle "source not found" when resolving template map #2716 @dantleech - -Improvements: - - - Allow exclude patterns to be set for diagnostics (e.g. `vendor/**/*`) #2705 @dantleech - - Improve formatting for override method #2702 @dantleech - - Offer completions on attributes not associated with class member body - #2695 @przepompownia - - Show prose associated with `@throws` tag #2694 @mamazu - - Support parsing generic variance e.g. `covariant` #2664 @dantleech - - Support opt-out of using temporary files with phpstan #2764 @tsterker - -Features: - - - Add support for 8.3 typed class constants - - Basic support for `@{phpstan,psalm}-assert` #2720 @dantleech - -## 2024-06-30 - -Features: - - - PHAR Indexing #2412 #2611 @dantleech - - Override method refactor #2686 @dantleech - -Improvements: - - - Do not use indexer when renaming private properties/methods #2672 @dantleech - - Fix contextual completion in constructor agrument position #2504 - - Basic support for `array_reduce` stub #2576 - - Support variadics in contextual completion #2603 - - Allow use of `%project_root%` in index paths #2665 @mamazon - - Fix another `PHP_BINARY` avoid writing to `dev/null` and other windows fixes @MatmaRex - - Use `get_debug_type` @zonuexe - - Show strikethrough for deprecated diagnostics #2623 @mamazu - - Adding more type coverage #2606 #2614 @mamazu - -Bug fixes: - - - Fix renaming attributed class members @przepompownia - - Do not error when PHPStan returns no output @mamazu - - Only filter new object expression names in contextual completion #2603 - - Fixing include and exclude patterns #2593 @mamazu - - Fix missing @implements code action #2668 @dantleech - - Initialized properties don't appear in LSP document symbols #2678 @mamazu - -## 2024-03-09 - -Features: - - - Completion suggestions filtered by accepting type #2456 - - Basic support for local type aliases #2462 - -Improvements: - - - Show enums in LSP document symbol provider #2575 @gmli - - PHPStan show tip if as a dignostic hint if available #2512 - - Docblock completion, suggest `@throws` @przepompownia - - Suggest named parameters on attributes @mamazu - - Remove redundant documentation #2500 @einenlum - - Resolve inherited generic types #2474 - - Allow additional CLI arguments to be passed to php code sniffer #2465 - - Clear document diagnostic cache on save #2458 - - Skip parent parameters on complete constructor #2471 @mamazu - - Support generics on `@mixin` #2463 - - Remove "on develop warning" service #2533 - - Disable the processing of includes/requires, it doesn't work very well but - it has massive performance impact on certain projects #2580 - - Include project PHP and runtime version and LSP status - - Add `iterable` "generic" `@param` in docblock #2585 - - Improved diagnostic engine #2584 - - Ongoing windows compatiblity effort #2567 #2572 #2570 @MatmaRex - - Ignore unnecessary files in gitexport #2570 @zonuexe - - Improve ANSI test compatiblity #2521 @gerardroche - - More snippet support #2515 #2508 @przepompownia - - Add completion for `@throws` #2509 @przepompownia - -Bug fixes: - - - Fix completion of constants in enums #2541 @eviljeks - - Fix `renderException` call in bin/phpactor #2548 @MatmaRex - - Psalm: fix exception handling #2587 @przepompownia - - Do not generalize generated return types (i.e. false instead of bool) #2588 - - Fix diagnostic process concurrency and do not lint outdated files #2538 - - Upgrade `amp/process` to fix #2516 thanks to @gerardroche - - Fix division by zero edge case - - Fix crash if referenced file no longer exists on class rename #2518 - - Fix detection of import used relatively in an annotation #2539 - - Fix PHAR crashing issue on PHP8.3 #2533 - - Fix UTF-16 conversion for LSP #2530 #2557 - - Fix support for Attributes on readonly classes #2493 - - Fix `$this` undefined var false positive in anon. class #2469 @mamazu - - Fix `$argv` undefined var false positives #2468 @mamazu - -Documentation: - - - Added Helix LSP instructions #2581 @lens0021 - - Fix typos in Behat #2534 @vuon9 - - Fix broken external links #2500 @einenlum - -## 2023-12-03 - -Bug fixes: - - - Support LSP document symbols in traits #2446 @lizhening - - Fix null variable name crash #2443 - - Fix frame merging of include/require #2391 - - Fix enum representation in method generation #2395 - - Fix enum cases() not existing false-positive #2423 - - Fix incorrect enum import #2400 - - Fix undefined var false positive for arra unpacking #2403 - - Fix autoloading class conflcits with test files #2535 @gerardroche - - Fix enum renaming in legacy renamer #2445 - - Fix enum renaming on "new" renamer #2445 - - Fix crash on resolveItem() caused by race condition (?) #2434 - - Fix false positive for undefined var where vardoc not counting as variable definition #2437 - - Render variadics as variadics in help, not as arrays #2448 - - Fix representation of int-range min/max #2444 - - Render default value for enum when filling object #2441 - -Features: - - - Generate enum cases and class constants #2422 - - Generate enum match arms #2401 - -Improvements: - - - PHPStan: Support setting custom config path and memory limit @ungrim97 - - Exclude tests from archive #2433 - -Breaking changes: - - - Drop support for PHP 8.0. Minimum version is now 8.1 - -## 2023.09.24 - -Bug fixes: - - - Fix crash with `php-cs-fixer` when using strict types rule #2348 - - Fix `null` error (and improve type safety) in the docblock parser #2379 - - Fix undefined-var false positive for undeclared variables that have `@var` #2366 - - Fix undefined-var false positive for pass by ref (again) #2361 - - Do not crash lanugage server if LSP header cannot be parsed (log error - instead) #2373 - -Improvements: - - - Correctly implementing LSP ranges #2352 @mamazu - - Add mechanism to automatically trigger an index update when breaking changes - are made - - Method generation on emums @mamazu - - -Improvements: - - - Support single line comments #2350 - - Do not promote parameters that are used in parent constructor #2119 @mamazu - - Improve detection of Xdebug @bart-jaskulsi #2347 - - Improve plain docblock parsing #2345 - - Generate `@param` tag for iterables #2343 @mamazu - -## 2023.08.06-1 - -Bug fixes: - - - Limit number of threads Psalm uses to 1 by default # - - Update file watching lib to handle "process already exited" errors - -## 2023.08.06 - -Improvements: - - - Improve Diagnostics: Run linters in parallel #2327 - - Index documents on save #2326 - -Bug fixes: - - - Fix generic extends with templated argument #2295 - - Do not report statically declared variables as undefined #2311 - - Do not trigger function completion for incomplete opening PHP tag - - Fix PHP linter #2318 - - Do not report undeclared variables that are passed by reference as undefined #2329 @mecha - -## 2023.06.17-1 - -Bug fixes: - - - Do not report globals or super globals as undefined #2302 - -## 2023.06.17 - -Features: - - - Diagnostics and code action for fixing missing `@implements` and `@extends` #2112 - - Diagnostic for undefined variables #2209 - - Code action to suggest fixes for undefined variables (in case of typos) #2209 - - PHPUnit: code action for adding `setUp` / `tearDown` #2180 @mamazu - - Making the completion label formatter configurable #2277 @mamazu - - Auto-reindex: unconditionally reindex modified files every N seconds - (default 5 minutes) - work around for missed file modification - notifications. - -Improvements: - - - Revised getting started documentation #2282 - - Support indexing PHP files that don't have a `.php` extension #2296 - - Allow language server auto-configuration to be disabled #2159 - (`language_server_configuration.auto_config`) - - Symfony: show and consider non-public services by default (e.g. in tests it's - possible to retrieve non-public services) #2263 - - Support traits in enums #2256 - -Bug fixes: - - - Fix enum case completion #2284 - - Fix error handling for responses from language client #2283 - - Do not show named parameters after string literal argument #2259 - - Fix "instanceof" behavior for statically reflected classes #2273 - - Fix behavior when user cancels type selection on goto type #2270 - - Fix docblock parsing of `array<'quoted'|'strings'>` #2264 - - Fix constant declaration indexing with `define` #2249 @mamazu - - Fix use of class-string variable as static scope resolution qualifier #2238 - - URL decode root URI - fixes issues with special chars in path #2228 - - Do not deduplicate suggestions of different types (e.g. prop/method with same name) #2214 - - Fix list assignment #2226 - - Support parsing interface clause on enums #2220 - - Do not make fully qualified name usage relative in class-mover #2208 @mamazu - - Fix resolution of `self` type (esp. in relation to traits) #2116 - - Fix different virtual member types with the same name replacing eachother #2108 - - Specify maximum size (255 chars) for string literal types #2144 - - Fix docblock parser with `$this` when used as generic argument #2092 - -## 2023.04.10 - -Features: - - - Show references to new objects when finding references to `__construct` method #2194 - - Support for inlay hints #2138 - - Deprecation diagnostics #2120 - - Auto configuration - automatically suggest and apply configuration #2114 - - Transform to "promote" unassigned consturctor properties #2106 - - Hierarchical namespace segment completion #2070 - - Completion for promoted property visiblity #2087 - - Option `language_server.diagnostic_outsource` to outsource diagnostics in separate process #2105 - -Bug fixes: - - - Also use in-memory files when enanching indexed records #2187 - - Prophecy: Do not crash when used in trait #2129 - - Prophecy: fixing chaining of methods via. `getObjectProphecy` #2122 - - `new class-string` now resolves to `new Foo` #2065 - - Fix extract method within trait #2076 @mamazu - - Do not attempt to index classes whose names are reserved words #2098 - - Fix typo in LanguageServerExtension::PARAM_FILE_EVENTS resulting in typo in documentation - - Fix parsing array types in `@param` Tags in doc blocks #2172 - -Improvements: - - - Only show completion suggestions for real attributes #2183, #2100 @mamazu @przepompownia - - Code action and formatting handlers now send progress notifications #2192 - - Invalidate diagnostics cache only when document changes #2191 - - Optimize analysis for scopes with many many assignments #2188 - - Made some heavy blocking operations non-blocking (e.g. diagnostics, code - actions). - - ⚠ Removed frame sorting which increases radically in some cases, but may - also cause regressions #2179 - - Psalm: Support for overriding the error level #2174 - - Generating constructor at the top of the file #2113 @mamazu - - Include (complex) docblock params when generating method - - Take into account named parameters when "guessing" parameter names #2090 - - Show full FQN for classes in hover #2081 - - Upgrade to 3.17 of the language server protocol #2082 - - Facilitate changing visiblity on promoted properties @mamazu - - Allow generation of constructor for Attributes. - -## 2023.01.21 - -Bug fixes: - - - Allow class completion within constant declaration in class #1985 @przepompownia - - Do not suggest return type on `__destruct` #1992 - - Do not report Prophecy methods as "not found" #2006 - - Do not add NULL to type list (fixes search bug) #2009 - - Create a real package for the tolerant-parser fork and use it #2033 - - Also highlight use statements when hovering on class #2039 @mamazu - - Fix priotity of "internal" stub locator - facilitating enum completion #2040 - - Require posix extension #2042 @dacianb - - Fix evaluation of replacement assignments #1705 - - Fix crash on missing token in Indexer #2049 @vlada-dudr - - Fix missing compact use name false positive #2052 - - Fix `class-template` when not in 1st arg position #2054 - -Features: - - - `@param` docblock generation - - Reintroduce the PHPUnit extension - - Support integer range type e.g. `int<0, max>` #2024 - -Improvements: - - - Support the Psalm cache #2046 @gbprod - - Support completion inside match expression #2051 @przepompownia - - Fixed typos in documentation #2050 @d-danilov - - Psalm Extension: allow `info` diagnostics to be hidden #2032 @gbprod - - Better docblock parsing and formatting #2004 - - More liberal support for vendor tags #2011 @ging-dev - - Fix nested template type arguments #2016 - - Fix importing of nested types #2009 - - Reverts #1974 - which made the situation worse rather than better. - - Change default WR cache TTL from 5 seconds to 1 second to avoid race with - diagnostics timeout. - - Add return tags to existing docblocks #1995 - - Naive support for attribute completion #2001 @przepompownia - - Support union type from class-string variadic generic - -## 2022.12.12 - -Breaking changes: - - - Minimum version of PHP changed to 8.0. **Phpactor will no longer run on PHP 7.4**. - -Features: - - - [lsp] Generate mutator @florian-merle - -Improvements: - - - [wr] Fix inference of array subscript expressions #1961 - -Bug fixes: - - - [lsp] Prevent race condition that makes old changes get analyzed after new changes #1974 - - [cmp] Constant visibility not taken into account for completion #1979 @przepompownia - - [rn] Fix crash on rename interface #1982 @nataneb32 - - [wr] Fix crash on enum with custom methods #1966 - - [ls] Log errors even if they are for a request @lumnn - - [ls] Do not include `results` key in JSON response when error @lumnn - - [lsp] Do not send workDoneProgress notifications to clients that do not - support it #1951 - - [lsp] Fix highlighting on PHP 8.1 #1960 - - [wr] Do not crash when encountering an array union operator #1971 @wouterj - - [wr] Fixing handling of HEREDOC in StringResolver #1977 @mamazu - -## 2022.11.12 - -Features: - - - [ct] Replace qualfier with import LSP refactoring #1939 @mamazu - - [sf] New Symfony extension #1915 - - [wr] Generic constructor parameters support #1920 - -Bug fixes: - - - [wr] Fix member template params when declared in interface #1914 - - [cb] Do not prompt to generate constructor when object is given no arguments #1911 - -Improvements: - - - [cb] Add properties _after_ constants #1917 @mamazu - - [--] Remove dependency on webmozart/path-util @mamazu - - [wr] "invokable" type refactoring - - [--] Do not register services for disabled extensions - -Documentation: - - - Added Emacs LSP client guides @zonuexe - -## 2022.10.11 - -Bug fixes: - - - [lsp] Import all unresolved names command no longer dupliates names #1835 - - [tp] Update tolerant parser library fixing issue with parsing `match` keyword #1873 - - [rpc] Fix regression with :PhpactorClassNew opening in `Untitled` buffer #1881 - - [ctf] Fix token issue with simple class-to-file converter #920 - - [wr] Built-in enum members are reflected #1902 - - [wr] Fix iterable generic not being resolved properly #1875 - -Improvements: - - - [wr] Better modelling of enums - - [wr] Add additional phpactor-specific stubs (e.g. for Enums) - - [lsp] Enum hover improvement - - [lsp] Improve formating signature help parameters #1894 - - [lsp] Highlighting more 10x faster #1891 - - [cmp/lsp] inline type information for completion items - - [cmp] complete `__construct` on `parent::` #1272 - - [wr] Refactored generic handling - -Features: - - - [wr] Support for `class-string` generic - - [ct] Decorate interface #1879 @mamazu - - [lsp] Document formatting via. php-cs-fixer #1897 - - [gtd] For member declarations, goto parent member definition if it exists #1886 - -## 2022.09.11 - -Bug fixes: - - - [wr] Inconsistent type resolution - removed node level cache #1673 - - [in] Fix exception when indexed file has no path #1643 - - [wr] Do not complete constants on class instance #1614 - - [wr] Include virtual properties in class members #1623 - - [wr] Fix false positive for virutal method not existing #1603 - - [wr] Ignore exceptions (permission denied f.e.) when traversing files #1569 - - [wr] Fix resolutin of virtual method - - [ct] Fix missing properties refactor does not import class #1534 - - [ct] Fix false diagnostic for missing method #1500 - - [dl] Fix docblock definition location at class level docblocks - - [idx] Do not try and use non-tokens for property names #1317 - - [cb] Fix rendering of array values in generated code - - [wr] Fix arrow function completion #1303 - - [dl] Fixed off-by-one error with plain text goto definition - - [fw] Ensure inotify is stopped before shutting down - - [wr] properly deconstruct array in foreach - - [lsp] import unresolved classes refactoring: Ensure only unique names are shown when asking user to select an import candidate - - [lsp] ensure fully qualified filename is used for generate method refactoring #1313 - - [wr] detect branch determination with throw expression - - [cr] add missing properties: correctly infer type from call expressions - - [filesystem] Fix "too many files open" issue #1376 - - [class-mover] Fix long standing bug with aliased imports being duplicated - on class move and other strange issues. - - [lsp] Fix call to properties() on non-class in generate accessors provider. - - [lsp] Fix unresolvable classes not being listed in code actions - - [cb] Do not apply HTML escaping when rendering code templates - - [wr] Promoted property docblock types not picked up #1334 - - [completion] Limit results from the search index (improve search performance significantly) - -Improvements: - - - [cmp] Show partial namespace to disambiguate class name suggestions - - [wr] Markdown formatted member completion documentation - - [ls] publish diagnostics on open and update - - [ct] Add missing properties for array assignments #1640 - - [cmp] Provide variables from parent frame for anonymous use #1602 - - [ref] Increase reference finder timeout to 1 minute by default #1579 - - [cmp] Improved contextual completion - - [rn] Fixed numerous issues - - [rename] Fixed numerous issues - - [compl] Allow named param completion on functions - - [wr] Infer return type for generators - - [lsp] Show Phpactor version info in initialize result @lalanikarim - - [ls] Fixed class completion performance - - [ct] Add option to disable importing global functions - - [cmp] (Better) support for completing imported names #1490 - - [wr] reset,array_shift and array_pop stubs - - [wr] improved ternary support - - [log] Include a channel prefxi in log messages - - [wr] require `ext-pnctl` (language server would crash otherwise) - - [wr] handle static properly #967 - - [ls] include list of diagnostic providers in status report - - [ct] add retutn type to generated method if it would immediately return - - [wr] support array [] addition operator - - [wr] support in_array type assertion - - [wr] support for casts - - [wr] if statement branches - - [wr] inline type inference - - [wr] infer param types from _function_ docblock - - [wr] support for Closure as a type #1413 - - [wr] expressions are evaluated as types - - [wr] literal types and internal refactorings - - [ls] Show error message in client if service stops unexpectedly with an - error - - [code-transform] Faithfully reproduce documented types in generated code - - [docblock] New docblock parser to facilitate parsing complex types - - [hover] Improve "offset" hover (mostly related to showing variable info) - - [templates] Include templates for creating new interfaces, traits and enums - - [wr] Resolve type from array access - - [cb] Preserve `?` operator as distinct from a union type - - [wr] Support for class-string type (not for type inference however) - - [completion + location] Better support for union types - - [cs] Updated CS and converted property docblock types to actual types - -Features: - - - [-] Better constant suppoer - indexing, goto def, find references, hover, etc. - - [cmp] Support absolute name completions - - [ls] Lazily resolve documentation for completion items - - [ct] Generate constructor refactoring - - [ct] Fill object refactoring - - [ct] Remove unused imports diagnositcs and code transformation #1758 - - [wr] Added native WR single-pass diagnostics #1700 - - [cmd] Index clean command #1691 @mamazu - - [cmp] (re?)support completion on parent:: #1643 - - [cb] Render types based on PHP version #1655 - - [wr] Support `@property-read` - - [wr] Support for mixins #990 - - [rf] Support for constants, properties and promoted properties - - [compl] Docblock completion - - [wr] Support for intersection types - - [rf] Union type support for goto definition - - [ct] Add missing PHP return types - - [wr] Support for inference for `array_map`, and arrow and anonymous functions - - [ct] Add missing @return type docblocks code transformation - - [cmp] Explicitly enable/disable completors and disable `keyword` completor by default. - - [wr] Support `iterator_to_array` - - [wr] Handle constant glob to union types (`@return Foo::BAR_*`). - - [lsp] show class category in offset hover info - - [lsp] jump to types in a union type - - [wr] Type combination - - [wr] Support for type assertions via. is_*, instanceof etc - - [wr] Array shape type support (types and completion) - - [wr] Support for variadics - - [lsp] Send rename file request to client when renaming a PSR class - @przepompownia - - [wr] Initial support for generics #1382 - - [lsp] Added generate accessors code action - - [lsp] Added extract constant code action - - [extension] Removed the extension manager. - - [extension] PHPStan and Psalm extensions are now included by default. - - [lsp] Code action to complete constructor with _public_ properties - - [php] Bump min. PHP version to 7.4 - - [php] Fix PHP 8.1 deprecations - - [config] JSON schema support - - [cli] `phpactor config:init` command to create or update config (to - include JSON schema location) - - [completion] Enum support (requires 8.1 PHP runtime) - - [reference-finder] Enum support (requires 8.1 PHP runtime) - - [php8.1] Disable deprecation warnings unless `PHPACTOR_DEPRECATIONS` - provided. - -## 2022-01-03 (0.18.0) - -Features: - - - [language-server] Import all names refactoring - @dantleech - - [language-server] Extract expression - @BladeMF - - [language-server] Extract method generation - @BladeMF - - [language-server] Initial support for method generation - @BladeMF - - [langauge-server] Support for renaming files (LSP 3.16) - @dantleech - - [language-server] Ability to use client file events where available - @dantleech - - [completion] Experimental support snippets for built-in functions - @weeman1337 - - [completion] Experimental support snippets for class constructos - @weeman1337 - - [completion] Added `experimental` flag - - [completion] Added flag to enable / disable snippets entirely - - [language-server] Ensure workspace is indexed before finding references - @dantleech - - [language-server] Support for renaming class names (short only) - @dantleech - - [language-server] Rename class members and variables - @BladeMF, @dantleech - - [language-server] Basic support for workspace symbols. - - [language-server] Added basic PHP linting by default. - -Improvements: - - - [completion] Improve diagnostic message for #1245 - @dantleech - - [language-server] Allow hover template paths to be customized - @BladeMF - - [language-server] Show warning in client if extra config keys present - @dantleech - - [code-transform] Improved performance for unresolvable class name finder - @dantleech - - [code-transform] Improved information in name-not-found exception - - @weeman1337 - - [language-server] Do not show "class not found" diagnostics by default - @dantleech - - [worse-reflection, etc] Update to latest tolerant parser lib to support PHP 8.1 syntax - -Bug fixes: - - - [worse-reflection] Fix handling of non-decimal integers - @Slamdunk - - [worse-reflection] Fix variable detection in closures - @BladeMF - - [completion] Fix snippet method completion #1172 - @BladeMF - - [worse-reflection] Fix PHP8.0 deprecation warnings - @gregoire - - [completion] Tests fail due to jetbrain stubs changes - @weeman1337 - - [worse-reference-finder] Do not know how to create class from type "NULL" #1246 - @dantleech - - [worse-reflection] Property context class not propagated - -## 2021-03-21 (0.17.1) - -Features: - - - [completion] Support Attribute Completion - -Bug fixes: - - - [language-server] Diagnostics do not tolerate NULL document version on save #1220 - - [worse-reflection] Unhandled exception thrown when variable name is "NULL" - - [language-server] Unhandled exception when function not found on hover - -## 2021-02-06 (0.17.0) - -Features: - - - [completion] Support for PHP named parameters - @dantleech - - [completion] Basic Doctrine annotation completion support - @elythyr - - [completion] References are sorted alphabetically - @elythyr - - [completion] Show warning character if method or class is deprecated - - [completion] Sort class names and fucntions according to proximity to current file by default - @dantleech - - - [composer] Class map only mode by default (do not register autoloader at all, do not include files) - - [file-watcher] Experimental support for [watchman](https://facebook.github.io/watchman/) - - [indexer] CLI command for index search (mainly for debugging) - - [indexer] PhpStorm stubs are now indexed - - [indexer] Show memory usage and limit in progress notification. - - [language-server] Import class/function code action and diagnostics - - [language-server] Transform code actions and diagnostics (complete constructor, implement contracts, fix class name and add missing properties) - - [completion] Keyword completion - @BladeMF - - [language-server] Create class code actions - @dantleech - - [phpactor] Update extensions after install composer hook - @dantleech - -Improvements: - - - [worse-reflection] Support for list foreach - - [worse-reflection] Various issues around NULL and exception handling - - [worse-reflection] Improved frame resolution performance by 99.5x - @dantleech - - [worse-reflection] Fixed mixed up start/end positions in symbol resolver - @BladeMF - - [language-server] Update classes on workspace update - @BladeMF - - [language-server] New LSP protocol and general refactoring - @dantleech - - [language-server] Support document symbols (f.e. showing code outline for document) - - [language-server] Support symbol highlighting - - [language-server] Support for indexing constants - - [code-tranform] Generated accessors automatically `ucfirst` the property name when prefix is used. - @einenlum - - [worse-reflection] Improved inference for property types - @elythyr - - [worse-reflection] Include virtual members from traits - @scisssssssors - - [code-tranform] fix invalid missing property diagnostic (#1126) - @elythyr - - [code-transform] Improve performance for missing properries - @dantleech - -Bug fixes: - - - [code-transform] Catch unhandled exceptions - @dantleech - - [text-document] valid php class names not detected for word-at-offset - - [code-tranform] Return types not considered for unresolved names - @dantleech - - [completion] Avoid reflection on NULL - - [scf] Fix support for moving and removing folders - @Lumnn - - [indexer] Fix indexing of static properties - @BladeMF - - [completion] Fix signature help in nested symbols - @BladeMF - - [worse-reflection] Static properties not resolved - @BladeMF - - [lanaguge-server] Correctly highlight use statements against qualified - names - @dantleech - - [language-server] Fix occasional class-not-found error on code transform (due to incorrectly formatted path) - - [worse-reflection] Do not consider "iterable" as an FQN - @elythyr - - [code-transform] Fix trailing line on class import - @elythyr - - [code-transform] Fix importing class names in docblocks - @elythyr - -## 2020-06-09 (0.16.1) - -Improvements: - - - [worse-reflection] Support for virtual methods in interfaces - @dantleech - - [code-transform] Fix regression with importing from root namespace - -## 2020-06-09 (0.16.0) - -Features: - - - [vim-plugin] Ability to set custom project root strategy (#1027) - @przepompownia - - [indexer-extension] Workspace reference finder (classes,functions,members) - @dantleech - - [worse-reflection] Support "final" keyword - @dantleech - - [language-server-hover] Show "final" keyword on class hover - @dantleech - - [language-server-hover] Show inherited method documentation - @dantleech - - [language-server-code-transform] Add command to import class - @dantleech - - [language-server-completion] Automatically import class on completion confirm - @dantleech - - [code-transform] Consider current class as a potential conflict for imports - @dantleech - - [completion] Indexed class name and function completion - @dantleech - - [indexer-extension] Support "deep references" (search over all implementaions) - @dantleech - - [composer] Enable disbaling of autoloader inclusion via. `composer.enable` - @dantleech - - [lanaguage-server-completion] Auto-import functions - @dantleech - -Improvements: - - - [code-builder] Removed functionality to "update" parameters: was very - buggy. Now only new parameters will be added when updating methods via. - generate method. - - [language-server-bridge] Service to convert Phpactor Locations to LSP locations - @dantleech - - [code-transform] Class import updates context name on alias - @dantleech - - [documentation] Generate the configuration reference - @dantleech - - [completion-worse] Allow completors to be disabled via `completion_worse.disabled_completors` - @dantleech - - [indexer-extension] Validate search results (remove from search index if invalid). - - [language-server] Exit session immediately if NULL given as CWD (instead of crashing). - - [container] Adds command for introspecting the container (`container:dump`) - @dantleech - - [indexer-extension] Increase priority of indexer source-locators (they should come before the composer locators) - @dantleech - - [language-server] Show explicit meassage when indexer dies - -Bug fixes; - - - [completion] Completion limit of 32 imposed in 0.15 removed. - - [ampfs-watch] Inotify watcher not reporting error when out of available - watchers - (https://github.com/phpactor/amp-fswatch/commit/1e38faadc3fb73158de9a966ee12d17992dad4fe) - - @dantleech - - [ampfs-watch] Buffered watcher not allowing errors to bubble up - (https://github.com/phpactor/amp-fswatch/commit/b5cb54b6d01a9ec3dcbfdcca804c2d63c0e84a19) - - @dantleech - - [language-server] Ensure that `result` key is missing when `NULL` (some - clients require it) - @dantleech - - [code-transform] Fixed occasional whitespace issues when importing classes - - [language-server] Support for LSP commands - - [indexer] Fixed crash with empty class name - -## 2020-05-03 0.15.0 - -Features: - - - [reference-finder] Goto type: goto the type of the symbol under the cursor #892 - @dantleech - - [worse-reflection] Enable cache lifetime (important for long running - processes) (#929) - @dantleech - - [language-server] Included in the core - @dantleech - - [indexer] Indexer included in the core - @dantleech - - [rpc] Add docblock prose to hover - - [vim-plugin] Add support `:checkhealth` and provide `:PhpactorStatus` in - terminal window (#974) - @elythyr - - [ref-finder] Goto definition works for vars (https://github.com/phpactor/worse-reference-finder/pull/1) - @FatBoyXPC - - [phpactor-ls] workspace/references support - @dantleech - -Improvements: - - - [text-document] Include `<` and `>` when getting "class" name undercursor - (allow implorting `Foobar` from an `@var array` doc - - [completion] Option to deduplicate suggetions (#905) - @dantleech - - [completion] Option to limit completion options - @dantleech - - [completion] Allow completors to return `true` when they finish (allow - final consumer to know if list is complete) - @elythyr - - [vim-plugin] Improved command registration (#965) - @elythyr - - [completion] Improved signature help (https://github.com/phpactor/completion/pull/31) - @elythyr - - [completion] Completors can return if they are complete (https://github.com/phpactor/completion/pull/30) - @elythyr - -Bug fixes: - - - [code-transform] Generate accessor doesn't work on selected property (regression) - - [vim-plugin] Configuration was not global (#964) - @elythyr - - [class-mover] `$` was removed when renaming static variables (#925) - - @dantleech - - [class-to-file] Remove duplicate candidates (fixes issue with class - completion duplicate suggestions) - -Documentation: - - - [doc] Fix examples in refactoring documentation - @Great-Antique - - [doc] Fix example mappings and add missing commands - @yeagassy - -## 2020-03-04 0.14.1 - -Bug fixes: - - - [vim-plugin] Fix `force_reload` behavior with `g:useOpenWindow` - -## 2020-03-01 0.14.0 - -Features: - - - [vim-plugin] Introduces Commands for user actions (instead of having to - call the functions) - - [vim-plugin] Generate the VIM help from the plugin's code documentation - - [code-builder] Support for nullable types - @elythyr / @dantleech - - [code-builder] Generates typed property for PHP 7.4 - @elythyr - - [worse-reflection] Support for PHP 7.4 property types - @dantleech / @elythyr - - [phpactor|code-builder] Allow to override the templates by PHP version - @elythyr - - [phpactor] Auto-detection of project PHP version - @dantleech - - [code-transform|rpc] Import missing classes - @dantleech - - [context-menu] Invoke menu for the nearest actionable node (i.e. you can - invoke the context menu on whitespace now) - @elythyr - - [vim-plugin] Extract functions handles motions @elythyr - - [vim-plugin] Jumping to another file preserves the jumplist @elythyr - - [class-mover] Jump to implementation - @dantleech - -Bug fix: - - - [code-transform] Cannot rename variable from anonymous function variable - (#829) - @dantleech - - [code-transform] Complete constructor does not take into account aliased - imports (#886) - @dantleech - - [code-builder] New aliased class imports alias not added (#860) - @dantleech - - [worse-reflection] instanceof returns negative if class implements - interface but extends another class - @dantleech - - [worse-reflection] foreach key variable resolves as symbol type "unknown" - - @dantleech - - [text-document] Word splitting includes commas, and other non-word chars - (#851) - @einenlum - - [worse-reflection] Functions wrongly memonized as classes - @dantleech - - [class-new-cli] response shows source code instead of path (#792) - - [class-new] Wrong file path when destination shares the same namespace as source (#795). - -Improvements: - - - [vim-plugin] Better handling of `json_decode` errors - - [vim-plugin] Add option to switch to open windows - `g:phpactorUseOpenWindows` - @przepompownia - - [vim-plugin] Stable context menu shortcuts - @dantleech (#896) - -## 2019-10-23 0.13.5 - -Bug fix: - - - [text-document] `?` included with word-at-offset #833 - -## 2019-09-13 0.13.4 - -Bug fixes: - - - [text-document] Word-at-offset offset off by one #816 - -## 2019-08-25 0.13.3 - -Bug fixes: - - - [context-menu] Import class from context menu not working #816 - -## 2019-08-25 0.13.0 - -Features: - - - [vim-plugin] Add new `GotoDefinition[Vsplit|Hsplit|Tab]` functions. - - [code-builder] Initial support for nullable types - @einenlum - - [vim-plugin] FZF integration for list inputs (#769) - @elythyr - - [vim-plugin] FZF multiple selection (#773). @elythyr - - [vim-plugin] Maintain correct cursor position after certain text diffs (#770) - @elythyr - - [code-transform|rpc] Generate multiple accessors for a class - @elythyr - - [code-tranform] Generate static methods if the call was static (#25) - @einenlum - - [completion] Use declared classes as completion source - - [import-class] Import declared classes (as long as they can be statically - resolved). - - [rpc] Class import uses offset to determine type to import - - [class-mover] Possiblity to move related any files whose relations are - defined in `navigator.destinations` (for both command and rpc) - - [worse-reflection] Support virtual class properties (in addition to - methods). - -Bug fixes: - - - [completion] Signature helper does not work on interfaces (#752) - @taluu - - [code-builder] Class import doesn't work with single element namespace - #760 - - [code-builder] Variant is not passed to class generator (#766) - - [phpactor|cli] response shows source code instead of path (#792) - - [class-mover|rpc] Fix order of open/close operations, prevent VIM crashing - -BC Break: - - - [rpc] Import class no longer requires name parameter. RPC version changed - to version 2. - - [code-transform] Generate accessors is now a class action and allows - generation of multiple accessors. - -## 2019-03-03 0.12.0 - -BC Break: - - - [completion] Comletion API changed to accept the new - [TextDocument](https://github.com/phpactor/text-document). - -Features: - - - [goto-definition] Goto definition extracted from core into separate - packages including [extension - point](https://github.com/phpactor/reference-finder-extension). - - [goto-definition] Support for "plain text" goto class definition, works - for docblocks, and non-PHP files. - - [completion] Do not suggest non-static method on static calls. - - [completion] Suggest ::class constant, fixes #673 - - [completion] Docblock type injection allow name to be omitted #618 - - [application] Log errors in command error handler (for logging async - completion errors using the complete command) - - [worse-reflection] Support variadic arguments #621 - - [worse-reflection] Support for virtual methods #682 - - [worse-reflection] Support for evaluating `clone()` - - [worse-reflection] Support for registering custom virtual class member - providers. - - [vim-plugin] Find references shows context line #706 - - [code-builder] Trait support, thanks @dshoreman - -Improvements: - - - [code-transform] Support extracting expressions to methods #666 - - [code-transform] Extract method adds return statement to calling code if - extracted code contained a return #704 - - [worse-reflection] Support union catch #711 - -Bug fixes: - - - [completion] Fix type resolution immediately following docblock #678 - - [completion] Include `$` on static properties #677 - - [extension-manager] Do not install dev dependencies for extensions #674 - - [class-to-file] sort candidates by path length #712 thanks @greg0ire - - [code-transform] Rename variable includes anonumous function use #713 - - [worse-reflection] Do not downcast union types in named docblocks #711 - - [code-transform] Extract method sometimes creates method in new class in - same file #730 - - [code-transform] Add Missing Properties added trait props in new class #726 - -## 2018-12-21 0.11.1 - - - [application] Resolve the vendor directory correctly when Phpactor - included as a dependency, thanks @kermorgant - -## 2018-12-02 0.11.0 - -BC Break: - - - [rpc] All handlers must now be registered with the "name" attribute. - -Features: - - - [worse-reflection-extension] Allows new framewalkers to be registered - (i.e. new ways to infer types). - - [config] Support loading config from JSON files - -Improvements: - - - [rpc] Handlers are lazy loaded, improving the RPC baseline latency - -## 2018-11-26 0.10.0 - -BC Break: - - - [php] Bumped minimum PHP version to 7.1 - - [config] Renamed `reflection.enable_cache` => `worse_reflection.enable_cache` - - [config] Renamed `reflection.stub_directory` => `worse_reflection.stub_directory` - - [config] Renamed `autoload` => `composer.autoloader_path` - - [config] Renamed `autoload.deregister` => `composer.autoload_deregister` - -Features: - - - [ExtensionManager] Facility to dynamically add extensions to Phpactor - - [RPC] `extension_list`, `extension_remove` and `extension_install` - handlers. - - [Completion] Class alias completor, #592 - - [CodeTransform] Cycle class member visiblity #521 - - [RPC] Adds `hover` handler which shows the synopsis of the symbol - underneath the cursor. - - [Completion] Introduction of a type-specific completion registry, to allow - registration of completors for different sources, e.g. cucumber. - -Improvements: - - - [Application] Do not eagerly load commands (~20% baseline improvement) - - [Transform] Complete constructor will work work on ! interfaces #597 - - [Transform] Import missing types on generate method - - [Transform] Adds return types on generate method - - [CodeBuilder] Do not add additional spaces when importing classes - - [Completion] Completion qualifiers to allow reusable way to determine - candidate completors. - - [Vim Plugin] The "label" for omni complete suggestions is now truncated to - a specified length. - -Other: - - - [Console] Config dump now only shows JSON format - - [Completion] Completors now `yield` suggestions and problems are no longer - returned. The `issues` key returned from suggestions is now deprecated. - - [Vim Plugin] The "omni error" feature has been removed (as completion no - longer returns them). - -## 2018-08-04 0.9.0 - -BC Breaks: - - - [RPC Plugins] a new `update_file_source` method is now returned by most - code-transforming RPC handlers (e.g. import class, complete constructors). - this is used in place of `replace_file_source`. - See [https://github.com/phpactor/phpactor/issues/550](#550) for details - -Deprecations: - - - [Completion|Completion Plugins] Serialized key `info` is deprecated in favour of - `short_description` and could be removed, at least, in 0.10.0. - -Features: - - - [RPC] `open_file` command now has a `force_reload` flag - - [Completion|Vim Plugin] Auto-import class names (thanks @kermorgant for - improvements) - - [Completion] Suggestion types now have more explicit types (e.g. `method`, - `constant`, `class`, rather than the VIM-centric kind characters). - - [WorseReflection] Fallback to inferring property types from constructor assignments. - - [RPC|Vim Plugin] RPC handler for file class info (e.g. namespace, class - FQN) and VIM functions new `phpactor#getNamespace()` and - `phpactir#getClassFullName()`. Thanks @voronkovich - - [WorseReflection] Reflect any user-defined functions parsed when including - the Composer autoloader. #562 - - [WorseReflection] Support trait alias maps. #540 - - [RPC] Return semantic RPC protocol version in response (starting at `1.0.0`). - - [Completion] Complete constructor parameters. - -Improvements: - - - [Rpc|VIM Plugin] Source code is now updated (by way of a diff algorithm) - not replaced. The cursor position and undo history are maintained. - - [VIM Plugin] Regression test for Transform RPC call. - - [Application] Make class completion candidate limit configurable. - - [WorseReflection] Foreach Frame walker: inject keys in foreach loop, #578 - - [RPC] find references: Do not return files with no concrete references, - #581 - - [CodeBuilder] Tracks which nodes have been modified after factory - creation. - - [ClassToFile] Composer class-to-file strategy no longer discards inferior - prefix lengths from consideration, fixes #576 - -Bug fixes: - - - [Completion] Fixed multi-byte issue with class completor. - - [VIM Plugin] Allow duplicate name suggestions (e.g. same class short-name - different FQNs) in omni-complete results. - - [CodeBuilder] Builder attempts to act on a string (when return type is f.e. - self). #529 - - [WorseReflection] Fix fatal error when `Parameter#getName()` returns NULL in - SymbolContextResolver. #533 - - [CodeBuilder] Fix for unrelated methods being updated, #583 - -## 2018-08-03 0.8.0 - -Improvements: - - - [WorseReflectoin] Smoke test for find parsing errors. - - [WorseReflection] Improved efficiency for frame building. - non-variable. - - [Completion] Improved multi-byte performance, fixes #537 thanks - @weirdan - -Bug fixes: - - - [WorseReflection] Handle fatal error on incomplete extends. - - [WorseReflection] Handle fatal error on instanceof coercion on - - [Completion] Fixed class member container resolution accuracy - - [SourceCodeFilesystem] Quote regular expressions in file list filter, fixes #543 - -Misc - - - [RPC] Refactored handlers to define input requirements more explicitly. - -## 2018-07-02 0.7.0 - -Features: - - - [CodeTransforn] Extract expression - - [Application] Changed behavior of Transform command: accepts globs, shows - diffs and writes to files (rather than just dumping them to stdout if they - changed). - - [Completion] Support constant completion - - [Application] Use version from composer instead of hard-coded version. - Thanks @weirdan - -Improvements: - - - [Completion] Support namespaced functions, fixes #473. - - [Completion] Sort completion results alphabetically. - - [Docs] Added section on completion. - - [WorseReflection] Explicitly do not support anonymous classes when - resolving nodes, fixes #505. - -Bug fixes: - - - [WorseReflection] Do not parse non-PHP files when building stub cache. - - [Completion] Fixed last non-whitespace char detection, fixes #504 - -Misc - - - Downgraded composer to 1.x as 2.x-dev now requires PHP 7.2 - -## 2018-06-16 0.6.0 - -Features: - - - [CodeTransform] Transformer to fix namesapce / class name #474 - -Improvements: - - - [WorseReflection] Resolve UseNameVariables (e.g. context menu `use ($f<>oo)`). #466 - - [Application] Improved status (show current version) #481 - - [CodeTransform] Better handling of new file generation - - [Docs] Added Development section - -Bug fixes: - - - [WorseReflection] access property on null error when resolving incomplete - function variable use. - - [CodeTransform] Generate method does can use pseudo type for return type #486 - - [Vim Plugin] Goto reference in a modified file causes warning #477. - - [Application] Overridden CWD not being passed to `Paths` (affected config - file resolution). - - [Application] Fixed find references regression (only the current class - wasn't being checked for references..) - -## 2018-05-20 0.5.0 - -Features: - - - [Completion] Parameter completion, suggests variables that are valid for - the parameter position. - -Refactoring: - - - [SourceCodeFilesystem] Public API accepts scalar paths in addition to - value objects. - -Improvements: - - - [Documentation] Updated VIM completion plugin docs including - `phpactor/ncm-phpactor` fork (mainline is not maintained currently). - -## 2018-05-01 0.4.0 - -Features: - - - [Navigation] Reflection navigation: navigate to related classes (currently - supports parent class and interfaces). - - [Completion] Built-in function completion, #371 - - [Completion] _Experimental_ class completion: complete use, new and - extends. Class names inferred from file names. - - [GotoDefinition] Goto function definitions (currently limited to functions - defined by the PHPStorm stubs). - -Improvements: - - - [ClassMover] Find/replace references will only traverse possible classes - when givn a known class member #349 (also it will no longer ask the scope, - instead defaulting to either composer or full-filesystem search depending - on env). - - [ClassMover] (RPC) Will update current (unsaved) source. - - [vim-plugin] Correctly handle expanding class when at beginning of word, #438 thanks @greg0ire - - [vim-plugin] Reload file before replacing contents, fixes #445 - - [vim-plugin] File references, do not show quick fix list if all references - are in current file. - - [vim-plugin] Completion - trigger on any word-like, fixes #443 - - [WorseReflection] Support for `@property` type override (but doesn't - create a "pretend" property). - - [Application] Pass the Phpactor vendor directory as an argument to the - Application and include vendor files (e.g. stubs) relative to that, fixes - #460 - - [Application] Use XDG data directory for cache. - - [Documentation] Typo fix, thanks @pierreboissinot - -Bug fixes: - - - [RPC] Import class from context menu, uses context class path - instead of current #448 - - [CodeBuilder] Regression where already-existing names are imported fixes - #452 - - [Application] Fixed location of cache directory. - - [Application] Fixed binary path, thanks @talbergs - - [RPC] Specify completion type for text input, fixes #455 - -Refactoring: - - - [WorseReflection] Full support for reflecting functions. - - [WorseReflection] All member collections extend common interface, - class-likes have a `members(): ReflectionMemberCollection` method. - - [Completion] Refactored to make interface more efficient, decoupled - formatting from completion. - - [Completion] Made existing completors a subset of tolerant-parser - completors (means there is one "chain" tolerant completor which delegates - to the other completors and we only have to parse once). - -## 0.3.0 - -Features: - - - [Application] Disable XDebug by default, very much improve performance. - Fixes #317 - -Improvements: - - - [Completion] Do not evaluate left operand when completing expression, - #380 - - [RPC] Request validation (no more undefined index errors). - - [WorseReflection] Classes inherit constants from interfaces. - - [CodeBuilder] Use statements added after the first lexigraphically - inferior existing use-statement, fixes #176. Thanks @greg0ire. - -Bug fixes: - - - [WorseReflection] Associated class for trait methods is the trait - itself, not the class it's used in, #412 - - [WorseReflection] Do not evaluate assignments with missing tokens. - - [SourceCodeFilesystem] Non-existing paths not ignored. - - [CodeTransform] Indentation not being taken into account for code - updates (fixes #423). - - [WorseReflection] Tolerate incomplete if statements, fixes #424 - - [WorseReflection] Tolerate missing token in expression evaluator #430 - -## 0.2.0 - -Features: - - - [VIM Plugin] `g:phpactorBranch` can be used to set the update branch. - - [WorseReflection] Support parenthesised expressions (i.e. complete for `(new Foobar())->`), #279 - -Improvements: - - - [Application] Large restructuring of code, almost everything is now in an extension. - - [WorseReflection] [problem with name import](https://github.com/phpactor/worse-reflection/pull/37) (thanks @adeslade) - - [WorseReflection] All class members implement common interface, fixes - #283 - - [VIM Plugin] Disable the omni-complete errors by default, as this breaks - the assumptions of some auto-complete managers (set - `g:phpactorOmniError` to `v:true` to enable again), fixes #370. - - [VIM Plugin] Only define settings if not already set. - - [WorseReflection] `Type#__toString` represents arrays and collections - - [WorseReflection] Improved `Type` class. - - [Completion] Use partial match to filter class members, fixes #321 - - [phpactor.vim] Correctly return start position for omni-complete - - [Docblock] Be tolerant of invalid tags, fixes #382 - - [WorseReflection] Refactored FrameBuilder: Extracted walkers - - [WorseReflection] [Expression evaluator](https://github.com/phpactor/worse-reflection/blob/master/lib/Core/Inference/ExpressionEvaluator.php). - -Bugfixes: - - - [SourceCodeFilesystem] Support symlinks in vendor dir #396 - - [WorseReflection] trait lists were not being correctly interpreted #320 - - [WorseReflection] could not find class "NULL"... - - [SourceCodeFilesystem] Support symlinks in vendor dir #396 - - [Dockblock] Tolerate extra spaces, fixes #365 - - [Completion] Was using the type of the first declared variable, instead - of the last before the offset. - - [Completion] Used `Type#__toString` to reflect class. - - [CodeBuilder] Extract method rewrites arguments #361 - - [VimPlugin] Fixed goto definition, #398 - - [WorseReflection] [problem with name import](https://github.com/phpactor/worse-reflection/pull/37) (thanks @adeslade) - -## 0.1.0 - -**2018-04-03** - -First tagged version, changes from 30th March. - -- **CodeTransform** - - New implementation of class import - - Offer to alias existing classes, - - Error message if class in same namespace, -- **Completion** - - New [Completion library](https://github.com/phpactor/completion). - - Improved formatting. - - Local variable completion. -- **Documentation** - - Configuration [documentation](http://phpactor.github.io/phpactor/configuration.html). - - Better Drupal integration (thanks @fenetikm). - - VIM Plugin documentation (`:help phpactor`) (thanks @joereynolds) -- **RPC** - - Request Replay: replay requests made from the IDE. -- **WorseReflection** - - Docblocks for Arrays and simple `Collection` supported. - - Foreach supported. - - Method `@param` supported. -- **Infrastructure** - - All packages are on packagist. - - [Infrastructure] Do not store PHPBench results on Travis if PR is a fork. -- Various bug fixes everywhere. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 981cd3ffbd..0000000000 --- a/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2018 Daniel Leech - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Makefile b/Makefile deleted file mode 100644 index 40f2f508fb..0000000000 --- a/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -W -SPHINXBUILD ?= sphinx-build -SPHINXAUTOBUILD ?= sphinx-autobuild -SOURCEDIR = doc -BUILDDIR = build - -.PHONY: help sphinx - -build: - mkdir build - -composer: - composer install --no-scripts --optimize-autoloader --classmap-authoritative - -vimdoc: - docker compose run php vimdoc . - -configreference: - ./bin/phpactor development:generate-documentation extension > doc/reference/configuration.rst - ./bin/phpactor development:generate-documentation rpc > doc/reference/rpc_command.rst - ./bin/phpactor development:generate-documentation diagnostic > doc/reference/diagnostic.rst - -# Put it first so that "make" without argument is like "make help". -help: - docker compose run php $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -sphinxwatch: - docker compose run php $(SPHINXAUTOBUILD) "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -sphinx: - docker compose run php $(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -sphinxlatex: - docker compose run php $(SPHINXBUILD) -M latex "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -docs: composer configreference vimdoc sphinx - -clean: - rm -Rf build diff --git a/README.md b/README.md deleted file mode 100644 index c78442927c..0000000000 --- a/README.md +++ /dev/null @@ -1,90 +0,0 @@ -Phpactor -======== - -> [!WARNING] -> This language server may not be the one you're looking for. -> -> I personally use it and will continue to maintain it until I don't use it -> anymore. There are new and interesting open source language servers being -> developed that can offer far more performant capabilities. I think Phpactor -> still offers some great features, especially in regards to code actions and -> I genuinely miss some functionality when using servers for Rust, Go and -> Typescript. -> -> There are many parts of this project that I'm proud of but ultimately it has -> some short-comings in regards to performance, accuracy and maintainability -> that I'm not capable of addressing at this time. -> [YMMV](https://en.wiktionary.org/wiki/your_mileage_may_vary). - -![phpactor2sm](https://user-images.githubusercontent.com/530801/27995098-82e72c4c-64c0-11e7-96d2-f549c711ca8b.png) - -![CI](https://github.com/phpactor/phpactor/workflows/CI/badge.svg?branch=master) - -This project aims to provide heavy-lifting *refactoring* and *introspection* -tools which can be used standalone or as the backend for a text editor to -provide intelligent code completion. - -- Accurate code [completion](https://phpactor.readthedocs.io/en/master/reference/completion.html) including class name auto-import. -- [Various](https://phpactor.readthedocs.io/en/master/reference/refactorings.html) refactoring,fixes and code generation options. -- Provides a [Language Server](https://phpactor.readthedocs.io/en/master/usage/language-server.html) -- Native [VIM plugin](https://phpactor.readthedocs.io/en/master/usage/vim-plugin.html) ([emacs](https://github.com/emacs-php/phpactor.el) plugin is in development). -- [Navigation](https://phpactor.readthedocs.io/en/master/reference/navigation.html) (jump to - definition, related classes, references etc). -- [More](https://phpactor.readthedocs.io/en/master). - -Installation ------------- - -Phpactor is a general tool, it is not intended that it be installed as a project dependency. - -See -[Installation](https://phpactor.readthedocs.io/en/master/usage/standalone.html) -for installation instructions. - -Requirements ------------- - -- PHP 8.2+ -- PHP [mbstring](https://www.php.net/manual/en/book.mbstring.php) extension. -- [Composer](https://getcomposer.org/) -- Linux or MacOS (Windows users will need to use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install)) - -Project Recommendations ------------------------ - -Phpactor will perform better with [Composer](https://getcomposer.org) and, to -a lesser extent, with [GiT](https://git-scm.org). - -Documentation -------------- - -Full documentation can be found on [Read the Docs](https://phpactor.readthedocs.io/en/master) - -Community ---------- - -- Follow [@phpactor](https://phpc.social/@phpactor) on 🦣 Mastodon for latest news. -- Join the `#phpactor` channel on the Slack [Symfony Devs](https://symfony.com/slack-invite) channel. - -Contributing ------------- - -This package is open source and welcomes contributions! Feel free to open a -pull request on this repository. - -Support -------- - -- Create an issue on the main [Phpactor](https://github.com/phpactor/phpactor) repository. -- Join the `#phpactor` channel on the Slack [Symfony Devs](https://symfony.com/slack-invite) channel. - -Sponsors --------- - -The following organisations are providing either financial support or free -services. If you would like to support Phpactor development financially you -can [sponsor me](https://github.com/sponsors/dantleech). - -### Tinkerwell - -[![Tinkerwell](https://user-images.githubusercontent.com/530801/172365695-f60dcd49-315f-48df-b146-7316697a30bd.png)](https://tinkerwell.app/) diff --git a/autoload/health/phpactor.vim b/autoload/health/phpactor.vim deleted file mode 100644 index bc8bf6b88d..0000000000 --- a/autoload/health/phpactor.vim +++ /dev/null @@ -1,40 +0,0 @@ -function! s:check_info(status) abort - call v:lua.vim.health.start('Info') - - call v:lua.vim.health.info('Phpactor version: '. a:status.phpactor_version) - call v:lua.vim.health.info('PHP version: '. a:status.php_version) - call v:lua.vim.health.info('Filesystems'. join(a:status.filesystems, ', ')) - call v:lua.vim.health.info('Working directory'. a:status.cwd) -endfunction - -function! s:check_diagnostics(diagnostics) abort - call v:lua.vim.health.start('Diagnostics') - - for [l:diagnostic, l:isOk] in items(a:diagnostics) - if l:isOk - call v:lua.vim.health.ok(l:diagnostic) - else - call v:lua.vim.health.warn(l:diagnostic) - endif - endfor -endfunction - -function! s:check_config_files(configFiles) abort - call v:lua.vim.health.start('Config files (missing is not bad)') - - for [l:configFile, l:isOk] in items(a:configFiles) - if l:isOk - call v:lua.vim.health.ok(l:configFile) - else - call v:lua.vim.health.warn(l:configFile) - endif - endfor -endfunction - -function! health#phpactor#check() abort - let l:status = phpactor#rpc('status', {'type': 'detailed'}) - - call s:check_info(l:status) - call s:check_diagnostics(l:status.diagnostics) - call s:check_config_files(l:status.config_files) -endfunction diff --git a/autoload/phpactor.vim b/autoload/phpactor.vim deleted file mode 100644 index c14f202b90..0000000000 --- a/autoload/phpactor.vim +++ /dev/null @@ -1,856 +0,0 @@ -"" -" @section Introduction, intro -" @order intro config completion commands mappings -" -" Phpactor is a auto-completion, refactoring and code-navigation tool for PHP. -" This is the help file for the VIM client. For more information see the -" official website: https://phpactor.github.io/phpactor/ -" -" NOTE: This help is auto-generated from the VimScript using -" https://github.com/google/vimdoc. See -" https://phpactor.github.io/phpactor/developing.html#vim-help - -let s:_phpactorCompletionMeta = {} - -function! phpactor#Update() - let current = getcwd() - execute 'cd ' . g:phpactorpath - echo system('git checkout ' . g:phpactorBranch) - echo system('git pull origin ' . g:phpactorBranch) - echo system('composer install --optimize-autoloader --classmap-authoritative') - execute 'cd ' . current -endfunction - -function! phpactor#Complete(findstart, base) - - let lineOffset = line2byte(line(".")) - - " get the source up until the cursor - let source = join(getline(1,line('.') - 1), "\n") - let partialLine = getline(line('.'))[0:col('.') - 2] - let source = source . "\n" . partialLine - - if a:findstart - - let patterns = ["[\$0-9A-Za-z_]\\+$"] - - for pattern in patterns - let pos = match(source, pattern) - - if -1 != pos - return pos - lineOffset + 1 - endif - endfor - - return -1 - endif - - let offset = lineOffset + col('.') - 2 - let offset = offset + strlen(a:base) - let source = source . a:base . "\n" . join(getline(line('.') + 1, '$'), "\n") - - let result = phpactor#rpc("complete", { "offset": offset, "source": source, "type": &ft}) - let suggestions = result['suggestions'] - let issues = result['issues'] - - let completions = [] - let s:_phpactorCompletionMeta = {} - - if !empty(suggestions) - for suggestion in suggestions - let completion = { - \ 'word': suggestion['name'], - \ 'abbr': phpactor#_completeTruncateLabel(suggestion['label'], g:phpactorCompleteLabelTruncateLength), - \ 'menu': suggestion['short_description'], - \ 'kind': suggestion['type'], - \ 'dup': 1, - \ 'icase': g:phpactorCompletionIgnoreCase - \ } - call add(completions, completion) - let s:_phpactorCompletionMeta[phpactor#_completionItemHash(completion)] = suggestion - endfor - endif - - return completions -endfunction - -function! phpactor#_completeTruncateLabel(label, length) - if strlen(a:label) < a:length - return a:label - endif - - return strpart(a:label, 0, a:length - 3) . '...' -endfunction - -function! phpactor#_completionItemHash(completion) - return a:completion['word'] . a:completion['menu'] . a:completion['kind'] -endfunction - -function! phpactor#_completeImportClass(completedItem) - - if get(b:, 'phpactorOmniAutoClassImport', g:phpactorOmniAutoClassImport) != v:true - return - endif - - if !has_key(a:completedItem, "word") - return - endif - - let hash = phpactor#_completionItemHash(a:completedItem) - if !has_key(s:_phpactorCompletionMeta, hash) - return - endif - - let suggestion = s:_phpactorCompletionMeta[hash] - - if !empty(get(suggestion, "class_import", "")) - call phpactor#rpc("import_class", { - \ "qualified_name": suggestion['class_import'], - \ "offset": phpactor#_offset(), - \ "source": phpactor#_source(), - \ "path": expand('%:p')}) - endif - - let s:_phpactorCompletionMeta = {} - -endfunction - -function! phpactor#ExtractMethod(...) - let positions = {} - - if 0 == a:0 " Visual mode - backward compatibility - let positions.start = phpactor#_selectionStart() - let positions.end = phpactor#_selectionEnd() - elseif a:1 ==? 'v' " Visual mode - let positions.start = phpactor#_selectionStart() - let positions.end = phpactor#_selectionEnd() - else " Linewise or characterwise motion - let linewise = 'line' == a:1 - - let positions.start = s:getStartOffsetFromMark("'[", linewise) - let positions.end = s:getEndOffsetFromMark("']", linewise) - endif - - call phpactor#rpc("extract_method", { "path": phpactor#_path(), "offset_start": positions.start, "offset_end": positions.end, "source": phpactor#_source()}) -endfunction - -function! phpactor#ExtractExpression(type) - let positions = {} - - if v:true == a:type " Invoked from Visual mode - backward compatibility - let positions.start = phpactor#_selectionStart() - let positions.end = phpactor#_selectionEnd() - elseif v:false == a:type " Invoked from an offset - backward compatibility - let positions.start = phpactor#_offset() - let positions.end = v:null - elseif a:type ==? 'v' " Visual mode - let positions.start = phpactor#_selectionStart() - let positions.end = phpactor#_selectionEnd() - else " Linewise or characterwise motion - let linewise = 'line' == a:type - - let positions.start = s:getStartOffsetFromMark("'[", linewise) - let positions.end = s:getEndOffsetFromMark("']", linewise) - endif - - call phpactor#rpc("extract_expression", { "path": phpactor#_path(), "offset_start": positions.start, "offset_end": positions.end, "source": phpactor#_source()}) -endfunction - -function! phpactor#ExtractConstant() - call phpactor#rpc("extract_constant", { "offset": phpactor#_offset(), "source": phpactor#_source(), "path": phpactor#_path()}) -endfunction - -function! phpactor#ClassExpand() - let word = expand("") - let classInfo = phpactor#rpc("class_search", { "short_name": word }) - - if (empty(classInfo)) - return - endif - - let line = getline('.') - let char = line[col('.') - 2] - let namespace_prefix = classInfo['class_namespace'] . "\\" - - " otherwise goto start of word - execute "normal! ciw" . namespace_prefix.word -endfunction - -function! phpactor#UseAdd() - call phpactor#ImportClass() -endfunction -function! phpactor#ImportClass() - call phpactor#rpc("import_class", {"offset": phpactor#_offset(), "source": phpactor#_source(), "path": expand('%:p')}) -endfunction -function! phpactor#ImportMissingClasses() - call phpactor#rpc("import_missing_classes", {"source": phpactor#_source(), "path": expand('%:p')}) -endfunction - -"" -" @default target=`edit` -" @default mods='' -" -" Goto the definition of the symbol under the cursor. -" Open the definition in the [target] window, see @section(window-target) for -" the list of possible targets. -" [mods] is a string containing || values separated by a space. -" -" Examples: -" > -" " Opens in the current buffer -" call phpactor#GotoDefinition() -" -" " Opens in a vertical split opened on the right side -" call phpactor#GotoDefinition('split', 'vertical botright') -" -" " Opens in a new tab -" call phpactor#GotoDefinition('tabnew') -" < -function! phpactor#GotoDefinition(...) - let target = !empty(a:0 ? a:1 : '') ? a:1 : 'edit' - let mods = a:0 > 1 ? a:2 : '' - - call phpactor#rpc("goto_definition", { - \ "offset": phpactor#_offset(), - \ "source": phpactor#_source(), - \ "path": expand('%:p'), - \ 'language': &ft, - \ }, { - \ "target": target, - \ "mods": mods, - \ }) -endfunction - -"" -" @usage [target] [mods] -" -" Same as @function(phpactor#GotoDefinition) but goto the implementation of -" the symbol under the cursor. -" -" If there is more than one result the quickfix strategy will be used and [target] -" will be ignored, see @setting(g:phpactorQuickfixStrategy). -function! phpactor#GotoImplementations(...) - let target = !empty(a:0 ? a:1 : '') ? a:1 : 'edit' - let mods = a:0 > 1 ? a:2 : '' - - call phpactor#rpc("goto_implementation", { - \ "offset": phpactor#_offset(), - \ "source": phpactor#_source(), - \ "path": expand('%:p'), - \ 'language': &ft, - \ }, { - \ "target": target, - \ "mods": mods, - \ }) -endfunction - -"" -" @usage [target] [mods] -" -" Same as @function(phpactor#GotoDefinition) but goto the type of -" the symbol under the cursor. -function! phpactor#GotoType(...) - let target = !empty(a:0 ? a:1 : '') ? a:1 : 'edit' - let mods = a:0 > 1 ? a:2 : '' - - call phpactor#rpc('goto_type', { - \ 'offset': phpactor#_offset(), - \ 'source': phpactor#_source(), - \ 'path': expand('%:p'), - \ 'language': &ft, - \ }, { - \ "target": target, - \ "mods": mods, - \ }) -endfunction - -function! phpactor#Hover() - call phpactor#rpc("hover", { "offset": phpactor#_offset(), "source": phpactor#_source() }) -endfunction - -function! phpactor#ContextMenu() - call phpactor#rpc("context_menu", { "offset": phpactor#_offset(), "source": phpactor#_source(), "current_path": expand('%:p') }) -endfunction - -function! phpactor#CopyFile() - call phpactor#rpc("copy_class", { "source_path": phpactor#_path() }) -endfunction - -function! phpactor#MoveFile() - call phpactor#rpc("move_class", { "source_path": phpactor#_path() }) -endfunction - -function! phpactor#Trust() - call phpactor#rpc("trust", { "trust": 1 }) -endfunction - -function! phpactor#Untrust() - call phpactor#rpc("trust", { "trust": 0 }) -endfunction - -function! phpactor#OffsetTypeInfo() - call phpactor#rpc("offset_info", { "offset": phpactor#_offset(), "source": phpactor#_source()}) -endfunction - -function! phpactor#Transform(...) - let transform = get(a:, 1, '') - - let args = { "path": phpactor#_path(), "source": phpactor#_source() } - - if transform != '' - let args.transform = transform - endif - - call phpactor#rpc("transform", args) -endfunction - -function! phpactor#ClassNew() - call phpactor#rpc("class_new", { "current_path": phpactor#_path() }) -endfunction - -function! phpactor#ClassInflect() - call phpactor#rpc("class_inflect", { "current_path": phpactor#_path() }) -endfunction - -" Deprecated!! Use FindReferences -function! phpactor#ClassReferences() - call phpactor#FindReferences() -endfunction - -function! phpactor#FindReferences() - call phpactor#rpc("references", { "offset": phpactor#_offset(), "source": phpactor#_source(), "path": phpactor#_path()}) -endfunction - -function! phpactor#Navigate() - call phpactor#rpc("navigate", { "source_path": phpactor#_path() }) -endfunction - -function! phpactor#CacheClear() - call phpactor#rpc("cache_clear", {}) -endfunction - -function! phpactor#Status() - if exists(':terminal') - let l:workspaceDir = phpactor#getRootDirectory() - - " note that we should escape these arguments, but using the list syntax - " here causes tests to fail on travis with VIM 8.3 ... - let l:cmd = g:phpactorPhpBin . ' ' . g:phpactorbinpath . ' status --working-dir=' . l:workspaceDir - - if has('nvim') - execute 'split term://' . l:cmd - " Press any key to leave - normal i - else - execute 'terminal ' . l:cmd - endif - else - call phpactor#rpc("status", {'type': 'formatted'}) - endif -endfunction - -function! phpactor#Config() - call phpactor#rpc("config", {}) -endfunction - -function! phpactor#GetNamespace() - let fileInfo = phpactor#rpc("file_info", { "path": phpactor#_path() }) - - return fileInfo['class_namespace'] -endfunction - -function! phpactor#GetClassFullName() - let fileInfo = phpactor#rpc("file_info", { "path": phpactor#_path() }) - - return fileInfo['class'] -endfunction - -function! phpactor#CopyFullClassName() - let className = phpactor#GetClassFullName() - if empty(className) - echo "No class name found for the current file" - else - let @+ = className - echo printf("Class Name copied to clipboard: %s", className) - endif -endfunction - -function! phpactor#ChangeVisibility() - call phpactor#rpc("change_visibility", { "offset": phpactor#_offset(), "source": phpactor#_source(), "path": expand('%:p') }) -endfunction - -function! phpactor#GenerateAccessors() - call phpactor#rpc("generate_accessor", { "source": phpactor#_source(), "path": expand('%:p'), 'offset': phpactor#_offset() }) -endfunction - -function! phpactor#GenerateMutators() - call phpactor#rpc("generate_mutator", { "source": phpactor#_source(), "path": expand('%:p'), 'offset': phpactor#_offset() }) -endfunction - -""""""""""""""""""""""" -" Utility functions -""""""""""""""""""""""" -function! s:isOpenInCurrentWindow(filePath) - return phpactor#_path() == a:filePath -endfunction - -function! phpactor#_switchToBufferOrEdit(filePath) - if s:isOpenInCurrentWindow(a:filePath) - return v:false - endif - - let bufferNumber = bufnr(a:filePath . '$') - - let command = (bufferNumber == -1) - \ ? ":edit " . a:filePath - \ : ":buffer " . bufferNumber - - exec command -endfunction - -function! phpactor#_offset() - return line2byte(line('.')) + col('.') - 1 -endfunction - -function! phpactor#_source() - return join(getline(1,'$'), "\n") -endfunction - -function! phpactor#_path() - let l:path = expand('%:p') - - if filereadable(l:path) || stridx(l:path, '/') == 0 - return l:path - endif - - " todo if empty path - " - return printf('%s/%s', g:phpactorInitialCwd, l:path) -endfunction - -function! s:getStartOffsetFromMark(mark, linewise) - let [line, column] = getpos(a:mark)[1:2] - let offset = line2byte(line) - - if v:true == a:linewise - return offset - 1 - endif - - return offset + column - 2 -endfunction - -function! s:getEndOffsetFromMark(mark, linewise) - let [line, column] = getpos(a:mark)[1:2] - let offset = line2byte(line) - let lineLenght = strlen(getline(line)) - - if v:true == a:linewise - return offset + lineLenght - 1 - endif - - " Note VIM returns 2,147,483,647 on this system when in block select mode - if (column > 1000000) - let column = lineLenght - endif - - return offset + column - 1 -endfunction - -function! phpactor#_selectionStart() - return s:getStartOffsetFromMark("'<", v:false) -endfunction - -function! phpactor#_selectionEnd() - return s:getEndOffsetFromMark("'>", v:false) -endfunction - -function! phpactor#_applyTextEdits(path, edits) - call phpactor#_switchToBufferOrEdit(a:path) - - let postCursorPosition = getpos('.') - let curLine = postCursorPosition[1] - let numberOfLinesToPreviousPosition = 0 - - for edit in a:edits - let startLine = edit.start.line - let endLine = edit.end.line - - if edit.start.character != 0 || edit.end.character != 0 - throw "Non-zero character offsets not supported in text edits, got " . json_encode(edit) - endif - - let numberOfDeletedLines = endLine - startLine - if numberOfDeletedLines > 0 - silent execute printf('keepjumps %d,%dd _', startLine + 1, endLine) - - if startLine < curLine && curLine <= endLine - let numberOfLinesToPreviousPosition += endLine - curLine + 1 - elseif endLine < curLine - let curLine -= numberOfDeletedLines - endif - endif - - let newLines = edit.text == "\n" ? [''] : split(edit.text, "\n") - keepjumps call append(startLine, newLines) - - if startLine < curLine - let curLine += len(newLines) - endif - endfor - - let postCursorPosition[1] = curLine - numberOfLinesToPreviousPosition - call setpos('.', postCursorPosition) -endfunction - -function! phpactor#getRootDirectory() abort - let l:Strategy = get(b:, 'PhpactorRootDirectoryStrategy', g:PhpactorRootDirectoryStrategy) - - if type(function('type')) != type(l:Strategy) - let l:Strategy = {-> g:phpactorInitialCwd} - endif - - return l:Strategy() -endfunction - -""""""""""""""""""""""" -" RPC -->-->-->-->-->-- -""""""""""""""""""""""" - -function! phpactor#rpc(action, arguments, ...) - " Remove any existing output in the message window - execute ':redraw' - - let request = { "action": a:action, "parameters": a:arguments } - - let l:workspaceDir = phpactor#getRootDirectory() - - " note that we should escape these arguments, but using the list syntax - " here causes tests to fail on travis with VIM 8.3 ... - let l:cmd = g:phpactorPhpBin . ' ' . g:phpactorbinpath . ' rpc --working-dir=' . l:workspaceDir - - let result = system(l:cmd, json_encode(request)) - - if (v:shell_error == 0) - try - let response = json_decode(result) - catch - throw "Could not parse response from Phpactor: " . v:exception - endtry - - let actionName = response['action'] - let parameters = extend(copy(response['parameters']), a:0 ? a:1 : {}) - - let response = phpactor#_rpc_dispatch(actionName, parameters) - - if !empty(response) - return response - endif - else - echo "Phpactor returned an error: " . result - return - endif -endfunction - -function! phpactor#_rpc_dispatch(actionName, parameters) - - " >> return_choice - if a:actionName == "return" - return a:parameters["value"] - endif - - " >> return_choice - if a:actionName == "return_choice" - let list = [] - let c = 1 - for choice in a:parameters["choices"] - call add(list, c . ") " . choice["name"]) - let c = c + 1 - endfor - - let choice = inputlist(list) - - if (choice == 0) - return - endif - - let choice = choice - 1 - - return a:parameters["choices"][choice]["value"] - endif - - " >> echo - if a:actionName == "echo" - echo a:parameters["message"] - return - endif - - " >> error - if a:actionName == "error" - echo "Error from Phpactor: " . a:parameters["message"] - return - endif - - " >> collection - if a:actionName == "collection" - for action in a:parameters["actions"] - let result = phpactor#_rpc_dispatch(action["name"], action["parameters"]) - - if !empty(result) - return result - endif - endfor - - return - endif - - " >> open_file - if a:actionName == "open_file" - let changedFileOrWindow = v:true - - let l:target = get(a:parameters, 'target', 'edit') - let l:mods = get(a:parameters, 'mods', '') - - if 'focused_window' ==# l:target - let l:target = 'edit' - elseif 'hsplit' ==# l:target - let l:target = 'split' - elseif 'new_tab' ==# l:target - let l:target = 'tabedit' - endif - - call s:openFileInSelectedTarget( - \ a:parameters["path"], - \ l:target, - \ l:mods, - \ get(a:parameters, "use_open_window", g:phpactorUseOpenWindows), - \ a:parameters["force_reload"] - \ ) - - if a:parameters["target"] == 'edit' - let changedFileOrWindow = !s:isOpenInCurrentWindow(a:parameters["path"]) - endif - - if (a:parameters['offset']) - let keepjumps = changedFileOrWindow ? 'keepjumps' : '' - - exec keepjumps . ":goto " . (a:parameters['offset'] + 1) - normal! zz - endif - return - endif - - " >> close_file - if a:actionName == "close_file" - let bufferNumber = bufnr(a:parameters['path']. '$') - - if (bufferNumber == -1) - return - endif - - exec ":bdelete " . bufferNumber - return - endif - - " >> file references - if a:actionName == "file_references" - " if there is only one file, and it is the open file, don't - " bother opening the quick fix window - if len(a:parameters['file_references']) == 1 - let fileRefs = a:parameters['file_references'][0] - if -1 != match(fileRefs['file'], bufname('%') . '$') - return - endif - endif - - let results = [] - for fileReferences in a:parameters['file_references'] - for reference in fileReferences['references'] - call add(results, { - \ 'filename': fileReferences['file'], - \ 'lnum': reference['line_no'], - \ 'col': reference['col_no'] + 1, - \ 'text': reference['line'] - \ }) - endfor - endfor - - call phpactor#quickfix#build(results) - - return - endif - - " >> input_callback - if a:actionName == "input_callback" - let inputs = a:parameters['inputs'] - let action = a:parameters['callback']['action'] - let parameters = a:parameters['callback']['parameters'] - - try - return phpactor#_rpc_dispatch_input(inputs, action, parameters) - catch /cancelled/ - redraw - echo 'Cancelled' - return - endtry - endif - - " >> information - if a:actionName == "information" - " We write to a temporary file and then "edit" it in the preview - " window. Not sure if there is a better way to do this. - let temp = resolve(tempname()) - execute 'pedit ' . temp - wincmd P - call append(0, split(a:parameters['information'], "\n")) - execute ":1" - silent write! - wincmd p - return - endif - - " >> update file source - " - " NOTE: This method currently works on a line-by-line basis as currently - " supported by Phpactor. We calculate the cursor offset by the - " number of lines inserted before the actual cursor line. Character - " offset is not taken into account, so same-line edits will cause an - " incorrect post-edit cursor character offset. - " - if a:actionName == "update_file_source" - call phpactor#_applyTextEdits(a:parameters['path'], a:parameters['edits']) - return - endif - - " >> replace_file_source - if a:actionName == "replace_file_source" - - " if the file is open in a buffer, reload it before replacing it's - " source (avoid file-modified-on-disk errors) - if -1 != bufnr(a:parameters['path'] . '$') - exec ":edit! " . a:parameters['path'] - endif - - call phpactor#_switchToBufferOrEdit(a:parameters['path']) - - " save the cursor position - let savePos = getpos(".") - - " delete everything into the blackhole buffer - exec "%d _" - - " insert the transformed source code - execute ":put =a:parameters['source']" - - " `put` will leave a blank line at the start of the file, remove it - execute ":1delete _" - - " restore the cursor position - call setpos('.', savePos) - return - endif - - throw "Do not know how to handle action '" . a:actionName . "'" -endfunction - -"" -" @section Window targets, window-targets -" @parentsection commands -" -" Phpactor provide a few window targets to use with some commands. -" See @command(PhpactorGotoDefinition) for an example of how to use them. -" -" Possible values are: -" * `e`, `edit`, `ex` -" * `new`, `vne`, `vnew` -" * `sp`, `split`, `vs`, `vsplit` -" * `vie`, `view`, `sv`, `sview`, `splitview` -" * `tabe`, `tabedit`, `tabnew` - -function! phpactor#_window_targets() abort - return [ - \ 'e', 'edit', 'ex', - \ 'new', 'vne', 'vnew', - \ 'sp', 'split', 'vs', 'vsplit', - \ 'vie', 'view', 'sv', 'sview', 'splitview', - \ 'tabe', 'tabedit', 'tabnew', - \ ] -endfunction - -function! s:openFileInSelectedTarget(filePath, target, mods, useOpenWindow, forceReload) - let l:bufferNumber = bufnr(a:filePath . '$') - - if v:true == a:useOpenWindow && -1 != l:bufferNumber - let l:firstWindowId = get(win_findbuf(l:bufferNumber), 0, v:null) - - if v:null != l:firstWindowId - call win_gotoid(l:firstWindowId) - if v:true == a:forceReload - exec 'e!' - endif - return - endif - endif - - if -1 == index(phpactor#_window_targets(), a:target) - echohl WarningMsg - echomsg printf('Error executing: %s %s %s', a:mods, a:target, a:filePath) - echomsg printf('Invalid target: %s', a:target) - echomsg printf('Valid targets are: %s', join(phpactor#_window_targets(), ', ')) - echohl None - return - endif - - execute printf('%s %s %s', a:mods, a:target, a:filePath) -endfunction - -function! phpactor#_rpc_dispatch_input_handler(Next, parameters, parameterName, result) - let a:parameters[a:parameterName] = a:result - - call a:Next(a:parameters) -endfunction - -function! phpactor#_rpc_dispatch_input(inputs, action, parameters) - let input = remove(a:inputs, 0) - let inputParameters = input['parameters'] - - let Next = empty(a:inputs) - \ ? function('phpactor#rpc', [a:action]) - \ : function('phpactor#_rpc_dispatch_input', [a:inputs, a:action]) - - let ResultHandler = function('phpactor#_rpc_dispatch_input_handler', [ - \ Next, - \ a:parameters, - \ input['name'], - \ ]) - - " Remove any existing output in the message window - execute ':redraw' - - if 'text' == input['type'] - let TypeHandler = function('phpactor#input#text', [ - \ inputParameters['label'], - \ inputParameters['default'], - \ inputParameters['type'] - \ ]) - elseif 'choice' == input['type'] - let TypeHandler = function('phpactor#input#choice', [ - \ inputParameters['label'], - \ inputParameters['choices'], - \ inputParameters['keyMap'] - \ ]) - elseif 'list' == input['type'] - let TypeHandler = function('phpactor#input#list', [ - \ inputParameters['label'], - \ inputParameters['choices'], - \ inputParameters['multi'] - \ ]) - elseif 'confirm' == input['type'] - let TypeHandler = function('phpactor#input#confirm', [ - \ inputParameters['label'] - \ ]) - else - throw "Do not know how to handle input '" . input['type'] . "'" - endif - - call TypeHandler(ResultHandler) -endfunction diff --git a/autoload/phpactor/completion.vim b/autoload/phpactor/completion.vim deleted file mode 100644 index a790725ec1..0000000000 --- a/autoload/phpactor/completion.vim +++ /dev/null @@ -1,28 +0,0 @@ -"" -" @section Completion -" -" You will need to explicitly configure Phpactor to provide completion -" capabilities. -" -" @subsection Omni-Completion -" -" Use VIMs native omni-completion (|compl-omni|) -" -" Enable omni-completion for PHP files: > -" -" autocmd FileType php setlocal omnifunc=phpactor#Complete -" -" For case sensitive searching see @setting(g:phpactorCompletionIgnoreCase) -" -" @subsection NCM2 -" -" Nvim Completion Manager is a completion manager for Neovim. -" -" Install the integration plugin to get started: https://github.com/phpactor/ncm2-phpactor -" -" @subsection Deoplete -" -" Deoplete is another completion plugin. -" -" Install the Deoplete Phpactor -" integration to get started: https://github.com/kristijanhusak/deoplete-phpactor diff --git a/autoload/phpactor/input.vim b/autoload/phpactor/input.vim deleted file mode 100644 index 29d15728a4..0000000000 --- a/autoload/phpactor/input.vim +++ /dev/null @@ -1,110 +0,0 @@ -function! phpactor#input#text(label, default, completionType, ResultHandler) - if v:null != a:completionType - let text = input(a:label, a:default, a:completionType) - else - let text = input(a:label, a:default) - endif - - call a:ResultHandler(text) -endfunction - -function! phpactor#input#confirm(label, ResultHandler) - let choice = confirm(a:label, "&Yes\n&No\n") - - if choice == 1 - let response = v:true - else - let response = v:false - endif - - call a:ResultHandler(response) -endfunction - -let s:usedShortcuts = [] -function! phpactor#input#choice(label, choices, keyMap, ResultHandler) - let s:usedShortcuts = [] - let list = [] - - if empty(a:choices) - call confirm("No choices available") - throw "cancelled" - endif - - for choiceLabel in keys(a:choices) - let buffer = [] - - " note that a:keyMap can be an empty list because PHP's json_decode - " can't tell the difference between an empty list and an empty dict - if !empty(a:keyMap) && has_key(a:keyMap, choiceLabel) && !empty(a:keyMap[choiceLabel]) - let confirmLabel = s:determineConfirmLabelFromPreference(choiceLabel, a:keyMap[choiceLabel]) - else - let confirmLabel = s:determineConfirmLabel(choiceLabel) - endif - - call add(list, confirmLabel) - endfor - - let choice = confirm(a:label, join(list, "\n")) - - if (choice == 0) - " this is an exception, not a message! - throw "cancelled" - endif - - call a:ResultHandler(keys(a:choices)[choice - 1]) -endfunction - -function! s:determineConfirmLabelFromPreference(choiceLabel, preference) - let buffer = [] - let foundShortcut = v:false - - for char in split(a:choiceLabel, '\zs') - if foundShortcut == v:false && tolower(char) == tolower(a:preference) - let foundShortcut = v:true - - call add(buffer, '&' . a:preference) - call add(s:usedShortcuts, a:preference) - continue - endif - - call add(buffer, char) - endfor - - if foundShortcut == v:false - " Could not find char in the label - add the shortcut at the end - call add(buffer, '&' . a:preference) - endif - - return join(buffer, "") -endfunction - -function! s:determineConfirmLabel(choiceLabel) - let foundShortcut = v:false - let buffer = [] - for char in split(a:choiceLabel, '\zs') - if v:false == foundShortcut && -1 == index(s:usedShortcuts, tolower(char)) - let foundShortcut = v:true - - call add(buffer, '&') - call add(s:usedShortcuts, tolower(char)) - endif - - call add(buffer, char) - endfor - - return join(buffer, "") -endfunction - -function! phpactor#input#list(label, choices, multi, ResultHandler) - let choices = sort(keys(a:choices)) - - try - let strategy = g:phpactorInputListStrategy - call call(strategy, [a:label, choices, a:multi, a:ResultHandler]) - catch /E117/ - redraw! - echo 'The strategy "'. strategy .'" is unknown, check the value of "g:phpactorInputListStrategy".' - endtry -endfunction - -" vim: et ts=4 sw=4 fdm=marker diff --git a/autoload/phpactor/input/list.vim b/autoload/phpactor/input/list.vim deleted file mode 100644 index 5bcfef8ee8..0000000000 --- a/autoload/phpactor/input/list.vim +++ /dev/null @@ -1,59 +0,0 @@ -function! phpactor#input#list#inputlist(label, choices, multi, ResultHandler) - echo a:label - let choice = inputlist(s:add_number_to_choices(a:choices)) - - if (choice == 0) - throw "cancelled" - endif - - call a:ResultHandler(a:choices[choice - 1]) -endfunction - -" expreimental: this stategy currently does not work when used in a -" non-terminal RPC step - https://github.com/phpactor/phpactor/issues/845 -function! phpactor#input#list#fzf(label, choices, multi, ResultHandler) - let options = [ - \ '--tiebreak=index', - \ '--layout=reverse-list', - \ ] - let sink = { - \ 'sink': {key -> a:ResultHandler(a:choices[key - 1])}, - \ } - - if a:multi - call extend(options, [ - \ '--multi', - \ '--bind=ctrl-a:select-all,ctrl-d:deselect-all', - \ ]) - - let sink = { - \ 'sink*': {results -> a:ResultHandler(map( - \ results, - \ {key, value -> a:choices[value - 1]} - \ ))} - \ } - endif - - " sink works because "key" is converted to integer, so only the number is kept - call fzf#run(extend({ - \ 'source': s:add_number_to_choices(a:choices), - \ 'down': '30%', - \ 'options': options - \ }, sink)) -endfunction - -function! s:auto_detect_strategy() - let strategy = 'inputlist' - - if get(g:, 'loaded_fzf', 0) - let strategy = 'fzf' - endif - - return 'phpactor#input#list#'. strategy -endfunction - -function! s:add_number_to_choices(choices) - return map(copy(a:choices), {key, value -> key + 1 .') '. value}) -endfunction - -" vim: et ts=4 sw=4 fdm=marker diff --git a/autoload/phpactor/quickfix.vim b/autoload/phpactor/quickfix.vim deleted file mode 100644 index 6ec7c2536b..0000000000 --- a/autoload/phpactor/quickfix.vim +++ /dev/null @@ -1,145 +0,0 @@ -function! phpactor#quickfix#vim(entries) abort - call setqflist(a:entries) - cw -endfunction - -function! phpactor#quickfix#build(entries) abort - try - let strategy = g:phpactorQuickfixStrategy - call call(strategy, [a:entries]) - catch /E117/ - redraw! - echo 'The strategy "'. string(strategy) .'" is unknown, check the value of "g:phpactorQuickfixStrategy".' - endtry -endfunction - -function! phpactor#quickfix#fzf(entries) abort - " Associate each entry data with a unique key - let entries = {} - " Keep track of the order of the entries by their key - let sortedKeys = [] - for entry in a:entries - let key = s:relative_path(entry['filename']) - \ .':'. entry['lnum'] - \ .':'. (entry['col']) - \ .':'. entry['text'] - - let entries[key] = entry - call add(sortedKeys, key) - endfor - - let formatedEntries = s:align_pairs(sortedKeys, '^\(.\{-}:\d\+:\d\+:\)\s*\(.*\)\s*$', 100) - - let tmp = copy(entries) - let entries = {} - let source = [] " Need a list to keep the order (dict does not guarantee it) - for key in sortedKeys - let newKey = formatedEntries[key] - let entries[newKey] = tmp[key] - call add(source, newKey) - endfor - unlet tmp - - let actions = { - \ 'ctrl-t': 'tab split', - \ 'ctrl-x': 'split', - \ 'ctrl-v': 'vsplit', - \ 'ctrl-q': function('phpactor#quickfix#vim') - \ } - - call fzf#run(fzf#wrap('find_references', fzf#vim#with_preview({ - \ 'source': source, - \ 'down': '60%', - \ '_action': actions, - \ 'sink*': function('quickfix_sink', [entries, actions]), - \ 'options': [ - \ '--exit-0', - \ '--expect='. join(keys(actions), ','), - \ '--multi', - \ '--bind=ctrl-a:select-all,ctrl-d:deselect-all', - \ '--inline-info', - \ '--header', ":: Press \x1b[35mCTRL-Q\x1b[m to open the quickfix with your selection", - \ '--delimiter=:', '--nth=1,4', - \ '--reverse' - \ ]}, 'up', '?'), 1)) -endfunction - -function! s:quickfix_sink(results, actions, lines) abort - if 2 > len(a:lines) - " Don't know how to handle this, should not append - return - endif - - let actionKey = remove(a:lines, 0) - let Action = get(a:actions, actionKey, 'e') - let items = map(copy(a:lines), {key, value -> a:results[value]}) - - if type(function('call')) == type(Action) - return Action(items) - endif - - if len(a:lines) > 1 - augroup fzf_swap - autocmd SwapExists * let v:swapchoice='o' | echohl WarningMsg - \| echom 'fzf: E325: swap file exists: '. expand('') - \| echohl None - augroup END - endif - - try - let empty = empty(expand('%')) && 1 == line('$') && empty(getline(1)) && !&modified - let autochdir = &autochdir - set noautochdir - - for item in items - let filename = fnameescape(item.filename) - let Action = empty ? 'e' : Action " Use the current buffer if empty - - execute Action '+'.item.lnum filename - execute 'normal!' item.col .'|' - normal! zz - - if empty - let empty = v:false - endif - - if !has('patch-8.0.0177') && !has('nvim-0.2') && exists('#BufEnter') - \ && isdirectory(item.filename) - doautocmd BufEnter - endif - endfor - catch /^Vim:Interrupt$/ - finally - let &autochdir = autochdir - silent! autocmd! fzf_swap - endtry -endfunction - -function! s:align_pairs(list, regexp, ...) abort - let maxlen = 0 - let pairs = {} - for elem in a:list - let match = matchlist(elem, a:regexp) - let [filename, text] = match[1:2] - let maxlen = max([maxlen, len(filename)]) - let pairs[elem] = [filename, text] - endfor - - let args = copy(a:000) - let max = 60 - if 0 < len(args) && type(v:t_number) == type(args[0]) - let max = remove(args, 0) - endif - - let maxlen = min([maxlen, max]) - - return map(pairs, "printf('%-'.maxlen.'s', v:val[0]).' '.v:val[1]") -endfunction - -function! s:relative_path(absolute_path) - let l:cwd = getcwd() - - return substitute(a:absolute_path, l:cwd .'/', '', '') -endfunction - -" vim: et ts=4 sw=4 fdm=marker diff --git a/bin/phpactor b/bin/phpactor deleted file mode 100755 index 1697fb3ca7..0000000000 --- a/bin/phpactor +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env php -run(null, $output); -} catch (Exception $e) { - $application->renderThrowable($e, $output); - exit(255); -} diff --git a/box.json b/box.json deleted file mode 100644 index 2781c8aa7a..0000000000 --- a/box.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compression": "GZ", - "directories": [ - "templates" - ], - "files": [ - "LICENSE" - ], - "finder": [ - { - "exclude": [ - "Tests" - ], - "in": "lib" - }, - { - "name": "{\\.(php|bash|fish|zsh)}", - "exclude": [ - "tests", - "test" - ], - "in": "vendor" - } - ], - "git-tag": "git_tag", - "intercept": true, - "output": "build/phpactor.phar" -} diff --git a/composer.json b/composer.json deleted file mode 100644 index 8dd4923ca0..0000000000 --- a/composer.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "name": "phpactor/phpactor", - "description": "PHP refactoring and intellisense tool for text editors", - "license": "MIT", - "require": { - "php": "^8.2", - "ext-mbstring": "*", - "ext-posix": "*", - "ext-tokenizer": "*", - "composer/xdebug-handler": "^3.0", - "symfony/yaml": "^5.1", - "phpactor/container": "^3.0", - "phpactor/class-to-file": "~0.5", - "twig/twig": "^3.4", - "dnoegel/php-xdg-base-dir": "^0.1.0", - "symfony/console": "^6.0", - "dantleech/invoke": "^2.0", - "phpactor/amp-fswatch": "^0.3.0", - "amphp/process": "^1.1.5", - "phpactor/phly-event-dispatcher": "^2.2.0", - "phpactor/language-server": "^7.0.1", - "phpactor/language-server-protocol": "^3.17.4", - "dantleech/object-renderer": "^0.1.1", - "monolog/monolog": "^2.10", - "sebastian/diff": "^5.0", - "webmozart/glob": "^4.4", - "symfony/filesystem": "^6.0", - "symfony/process": "^6.0", - "jetbrains/phpstorm-stubs": "dev-master", - "phpactor/tolerant-php-parser": "dev-phan-phactor-fixes", - "phpactor/map-resolver": "^1.7.0", - "webmozart/assert": "^1.11", - "composer/semver": "^3.4", - "myclabs/deep-copy": "^1.13" - }, - "require-dev": { - "blackfire/php-sdk": "^1.31", - "dantleech/what-changed": "~0.4", - "dms/phpunit-arraysubset-asserts": "dev-master", - "friendsofphp/php-cs-fixer": "^3.32", - "guzzlehttp/psr7": "^2.8", - "jangregor/phpstan-prophecy": "^2", - "open-telemetry/exporter-otlp": "^1.3", - "open-telemetry/sdk": "^1.7", - "php-http/guzzle7-adapter": "^1.1", - "phpactor/test-utils": "^2.0.0", - "phpbench/phpbench": "^1.6.0", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^2", - "phpstan/phpstan-phpunit": "^2", - "phpunit/phpunit": "^10.0", - "psalm/phar": "6.14.1", - "psr/log": "^1.1", - "rector/rector": "^2", - "squizlabs/php_codesniffer": "^3.7", - "symfony/var-dumper": "^6.4" - }, - "replace": { - "phpactor/class-mover": "0.2.0", - "phpactor/class-to-file-extension": "0.2.2", - "phpactor/code-builder": "0.4.3", - "phpactor/code-transform": "0.4.3", - "phpactor/code-transform-extension": "0.2.2", - "phpactor/completion": "*", - "phpactor/completion-extension": "0.2.5", - "phpactor/completion-rpc-extension": "0.2.3", - "phpactor/completion-worse-extension": "0.2.4", - "phpactor/composer-autoloader-extension": "0.2.3", - "phpactor/config-loader": "0.1.2", - "phpactor/console-extension": "0.1.6", - "phpactor/debug-extension": "*", - "phpactor/file-path-resolver": "0.8.3", - "phpactor/file-path-resolver-extension": "0.3.4", - "phpactor/indexer-extension": "0.3.3", - "phpactor/language-server-extension": "0.6.4", - "phpactor/language-server-phpactor-extensions": "0.5.3", - "phpactor/logging-extension": "0.3.4", - "phpactor/name": "0.1.1", - "phpactor/path-finder": "0.1.2", - "phpactor/php-extension": "0.1.1", - "phpactor/reference-finder": "0.1.6", - "phpactor/reference-finder-extension": "0.1.7", - "phpactor/reference-finder-rpc-extension": "0.1.5", - "phpactor/rpc-extension": "0.2.4", - "phpactor/source-code-filesystem": "0.1.8", - "phpactor/source-code-filesystem-extension": "0.1.5", - "phpactor/worse-reference-finder-extension": "0.1.6", - "phpactor/worse-reference-finder": "0.2.6", - "phpactor/worse-reflection-extension": "0.2.5" - }, - "config": { - "platform": { - "php": "8.2.0" - }, - "allow-plugins": { - "dantleech/what-changed": true, - "phpstan/extension-installer": true, - "php-http/discovery": false, - "tbachert/spi": false - }, - "preferred-install": { - "phan/tolerant-php-parser": "source" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\": "lib/" - } - }, - "autoload-dev": { - "psr-4": { - "Phpactor\\Tests\\": "tests/" - }, - "files": [ - "lib/Extension/Debug/bootstrap.php" - ] - }, - "minimum-stability": "dev", - "prefer-stable": true, - "bin": [ - "bin/phpactor" - ], - "scripts": { - "post-install-cmd": [ - "@php bin/phpactor config:json-schema phpactor.schema.json" - ], - "integrate": [ - "@composer validate --strict", - "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix", - "@php vendor/bin/phpstan analyse --memory-limit=-1", - "@php vendor/bin/phpunit", - "@php vendor/bin/phpbench run --iterations=1 --revs=1", - "make docs" - ] - } -} diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 731d52bae9..0000000000 --- a/composer.lock +++ /dev/null @@ -1,9517 +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": "ce37ef692b689fcf157fc2e665060a14", - "packages": [ - { - "name": "amphp/amp", - "version": "v2.6.5", - "source": { - "type": "git", - "url": "https://github.com/amphp/amp.git", - "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", - "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^7 | ^8 | ^9", - "react/promise": "^2", - "vimeo/psalm": "^3.12" - }, - "type": "library", - "autoload": { - "files": [ - "lib/functions.php", - "lib/Internal/functions.php" - ], - "psr-4": { - "Amp\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A non-blocking concurrency framework for PHP applications.", - "homepage": "https://amphp.org/amp", - "keywords": [ - "async", - "asynchronous", - "awaitable", - "concurrency", - "event", - "event-loop", - "future", - "non-blocking", - "promise" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.5" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2025-09-03T19:41:28+00:00" - }, - { - "name": "amphp/byte-stream", - "version": "v1.8.2", - "source": { - "type": "git", - "url": "https://github.com/amphp/byte-stream.git", - "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", - "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.4", - "friendsofphp/php-cs-fixer": "^2.3", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^6 || ^7 || ^8", - "psalm/phar": "^3.11.4" - }, - "type": "library", - "autoload": { - "files": [ - "lib/functions.php" - ], - "psr-4": { - "Amp\\ByteStream\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "https://amphp.org/byte-stream", - "keywords": [ - "amp", - "amphp", - "async", - "io", - "non-blocking", - "stream" - ], - "support": { - "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-04-13T18:00:56+00:00" - }, - { - "name": "amphp/cache", - "version": "v1.5.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/cache.git", - "reference": "fe78cfae2fb8c92735629b8cd1893029c73c9b63" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/cache/zipball/fe78cfae2fb8c92735629b8cd1893029c73c9b63", - "reference": "fe78cfae2fb8c92735629b8cd1893029c73c9b63", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "amphp/serialization": "^1", - "amphp/sync": "^1.2", - "php": ">=7.1" - }, - "conflict": { - "amphp/file": "<0.2 || >=3" - }, - "require-dev": { - "amphp/file": "^1 || ^2", - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.1", - "phpunit/phpunit": "^6 | ^7 | ^8 | ^9", - "vimeo/psalm": "^4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Amp\\Cache\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - }, - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - } - ], - "description": "A promise-aware caching API for Amp.", - "homepage": "https://github.com/amphp/cache", - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/cache/issues", - "source": "https://github.com/amphp/cache/tree/v1.5.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-03-21T19:35:02+00:00" - }, - { - "name": "amphp/dns", - "version": "v1.2.4", - "source": { - "type": "git", - "url": "https://github.com/amphp/dns.git", - "reference": "4a13ffdc5e088593eb01860fc5002ebd9316d562" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/dns/zipball/4a13ffdc5e088593eb01860fc5002ebd9316d562", - "reference": "4a13ffdc5e088593eb01860fc5002ebd9316d562", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "amphp/byte-stream": "^1.1", - "amphp/cache": "^1.2", - "amphp/parser": "^1", - "amphp/windows-registry": "^0.3", - "daverandom/libdns": "^2.0.1", - "ext-filter": "*", - "ext-json": "*", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "phpunit/phpunit": "^6 || ^7 || ^8 || ^9" - }, - "type": "library", - "autoload": { - "files": [ - "lib/functions.php" - ], - "psr-4": { - "Amp\\Dns\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Wright", - "email": "addr@daverandom.com" - }, - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - } - ], - "description": "Async DNS resolution for Amp.", - "homepage": "https://github.com/amphp/dns", - "keywords": [ - "amp", - "amphp", - "async", - "client", - "dns", - "resolve" - ], - "support": { - "issues": "https://github.com/amphp/dns/issues", - "source": "https://github.com/amphp/dns/tree/v1.2.4" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-12-08T15:06:32+00:00" - }, - { - "name": "amphp/parser", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/parser.git", - "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", - "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "phpunit/phpunit": "^9", - "psalm/phar": "^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Amp\\Parser\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A generator parser to make streaming parsers simple.", - "homepage": "https://github.com/amphp/parser", - "keywords": [ - "async", - "non-blocking", - "parser", - "stream" - ], - "support": { - "issues": "https://github.com/amphp/parser/issues", - "source": "https://github.com/amphp/parser/tree/v1.1.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-03-21T19:16:53+00:00" - }, - { - "name": "amphp/process", - "version": "v1.1.9", - "source": { - "type": "git", - "url": "https://github.com/amphp/process.git", - "reference": "55b837d4f1857b9bd7efb7bb859ae6b0e804f13f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/process/zipball/55b837d4f1857b9bd7efb7bb859ae6b0e804f13f", - "reference": "55b837d4f1857b9bd7efb7bb859ae6b0e804f13f", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "amphp/byte-stream": "^1.4", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "phpunit/phpunit": "^6" - }, - "type": "library", - "autoload": { - "files": [ - "lib/functions.php" - ], - "psr-4": { - "Amp\\Process\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Asynchronous process manager.", - "homepage": "https://github.com/amphp/process", - "support": { - "issues": "https://github.com/amphp/process/issues", - "source": "https://github.com/amphp/process/tree/v1.1.9" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-12-13T17:38:25+00:00" - }, - { - "name": "amphp/serialization", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/amphp/serialization.git", - "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", - "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "phpunit/phpunit": "^9 || ^8 || ^7" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Amp\\Serialization\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Serialization tools for IPC and data storage in PHP.", - "homepage": "https://github.com/amphp/serialization", - "keywords": [ - "async", - "asynchronous", - "serialization", - "serialize" - ], - "support": { - "issues": "https://github.com/amphp/serialization/issues", - "source": "https://github.com/amphp/serialization/tree/master" - }, - "time": "2020-03-25T21:39:07+00:00" - }, - { - "name": "amphp/socket", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/socket.git", - "reference": "b00528bd75548b7ae06a502358bb3ff8b106f5ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/socket/zipball/b00528bd75548b7ae06a502358bb3ff8b106f5ab", - "reference": "b00528bd75548b7ae06a502358bb3ff8b106f5ab", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "amphp/byte-stream": "^1.6", - "amphp/dns": "^1 || ^0.9", - "ext-openssl": "*", - "kelunik/certificate": "^1.1", - "league/uri-parser": "^1.4", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "phpunit/phpunit": "^6 || ^7 || ^8", - "vimeo/psalm": "^3.9@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php", - "src/Internal/functions.php" - ], - "psr-4": { - "Amp\\Socket\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@gmail.com" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Async socket connection / server tools for Amp.", - "homepage": "https://github.com/amphp/socket", - "keywords": [ - "amp", - "async", - "encryption", - "non-blocking", - "sockets", - "tcp", - "tls" - ], - "support": { - "issues": "https://github.com/amphp/socket/issues", - "source": "https://github.com/amphp/socket/tree/v1.2.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-03-21T18:12:22+00:00" - }, - { - "name": "amphp/sync", - "version": "v1.4.2", - "source": { - "type": "git", - "url": "https://github.com/amphp/sync.git", - "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/sync/zipball/85ab06764f4f36d63b1356b466df6111cf4b89cf", - "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf", - "shasum": "" - }, - "require": { - "amphp/amp": "^2.2", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.1", - "phpunit/phpunit": "^9 || ^8 || ^7" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php", - "src/ConcurrentIterator/functions.php" - ], - "psr-4": { - "Amp\\Sync\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Stephen Coakley", - "email": "me@stephencoakley.com" - } - ], - "description": "Mutex, Semaphore, and other synchronization tools for Amp.", - "homepage": "https://github.com/amphp/sync", - "keywords": [ - "async", - "asynchronous", - "mutex", - "semaphore", - "synchronization" - ], - "support": { - "issues": "https://github.com/amphp/sync/issues", - "source": "https://github.com/amphp/sync/tree/v1.4.2" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2021-10-25T18:29:10+00:00" - }, - { - "name": "amphp/windows-registry", - "version": "v0.3.3", - "source": { - "type": "git", - "url": "https://github.com/amphp/windows-registry.git", - "reference": "0f56438b9197e224325e88f305346f0221df1f71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/windows-registry/zipball/0f56438b9197e224325e88f305346f0221df1f71", - "reference": "0f56438b9197e224325e88f305346f0221df1f71", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "amphp/byte-stream": "^1.4", - "amphp/process": "^1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master" - }, - "type": "library", - "autoload": { - "psr-4": { - "Amp\\WindowsRegistry\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Windows Registry Reader.", - "support": { - "issues": "https://github.com/amphp/windows-registry/issues", - "source": "https://github.com/amphp/windows-registry/tree/master" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2020-07-10T16:13:29+00:00" - }, - { - "name": "brick/math", - "version": "0.13.1", - "source": { - "type": "git", - "url": "https://github.com/brick/math.git", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Brick\\Math\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Arbitrary-precision arithmetic library", - "keywords": [ - "Arbitrary-precision", - "BigInteger", - "BigRational", - "arithmetic", - "bigdecimal", - "bignum", - "bignumber", - "brick", - "decimal", - "integer", - "math", - "mathematics", - "rational" - ], - "support": { - "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.13.1" - }, - "funding": [ - { - "url": "https://github.com/BenMorel", - "type": "github" - } - ], - "time": "2025-03-29T13:50:30+00:00" - }, - { - "name": "composer/pcre", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<1.11.10" - }, - "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" - }, - "type": "library", - "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] - }, - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" - }, - "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": "2024-11-12T16:29:46+00:00" - }, - { - "name": "composer/semver", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.4" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - } - ], - "time": "2025-08-20T19:15:30+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "3.0.5", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", - "shasum": "" - }, - "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" - }, - "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": "2024-05-06T16:37:16+00:00" - }, - { - "name": "dantleech/argument-resolver", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://gitlab.com/dantleech/argument-resolver.git", - "reference": "e34fabf7d6e53e5194f745ad069c4a87cc4b34cc" - }, - "dist": { - "type": "zip", - "url": "https://gitlab.com/api/v4/projects/dantleech%2Fargument-resolver/repository/archive.zip?sha=e34fabf7d6e53e5194f745ad069c4a87cc4b34cc", - "reference": "e34fabf7d6e53e5194f745ad069c4a87cc4b34cc", - "shasum": "" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.16", - "phpstan/phpstan": "^0.10.1", - "phpunit/phpunit": "^7.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "DTL\\ArgumentResolver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Resolve method arguments from an associative array", - "support": { - "issues": "https://gitlab.com/api/v4/projects/7322320/issues" - }, - "time": "2020-04-09T09:32:31+00:00" - }, - { - "name": "dantleech/invoke", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/dantleech/invoke.git", - "reference": "9b002d746d2c1b86cfa63a47bb5909cee58ef50c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dantleech/invoke/zipball/9b002d746d2c1b86cfa63a47bb5909cee58ef50c", - "reference": "9b002d746d2c1b86cfa63a47bb5909cee58ef50c", - "shasum": "" - }, - "require": { - "php": "^7.2||^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.13", - "phpbench/phpbench": "^1.0", - "phpstan/phpstan": "^0.12.0", - "phpunit/phpunit": "^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "psr-4": { - "DTL\\Invoke\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "daniel leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Emulate named parameters", - "support": { - "issues": "https://github.com/dantleech/invoke/issues", - "source": "https://github.com/dantleech/invoke/tree/2.0.0" - }, - "time": "2021-05-01T17:22:58+00:00" - }, - { - "name": "dantleech/object-renderer", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/dantleech/object-renderer.git", - "reference": "942ad54a22e5ffb9ac3421d7bb06fa76bc45ad30" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dantleech/object-renderer/zipball/942ad54a22e5ffb9ac3421d7bb06fa76bc45ad30", - "reference": "942ad54a22e5ffb9ac3421d7bb06fa76bc45ad30", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8.0", - "psr/container": "^1.0@dev", - "twig/twig": "^2.0||^3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.15.0", - "phpactor/test-utils": "^1.1", - "phpstan/phpstan": "^0.12.0", - "phpunit/phpunit": "^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\ObjectRenderer\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Render/pretty-print objects", - "support": { - "issues": "https://github.com/dantleech/object-renderer/issues", - "source": "https://github.com/dantleech/object-renderer/tree/0.1.1" - }, - "time": "2021-01-31T18:57:08+00:00" - }, - { - "name": "daverandom/libdns", - "version": "v2.1.0", - "source": { - "type": "git", - "url": "https://github.com/DaveRandom/LibDNS.git", - "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", - "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "Required for IDN support" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "LibDNS\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "DNS protocol implementation written in pure PHP", - "keywords": [ - "dns" - ], - "support": { - "issues": "https://github.com/DaveRandom/LibDNS/issues", - "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" - }, - "time": "2024-04-12T12:12:48+00:00" - }, - { - "name": "dnoegel/php-xdg-base-dir", - "version": "v0.1.1", - "source": { - "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "implementation of xdg base directory specification for php", - "support": { - "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", - "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" - }, - "time": "2019-12-04T15:06:13+00:00" - }, - { - "name": "jetbrains/phpstorm-stubs", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/JetBrains/phpstorm-stubs", - "reference": "4a38b62928bb95c29d3cd866ffc188c07d035e0a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/4a38b62928bb95c29d3cd866ffc188c07d035e0a", - "reference": "4a38b62928bb95c29d3cd866ffc188c07d035e0a", - "shasum": "" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^v3.86", - "nikic/php-parser": "^v5.6", - "phpdocumentor/reflection-docblock": "^5.6", - "phpunit/phpunit": "^12.3" - }, - "default-branch": true, - "type": "library", - "autoload": { - "files": [ - "PhpStormStubsMap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "PHP runtime & extensions header files for PhpStorm", - "homepage": "https://www.jetbrains.com/phpstorm", - "keywords": [ - "autocomplete", - "code", - "inference", - "inspection", - "jetbrains", - "phpstorm", - "stubs", - "type" - ], - "support": { - "source": "https://github.com/JetBrains/phpstorm-stubs/tree/master" - }, - "time": "2025-11-11T19:21:31+00:00" - }, - { - "name": "kelunik/certificate", - "version": "v1.1.3", - "source": { - "type": "git", - "url": "https://github.com/kelunik/certificate.git", - "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", - "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "php": ">=7.0" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "phpunit/phpunit": "^6 | 7 | ^8 | ^9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Kelunik\\Certificate\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Access certificate details and transform between different formats.", - "keywords": [ - "DER", - "certificate", - "certificates", - "openssl", - "pem", - "x509" - ], - "support": { - "issues": "https://github.com/kelunik/certificate/issues", - "source": "https://github.com/kelunik/certificate/tree/v1.1.3" - }, - "time": "2023-02-03T21:26:53+00:00" - }, - { - "name": "league/uri-parser", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/uri-parser.git", - "reference": "671548427e4c932352d9b9279fdfa345bf63fa00" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-parser/zipball/671548427e4c932352d9b9279fdfa345bf63fa00", - "reference": "671548427e4c932352d9b9279fdfa345bf63fa00", - "shasum": "" - }, - "require": { - "php": ">=7.0.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.0", - "phpstan/phpstan": "^0.9.2", - "phpstan/phpstan-phpunit": "^0.9.4", - "phpstan/phpstan-strict-rules": "^0.9.0", - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-intl": "Allow parsing RFC3987 compliant hosts", - "league/uri-schemes": "Allow validating and normalizing URI parsing results" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "League\\Uri\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://nyamsprod.com" - } - ], - "description": "userland URI parser RFC 3986 compliant", - "homepage": "https://github.com/thephpleague/uri-parser", - "keywords": [ - "parse_url", - "parser", - "rfc3986", - "rfc3987", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/thephpleague/uri-parser/issues", - "source": "https://github.com/thephpleague/uri-parser/tree/master" - }, - "abandoned": "league/uri-interfaces", - "time": "2018-11-22T07:55:51+00:00" - }, - { - "name": "monolog/monolog", - "version": "2.11.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "37308608e599f34a1a4845b16440047ec98a172a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/37308608e599f34a1a4845b16440047ec98a172a", - "reference": "37308608e599f34a1a4845b16440047ec98a172a", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" - }, - "provide": { - "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7 || ^8", - "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2@dev", - "guzzlehttp/guzzle": "^7.4", - "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8 || ^2.0", - "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpspec/prophecy": "^1.15", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.5.38 || ^9.6.19", - "predis/predis": "^1.1 || ^2.0", - "rollbar/rollbar": "^1.3 || ^2 || ^3", - "ruflin/elastica": "^7", - "swiftmailer/swiftmailer": "^5.3|^6.0", - "symfony/mailer": "^5.4 || ^6", - "symfony/mime": "^5.4 || ^6" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", - "ext-mbstring": "Allow to work properly with unicode symbols", - "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", - "ext-openssl": "Required to send log messages using SSL", - "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "https://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "support": { - "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.11.0" - }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2026-01-01T13:05:00+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.13.4", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "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", - "phpspec/prophecy": "^1.10", - "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.13.4" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2025-08-01T08:46:24+00:00" - }, - { - "name": "phpactor/amp-fswatch", - "version": "0.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpactor/amp-fswatch.git", - "reference": "8b79e76b451d40a3367aae460883d90455dbeb1a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/amp-fswatch/zipball/8b79e76b451d40a3367aae460883d90455dbeb1a", - "reference": "8b79e76b451d40a3367aae460883d90455dbeb1a", - "shasum": "" - }, - "require": { - "amphp/amp": "^2.4", - "amphp/process": "^1.1", - "php": "^8.0", - "psr/log": "^1.1", - "symfony/filesystem": "^5.0|^6.0", - "webmozart/glob": "^4.4" - }, - "require-dev": { - "amphp/phpunit-util": "^1.3", - "ergebnis/composer-normalize": "^2.0", - "friendsofphp/php-cs-fixer": "^3.15", - "jangregor/phpstan-prophecy": "^1.0", - "phpactor/test-utils": "~1.1.3", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^1.1", - "phpunit/phpunit": "^9.0", - "symfony/var-dumper": "^5.0|^6.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\AmpFsWatch\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Async Filesystem Watcher for Amphp", - "support": { - "issues": "https://github.com/phpactor/amp-fswatch/issues", - "source": "https://github.com/phpactor/amp-fswatch/tree/0.3.0" - }, - "time": "2023-08-12T15:51:57+00:00" - }, - { - "name": "phpactor/class-to-file", - "version": "0.6.0", - "source": { - "type": "git", - "url": "https://github.com/phpactor/class-to-file.git", - "reference": "2fcf99a39f830b0d57c374bacaa9471e5c0483e3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/class-to-file/zipball/2fcf99a39f830b0d57c374bacaa9471e5c0483e3", - "reference": "2fcf99a39f830b0d57c374bacaa9471e5c0483e3", - "shasum": "" - }, - "require": { - "php": "^8.1", - "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/filesystem": "^4.2 || ^5.0 || ^6.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.0", - "friendsofphp/php-cs-fixer": "^3.0", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^9.0", - "symfony/var-dumper": "^6.0 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.4.x-dev" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\ClassFileConverter\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Library to covert class names to file paths and vice-versa", - "support": { - "source": "https://github.com/phpactor/class-to-file/tree/0.6.0" - }, - "time": "2025-10-02T14:48:57+00:00" - }, - { - "name": "phpactor/container", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpactor/container.git", - "reference": "143bbd987da798f2d7e5cedafd35b49d5051e167" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/container/zipball/143bbd987da798f2d7e5cedafd35b49d5051e167", - "reference": "143bbd987da798f2d7e5cedafd35b49d5051e167", - "shasum": "" - }, - "require": { - "php": "^8.1", - "phpactor/map-resolver": "^1.4", - "psr/container": "^1.0||^2.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.0", - "friendsofphp/php-cs-fixer": "^3.0", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\Container\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Phpactor's DI Container", - "support": { - "issues": "https://github.com/phpactor/container/issues", - "source": "https://github.com/phpactor/container/tree/3.0.1" - }, - "time": "2024-11-16T22:20:43+00:00" - }, - { - "name": "phpactor/language-server", - "version": "7.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpactor/language-server.git", - "reference": "e4934195cc1857ec3347a488f3357b0f0df2d2bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/language-server/zipball/e4934195cc1857ec3347a488f3357b0f0df2d2bf", - "reference": "e4934195cc1857ec3347a488f3357b0f0df2d2bf", - "shasum": "" - }, - "require": { - "amphp/socket": "^1.1", - "dantleech/argument-resolver": "^1.1", - "dantleech/invoke": "^2.0", - "php": "^8.1", - "phpactor/language-server-protocol": "^3.17", - "psr/event-dispatcher": "^1.0", - "psr/log": "^1.0", - "ramsey/uuid": "^4.0" - }, - "require-dev": { - "amphp/phpunit-util": "^1.3", - "ergebnis/composer-normalize": "^2.0", - "friendsofphp/php-cs-fixer": "^3.0", - "jangregor/phpstan-prophecy": "^1.0", - "phpactor/phly-event-dispatcher": "~2.0.0", - "phpactor/test-utils": "~1.1.3", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^9.0", - "symfony/var-dumper": "^5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\LanguageServer\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Generic Language Server Platform", - "support": { - "issues": "https://github.com/phpactor/language-server/issues", - "source": "https://github.com/phpactor/language-server/tree/7.0.1" - }, - "time": "2025-01-26T23:40:29+00:00" - }, - { - "name": "phpactor/language-server-protocol", - "version": "3.17.4", - "source": { - "type": "git", - "url": "https://github.com/phpactor/language-server-protocol.git", - "reference": "1f7d7218db2cc77223bbb4e03961d0bceea50e60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/language-server-protocol/zipball/1f7d7218db2cc77223bbb4e03961d0bceea50e60", - "reference": "1f7d7218db2cc77223bbb4e03961d0bceea50e60", - "shasum": "" - }, - "require": { - "dantleech/invoke": "^2.0", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.0", - "friendsofphp/php-cs-fixer": "^2.17", - "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^9.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\LanguageServerProtocol\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Langauge Server Protocol for PHP (transpiled)", - "support": { - "issues": "https://github.com/phpactor/language-server-protocol/issues", - "source": "https://github.com/phpactor/language-server-protocol/tree/3.17.4" - }, - "time": "2024-11-21T20:07:12+00:00" - }, - { - "name": "phpactor/map-resolver", - "version": "1.7.0", - "source": { - "type": "git", - "url": "https://github.com/phpactor/map-resolver.git", - "reference": "a1ed625b9aa93ca9520f85a0d54e9359e70d7276" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/map-resolver/zipball/a1ed625b9aa93ca9520f85a0d54e9359e70d7276", - "reference": "a1ed625b9aa93ca9520f85a0d54e9359e70d7276", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.0", - "friendsofphp/php-cs-fixer": "^3.91", - "infection/infection": "^0.29.0", - "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^10.0", - "symfony/var-dumper": "^6.0|^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\MapResolver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Map Resolver", - "support": { - "issues": "https://github.com/phpactor/map-resolver/issues", - "source": "https://github.com/phpactor/map-resolver/tree/1.7.0" - }, - "time": "2025-11-30T19:17:10+00:00" - }, - { - "name": "phpactor/phly-event-dispatcher", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpactor/phly-event-dispatcher.git", - "reference": "9817636e907075494e6a95479552cbe9a301ca6d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/phly-event-dispatcher/zipball/9817636e907075494e6a95479552cbe9a301ca6d", - "reference": "9817636e907075494e6a95479552cbe9a301ca6d", - "shasum": "" - }, - "require": { - "php": "^8.1", - "psr/container": "^1.0||^2.0", - "psr/event-dispatcher": "^1.0" - }, - "conflict": { - "phpspec/prophecy": "<1.7.5" - }, - "provide": { - "psr/event-dispatcher-implementation": "^1.0" - }, - "require-dev": { - "fig/event-dispatcher-util": "^1.0", - "friendsofphp/php-cs-fixer": "^2.17", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^9.0.0", - "zendframework/zend-coding-standard": "~1.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions/lazy_listener.php" - ], - "psr-4": { - "Phly\\EventDispatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Experimental event dispatcher for PSR-14", - "keywords": [ - "components", - "event-dispatcher", - "psr-14" - ], - "support": { - "issues": "https://github.com/phly/phly-event-dispatcher/issues", - "rss": "https://github.com/phly/phly-event-dispatcher/releases.atom", - "source": "https://github.com/phly/phly-event-dispatcher" - }, - "time": "2025-01-26T23:32:28+00:00" - }, - { - "name": "phpactor/tolerant-php-parser", - "version": "dev-phan-phactor-fixes", - "source": { - "type": "git", - "url": "https://github.com/phpactor/tolerant-php-parser.git", - "reference": "960913636ee651899dc39a6968bc7425f6a32660" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/tolerant-php-parser/zipball/960913636ee651899dc39a6968bc7425f6a32660", - "reference": "960913636ee651899dc39a6968bc7425f6a32660", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Microsoft\\PhpParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Rob Lourens", - "email": "roblou@microsoft.com" - } - ], - "description": "Tolerant PHP-to-AST parser designed for IDE usage scenarios", - "support": { - "source": "https://github.com/phpactor/tolerant-php-parser/tree/phan-phactor-fixes" - }, - "time": "2025-10-29T17:55:10+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": "psr/event-dispatcher", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Standard interfaces for event handling.", - "keywords": [ - "events", - "psr", - "psr-14" - ], - "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" - }, - "time": "2019-01-08T18:20:26+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": "ramsey/collection", - "version": "2.1.1", - "source": { - "type": "git", - "url": "https://github.com/ramsey/collection.git", - "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", - "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.45", - "fakerphp/faker": "^1.24", - "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^2.1", - "mockery/mockery": "^1.6", - "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.4", - "phpspec/prophecy-phpunit": "^2.3", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-mockery": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.5", - "ramsey/coding-standard": "^2.3", - "ramsey/conventional-commits": "^1.6", - "roave/security-advisories": "dev-latest" - }, - "type": "library", - "extra": { - "captainhook": { - "force-install": true - }, - "ramsey/conventional-commits": { - "configFile": "conventional-commits.json" - } - }, - "autoload": { - "psr-4": { - "Ramsey\\Collection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } - ], - "description": "A PHP library for representing and manipulating collections.", - "keywords": [ - "array", - "collection", - "hash", - "map", - "queue", - "set" - ], - "support": { - "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.1.1" - }, - "time": "2025-03-22T05:38:12+00:00" - }, - { - "name": "ramsey/uuid", - "version": "4.9.1", - "source": { - "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", - "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", - "shasum": "" - }, - "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", - "php": "^8.0", - "ramsey/collection": "^1.2 || ^2.0" - }, - "replace": { - "rhumsaa/uuid": "self.version" - }, - "require-dev": { - "captainhook/captainhook": "^5.25", - "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "ergebnis/composer-normalize": "^2.47", - "mockery/mockery": "^1.6", - "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.6", - "php-mock/php-mock-mockery": "^1.5", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpbench/phpbench": "^1.2.14", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-mockery": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.6", - "slevomat/coding-standard": "^8.18", - "squizlabs/php_codesniffer": "^3.13" - }, - "suggest": { - "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", - "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", - "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." - }, - "type": "library", - "extra": { - "captainhook": { - "force-install": true - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Ramsey\\Uuid\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", - "keywords": [ - "guid", - "identifier", - "uuid" - ], - "support": { - "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.1" - }, - "time": "2025-09-04T20:59:21+00:00" - }, - { - "name": "sebastian/diff", - "version": "5.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-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", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-02T07:15:17+00:00" - }, - { - "name": "symfony/console", - "version": "v6.4.36", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9f481cfb580db8bcecc9b2d4c63f3e13df022ad5", - "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" - }, - "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/v6.4.36" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-27T15:30:51+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "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/v3.6.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": "2024-09-25T14:21:43+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v6.4.34", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "01ffe0411b842f93c571e5c391f289c3fdd498c3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/01ffe0411b842f93c571e5c391f289c3fdd498c3", - "reference": "01ffe0411b842f93c571e5c391f289c3fdd498c3", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" - }, - "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "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": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.34" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-02-24T17:51:06+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.36.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "141046a8f9477948ff284fa65be2095baafb94f2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", - "reference": "141046a8f9477948ff284fa65be2095baafb94f2", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "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.36.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-04-10T16:19:22+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.36.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/ad1b7b9092976d6c948b8a187cec9faaea9ec1df", - "reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "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.36.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-04-10T16:19:22+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.36.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "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.36.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.36.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", - "shasum": "" - }, - "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "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.36.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-04-10T17:25:58+00:00" - }, - { - "name": "symfony/process", - "version": "v6.4.33", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "c46e854e79b52d07666e43924a20cb6dc546644e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c46e854e79b52d07666e43924a20cb6dc546644e", - "reference": "c46e854e79b52d07666e43924a20cb6dc546644e", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "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/v6.4.33" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-23T16:02:12+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "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/v3.6.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-15T11:30:57+00:00" - }, - { - "name": "symfony/string", - "version": "v7.4.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "114ac57257d75df748eda23dd003878080b8e688" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/114ac57257d75df748eda23dd003878080b8e688", - "reference": "114ac57257d75df748eda23dd003878080b8e688", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.33", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.1|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "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/v7.4.8" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-24T13:12:05+00:00" - }, - { - "name": "symfony/yaml", - "version": "v5.4.45", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "a454d47278cc16a5db371fe73ae66a78a633371e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a454d47278cc16a5db371fe73ae66a78a633371e", - "reference": "a454d47278cc16a5db371fe73ae66a78a633371e", - "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.45" - }, - "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": "2024-09-25T14:11:13+00:00" - }, - { - "name": "twig/twig", - "version": "v3.22.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "1de2ec1fc43ab58a4b7e80b214b96bfc895750f3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/1de2ec1fc43ab58a4b7e80b214b96bfc895750f3", - "reference": "1de2ec1fc43ab58a4b7e80b214b96bfc895750f3", - "shasum": "" - }, - "require": { - "php": ">=8.1.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "phpstan/phpstan": "^2.0", - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" - }, - "type": "library", - "autoload": { - "files": [ - "src/Resources/core.php", - "src/Resources/debug.php", - "src/Resources/escaper.php", - "src/Resources/string_loader.php" - ], - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.22.1" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2025-11-16T16:01:12+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.12.1", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-date": "*", - "ext-filter": "*", - "php": "^7.2 || ^8.0" - }, - "suggest": { - "ext-intl": "", - "ext-simplexml": "", - "ext-spl": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" - }, - "time": "2025-10-29T15:56:20+00:00" - }, - { - "name": "webmozart/glob", - "version": "4.7.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/glob.git", - "reference": "8a2842112d6916e61e0e15e316465b611f3abc17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/glob/zipball/8a2842112d6916e61e0e15e316465b611f3abc17", - "reference": "8a2842112d6916e61e0e15e316465b611f3abc17", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5", - "symfony/filesystem": "^5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Glob\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "A PHP implementation of Ant's glob.", - "support": { - "issues": "https://github.com/webmozarts/glob/issues", - "source": "https://github.com/webmozarts/glob/tree/4.7.0" - }, - "time": "2024-03-07T20:33:40+00:00" - } - ], - "packages-dev": [ - { - "name": "blackfire/php-sdk", - "version": "v1.35.0", - "source": { - "type": "git", - "url": "https://github.com/blackfireio/php-sdk.git", - "reference": "2c5950ff2ad29c2af96c69810eb304f0f350177d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/blackfireio/php-sdk/zipball/2c5950ff2ad29c2af96c69810eb304f0f350177d", - "reference": "2c5950ff2ad29c2af96c69810eb304f0f350177d", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "php": ">=5.2.0" - }, - "require-dev": { - "behat/behat": "^3.8", - "friends-of-behat/mink-browserkit-driver": "^1.4", - "friends-of-behat/mink-extension": "^2.5", - "guzzlehttp/psr7": "^1.6", - "illuminate/console": "^8.81", - "illuminate/queue": "^8.81", - "illuminate/support": "^8.81", - "laravel/octane": "^1.2", - "phpunit/phpunit": "^9.5", - "psr/http-message": "^1.0", - "symfony/browser-kit": "^5.1", - "symfony/framework-bundle": "^5.1", - "symfony/http-client": "^5.1", - "symfony/messenger": "^5.1", - "symfony/panther": "^1.0", - "symfony/phpunit-bridge": "^5.2" - }, - "suggest": { - "ext-blackfire": "The C version of the Blackfire probe", - "ext-zlib": "To push config to remote profiling targets", - "symfony/panther": "To use Symfony web test cases with Blackfire" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.36-dev" - } - }, - "autoload": { - "files": [ - "src/autostart.php" - ], - "psr-4": { - "Blackfire\\": "src/Blackfire" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Blackfire.io", - "email": "support@blackfire.io" - } - ], - "description": "Blackfire.io PHP SDK", - "keywords": [ - "performance", - "profiler", - "uprofiler", - "xhprof" - ], - "support": { - "issues": "https://github.com/blackfireio/php-sdk/issues", - "source": "https://github.com/blackfireio/php-sdk/tree/v1.35.0" - }, - "time": "2023-04-06T09:37:28+00:00" - }, - { - "name": "clue/ndjson-react", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/clue/reactphp-ndjson.git", - "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", - "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "react/stream": "^1.2" - }, - "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", - "react/event-loop": "^1.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\React\\NDJson\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", - "homepage": "https://github.com/clue/reactphp-ndjson", - "keywords": [ - "NDJSON", - "json", - "jsonlines", - "newline", - "reactphp", - "streaming" - ], - "support": { - "issues": "https://github.com/clue/reactphp-ndjson/issues", - "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2022-12-23T10:58:28+00:00" - }, - { - "name": "composer/ca-bundle", - "version": "1.5.9", - "source": { - "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "1905981ee626e6f852448b7aaa978f8666c5bc54" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/1905981ee626e6f852448b7aaa978f8666c5bc54", - "reference": "1905981ee626e6f852448b7aaa978f8666c5bc54", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8 || ^9", - "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\CaBundle\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", - "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.9" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - } - ], - "time": "2025-11-06T11:46:17+00:00" - }, - { - "name": "dantleech/what-changed", - "version": "0.4.5", - "source": { - "type": "git", - "url": "https://github.com/dantleech/what-changed.git", - "reference": "dd46d0f43809e43282c5aca8d7f68da5febb8cb1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dantleech/what-changed/zipball/dd46d0f43809e43282c5aca8d7f68da5febb8cb1", - "reference": "dd46d0f43809e43282c5aca8d7f68da5febb8cb1", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1|^2.0", - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "composer/composer": "^1.4|^2.0", - "friendsofphp/php-cs-fixer": "^2.16", - "phpactor/test-utils": "^1.0", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^9.0", - "symfony/process": "^4.2" - }, - "type": "composer-plugin", - "extra": { - "class": "DTL\\WhatChanged\\WhatChangedPlugin", - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "psr-4": { - "DTL\\WhatChanged\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Report changes in your dependencies", - "support": { - "issues": "https://github.com/dantleech/what-changed/issues", - "source": "https://github.com/dantleech/what-changed/tree/0.4.5" - }, - "time": "2025-04-03T09:29:53+00:00" - }, - { - "name": "dms/phpunit-arraysubset-asserts", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/rdohms/phpunit-arraysubset-asserts.git", - "reference": "1aa0d838475b1b3de642aa7ad022fadc1e0f5a75" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rdohms/phpunit-arraysubset-asserts/zipball/1aa0d838475b1b3de642aa7ad022fadc1e0f5a75", - "reference": "1aa0d838475b1b3de642aa7ad022fadc1e0f5a75", - "shasum": "" - }, - "require": { - "php": "^5.4 || ^7.0 || ^8.0", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0" - }, - "require-dev": { - "dms/coding-standard": "^9" - }, - "default-branch": true, - "type": "library", - "autoload": { - "files": [ - "assertarraysubset-autoload.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Rafael Dohms", - "email": "rdohms@gmail.com" - } - ], - "description": "This package provides ArraySubset and related asserts once deprecated in PHPUnit 8", - "support": { - "issues": "https://github.com/rdohms/phpunit-arraysubset-asserts/issues", - "source": "https://github.com/rdohms/phpunit-arraysubset-asserts/tree/master" - }, - "time": "2024-01-29T14:09:12+00:00" - }, - { - "name": "doctrine/annotations", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", - "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^2 || ^3", - "ext-tokenizer": "*", - "php": "^7.2 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^2.0", - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.10.28", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6.4 || ^7", - "vimeo/psalm": "^4.30 || ^5.14" - }, - "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.2" - }, - "abandoned": true, - "time": "2024-09-05T10:17:24+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.6", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", - "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "phpunit/phpunit": "<=7.5 || >=14" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^14", - "phpstan/phpstan": "1.4.10 || 2.1.30", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", - "psr/log": "^1 || ^2 || ^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.6" - }, - "time": "2026-02-07T07:09:04+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" - }, - "type": "library", - "autoload": { - "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/2.0.0" - }, - "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-12-30T00:23:10+00:00" - }, - { - "name": "doctrine/lexer", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.21" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.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%2Flexer", - "type": "tidelift" - } - ], - "time": "2024-02-05T11:56:58+00:00" - }, - { - "name": "evenement/evenement", - "version": "v3.0.2", - "source": { - "type": "git", - "url": "https://github.com/igorw/evenement.git", - "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", - "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^9 || ^6" - }, - "type": "library", - "autoload": { - "psr-4": { - "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/v3.0.2" - }, - "time": "2023-08-08T05:53:35+00:00" - }, - { - "name": "fidry/cpu-core-counter", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "fidry/makefile": "^0.2.0", - "fidry/php-cs-fixer-config": "^1.1.2", - "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-deprecation-rules": "^2.0.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^8.5.31 || ^9.5.26", - "webmozarts/strict-phpunit": "^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" - } - ], - "description": "Tiny utility to get the number of CPU cores.", - "keywords": [ - "CPU", - "core" - ], - "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" - }, - "funding": [ - { - "url": "https://github.com/theofidry", - "type": "github" - } - ], - "time": "2025-08-14T07:29:31+00:00" - }, - { - "name": "friendsofphp/php-cs-fixer", - "version": "v3.91.0", - "source": { - "type": "git", - "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "c4a25f20390337789c26b693ae46faa125040352" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/c4a25f20390337789c26b693ae46faa125040352", - "reference": "c4a25f20390337789c26b693ae46faa125040352", - "shasum": "" - }, - "require": { - "clue/ndjson-react": "^1.3", - "composer/semver": "^3.4", - "composer/xdebug-handler": "^3.0.5", - "ext-filter": "*", - "ext-hash": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "fidry/cpu-core-counter": "^1.3", - "php": "^7.4 || ^8.0", - "react/child-process": "^0.6.6", - "react/event-loop": "^1.5", - "react/socket": "^1.16", - "react/stream": "^1.4", - "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", - "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", - "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", - "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", - "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", - "symfony/polyfill-mbstring": "^1.33", - "symfony/polyfill-php80": "^1.33", - "symfony/polyfill-php81": "^1.33", - "symfony/polyfill-php84": "^1.33", - "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", - "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" - }, - "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.31.0", - "justinrainbow/json-schema": "^6.5", - "keradus/cli-executor": "^2.2", - "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.9", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", - "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0", - "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0" - }, - "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters." - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "autoload": { - "psr-4": { - "PhpCsFixer\\": "src/" - }, - "exclude-from-classmap": [ - "src/Fixer/Internal/*" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - } - ], - "description": "A tool to automatically fix PHP code style", - "keywords": [ - "Static code analysis", - "fixer", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.91.0" - }, - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], - "time": "2025-11-28T22:07:42+00:00" - }, - { - "name": "google/protobuf", - "version": "v4.33.1", - "source": { - "type": "git", - "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/0cd73ccf0cd26c3e72299cce1ea6144091a57e12", - "reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12", - "shasum": "" - }, - "require": { - "php": ">=8.1.0" - }, - "require-dev": { - "phpunit/phpunit": ">=5.0.0 <8.5.27" - }, - "suggest": { - "ext-bcmath": "Need to support JSON deserialization" - }, - "type": "library", - "autoload": { - "psr-4": { - "Google\\Protobuf\\": "src/Google/Protobuf", - "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "proto library for PHP", - "homepage": "https://developers.google.com/protocol-buffers/", - "keywords": [ - "proto" - ], - "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.1" - }, - "time": "2025-11-12T21:58:05+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.10.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^2.3", - "guzzlehttp/psr7": "^2.8", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.2", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2025-08-23T22:36:01+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "481557b130ef3790cf82b713667b43030dc9c957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", - "reference": "481557b130ef3790cf82b713667b43030dc9c957", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.3.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2025-08-22T14:34:08+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.8.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "21dc724a0583619cd1652f673303492272778051" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", - "reference": "21dc724a0583619cd1652f673303492272778051", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.8.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2025-08-23T21:21:41+00:00" - }, - { - "name": "jangregor/phpstan-prophecy", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/Jan0707/phpstan-prophecy.git", - "reference": "aebda94b6b1c39055d8f2227e879c07bac651550" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jan0707/phpstan-prophecy/zipball/aebda94b6b1c39055d8f2227e879c07bac651550", - "reference": "aebda94b6b1c39055d8f2227e879c07bac651550", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.5" - }, - "conflict": { - "phpspec/prophecy": "<1.17.0 || >=2.0.0", - "phpspec/prophecy-phpunit": "<2.3.0 || >=3.0.0", - "phpunit/phpunit": "<9.1.0 || >=13.0.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.47.0", - "ergebnis/license": "^2.6.0", - "ergebnis/php-cs-fixer-config": "^6.46.0", - "phpspec/prophecy": "^1.7.0", - "phpspec/prophecy-phpunit": "^2.3", - "phpunit/phpunit": "^9.1.0" - }, - "type": "phpstan-extension", - "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] - } - }, - "autoload": { - "psr-4": { - "JanGregor\\Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Gregor Emge-Triebel", - "email": "jan@jangregor.me" - } - ], - "description": "Provides a phpstan/phpstan extension for phpspec/prophecy", - "support": { - "issues": "https://github.com/Jan0707/phpstan-prophecy/issues", - "source": "https://github.com/Jan0707/phpstan-prophecy/tree/2.2.0" - }, - "time": "2025-05-22T08:21:52+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v5.6.2", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "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/v5.6.2" - }, - "time": "2025-10-21T19:32:17+00:00" - }, - { - "name": "nyholm/psr7-server", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/Nyholm/psr7-server.git", - "reference": "4335801d851f554ca43fa6e7d2602141538854dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7-server/zipball/4335801d851f554ca43fa6e7d2602141538854dc", - "reference": "4335801d851f554ca43fa6e7d2602141538854dc", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "require-dev": { - "nyholm/nsa": "^1.1", - "nyholm/psr7": "^1.3", - "phpunit/phpunit": "^7.0 || ^8.5 || ^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Nyholm\\Psr7Server\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - }, - { - "name": "Martijn van der Ven", - "email": "martijn@vanderven.se" - } - ], - "description": "Helper classes to handle PSR-7 server requests", - "homepage": "http://tnyholm.se", - "keywords": [ - "psr-17", - "psr-7" - ], - "support": { - "issues": "https://github.com/Nyholm/psr7-server/issues", - "source": "https://github.com/Nyholm/psr7-server/tree/1.1.0" - }, - "funding": [ - { - "url": "https://github.com/Zegnat", - "type": "github" - }, - { - "url": "https://github.com/nyholm", - "type": "github" - } - ], - "time": "2023-11-08T09:30:43+00:00" - }, - { - "name": "open-telemetry/api", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/opentelemetry-php/api.git", - "reference": "45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4", - "reference": "45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4", - "shasum": "" - }, - "require": { - "open-telemetry/context": "^1.4", - "php": "^8.1", - "psr/log": "^1.1|^2.0|^3.0", - "symfony/polyfill-php82": "^1.26" - }, - "conflict": { - "open-telemetry/sdk": "<=1.0.8" - }, - "type": "library", - "extra": { - "spi": { - "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ - "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" - ] - }, - "branch-alias": { - "dev-main": "1.8.x-dev" - } - }, - "autoload": { - "files": [ - "Trace/functions.php" - ], - "psr-4": { - "OpenTelemetry\\API\\": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "opentelemetry-php contributors", - "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" - } - ], - "description": "API for OpenTelemetry PHP.", - "keywords": [ - "Metrics", - "api", - "apm", - "logging", - "opentelemetry", - "otel", - "tracing" - ], - "support": { - "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/languages/php", - "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", - "source": "https://github.com/open-telemetry/opentelemetry-php" - }, - "time": "2025-10-19T10:49:48+00:00" - }, - { - "name": "open-telemetry/context", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/opentelemetry-php/context.git", - "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/d4c4470b541ce72000d18c339cfee633e4c8e0cf", - "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf", - "shasum": "" - }, - "require": { - "php": "^8.1", - "symfony/polyfill-php82": "^1.26" - }, - "suggest": { - "ext-ffi": "To allow context switching in Fibers" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.0.x-dev" - } - }, - "autoload": { - "files": [ - "fiber/initialize_fiber_handler.php" - ], - "psr-4": { - "OpenTelemetry\\Context\\": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "opentelemetry-php contributors", - "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" - } - ], - "description": "Context implementation for OpenTelemetry PHP.", - "keywords": [ - "Context", - "opentelemetry", - "otel" - ], - "support": { - "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/php", - "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", - "source": "https://github.com/open-telemetry/opentelemetry-php" - }, - "time": "2025-09-19T00:05:49+00:00" - }, - { - "name": "open-telemetry/exporter-otlp", - "version": "1.3.3", - "source": { - "type": "git", - "url": "https://github.com/opentelemetry-php/exporter-otlp.git", - "reference": "07b02bc71838463f6edcc78d3485c04b48fb263d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/07b02bc71838463f6edcc78d3485c04b48fb263d", - "reference": "07b02bc71838463f6edcc78d3485c04b48fb263d", - "shasum": "" - }, - "require": { - "open-telemetry/api": "^1.0", - "open-telemetry/gen-otlp-protobuf": "^1.1", - "open-telemetry/sdk": "^1.0", - "php": "^8.1", - "php-http/discovery": "^1.14" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.0.x-dev" - } - }, - "autoload": { - "files": [ - "_register.php" - ], - "psr-4": { - "OpenTelemetry\\Contrib\\Otlp\\": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "opentelemetry-php contributors", - "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" - } - ], - "description": "OTLP exporter for OpenTelemetry.", - "keywords": [ - "Metrics", - "exporter", - "gRPC", - "http", - "opentelemetry", - "otel", - "otlp", - "tracing" - ], - "support": { - "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/languages/php", - "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", - "source": "https://github.com/open-telemetry/opentelemetry-php" - }, - "time": "2025-11-13T08:04:37+00:00" - }, - { - "name": "open-telemetry/gen-otlp-protobuf", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git", - "reference": "673af5b06545b513466081884b47ef15a536edde" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/673af5b06545b513466081884b47ef15a536edde", - "reference": "673af5b06545b513466081884b47ef15a536edde", - "shasum": "" - }, - "require": { - "google/protobuf": "^3.22 || ^4.0", - "php": "^8.0" - }, - "suggest": { - "ext-protobuf": "For better performance, when dealing with the protobuf format" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Opentelemetry\\Proto\\": "Opentelemetry/Proto/", - "GPBMetadata\\Opentelemetry\\": "GPBMetadata/Opentelemetry/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "opentelemetry-php contributors", - "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" - } - ], - "description": "PHP protobuf files for communication with OpenTelemetry OTLP collectors/servers.", - "keywords": [ - "Metrics", - "apm", - "gRPC", - "logging", - "opentelemetry", - "otel", - "otlp", - "protobuf", - "tracing" - ], - "support": { - "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/php", - "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", - "source": "https://github.com/open-telemetry/opentelemetry-php" - }, - "time": "2025-09-17T23:10:12+00:00" - }, - { - "name": "open-telemetry/sdk", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99", - "reference": "3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99", - "shasum": "" - }, - "require": { - "ext-json": "*", - "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "^1.7", - "open-telemetry/context": "^1.4", - "open-telemetry/sem-conv": "^1.0", - "php": "^8.1", - "php-http/discovery": "^1.14", - "psr/http-client": "^1.0", - "psr/http-client-implementation": "^1.0", - "psr/http-factory-implementation": "^1.0", - "psr/http-message": "^1.0.1|^2.0", - "psr/log": "^1.1|^2.0|^3.0", - "ramsey/uuid": "^3.0 || ^4.0", - "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php82": "^1.26", - "tbachert/spi": "^1.0.5" - }, - "suggest": { - "ext-gmp": "To support unlimited number of synchronous metric readers", - "ext-mbstring": "To increase performance of string operations", - "open-telemetry/sdk-configuration": "File-based OpenTelemetry SDK configuration" - }, - "type": "library", - "extra": { - "spi": { - "OpenTelemetry\\API\\Configuration\\ConfigEnv\\EnvComponentLoader": [ - "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderHttpConfig", - "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig" - ], - "OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\ResolverInterface": [ - "OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\SdkConfigurationResolver" - ], - "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ - "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" - ] - }, - "branch-alias": { - "dev-main": "1.9.x-dev" - } - }, - "autoload": { - "files": [ - "Common/Util/functions.php", - "Logs/Exporter/_register.php", - "Metrics/MetricExporter/_register.php", - "Propagation/_register.php", - "Trace/SpanExporter/_register.php", - "Common/Dev/Compatibility/_load.php", - "_autoload.php" - ], - "psr-4": { - "OpenTelemetry\\SDK\\": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "opentelemetry-php contributors", - "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" - } - ], - "description": "SDK for OpenTelemetry PHP.", - "keywords": [ - "Metrics", - "apm", - "logging", - "opentelemetry", - "otel", - "sdk", - "tracing" - ], - "support": { - "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/languages/php", - "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", - "source": "https://github.com/open-telemetry/opentelemetry-php" - }, - "time": "2025-11-25T10:59:15+00:00" - }, - { - "name": "open-telemetry/sem-conv", - "version": "1.37.0", - "source": { - "type": "git", - "url": "https://github.com/opentelemetry-php/sem-conv.git", - "reference": "8da7ec497c881e39afa6657d72586e27efbd29a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/8da7ec497c881e39afa6657d72586e27efbd29a1", - "reference": "8da7ec497c881e39afa6657d72586e27efbd29a1", - "shasum": "" - }, - "require": { - "php": "^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "OpenTelemetry\\SemConv\\": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "opentelemetry-php contributors", - "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" - } - ], - "description": "Semantic conventions for OpenTelemetry PHP.", - "keywords": [ - "Metrics", - "apm", - "logging", - "opentelemetry", - "otel", - "semantic conventions", - "semconv", - "tracing" - ], - "support": { - "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/php", - "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", - "source": "https://github.com/open-telemetry/opentelemetry-php" - }, - "time": "2025-09-03T12:08:10+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "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": "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/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2024-03-03T12:33:53+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "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" - }, - { - "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/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "php-http/discovery", - "version": "1.20.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/discovery.git", - "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", - "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "nyholm/psr7": "<1.0", - "zendframework/zend-diactoros": "*" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "*", - "psr/http-factory-implementation": "*", - "psr/http-message-implementation": "*" - }, - "require-dev": { - "composer/composer": "^1.0.2|^2.0", - "graham-campbell/phpspec-skip-example-extension": "^5.0", - "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0", - "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", - "sebastian/comparator": "^3.0.5 || ^4.0.8", - "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" - }, - "type": "composer-plugin", - "extra": { - "class": "Http\\Discovery\\Composer\\Plugin", - "plugin-optional": true - }, - "autoload": { - "psr-4": { - "Http\\Discovery\\": "src/" - }, - "exclude-from-classmap": [ - "src/Composer/Plugin.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", - "homepage": "http://php-http.org", - "keywords": [ - "adapter", - "client", - "discovery", - "factory", - "http", - "message", - "psr17", - "psr7" - ], - "support": { - "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.20.0" - }, - "time": "2024-10-02T11:20:13+00:00" - }, - { - "name": "php-http/guzzle7-adapter", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/guzzle7-adapter.git", - "reference": "03a415fde709c2f25539790fecf4d9a31bc3d0eb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/guzzle7-adapter/zipball/03a415fde709c2f25539790fecf4d9a31bc3d0eb", - "reference": "03a415fde709c2f25539790fecf4d9a31bc3d0eb", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "^7.0", - "php": "^7.3 | ^8.0", - "php-http/httplug": "^2.0", - "psr/http-client": "^1.0" - }, - "provide": { - "php-http/async-client-implementation": "1.0", - "php-http/client-implementation": "1.0", - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "php-http/client-integration-tests": "^3.0", - "php-http/message-factory": "^1.1", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^8.0|^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Http\\Adapter\\Guzzle7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - } - ], - "description": "Guzzle 7 HTTP Adapter", - "homepage": "http://httplug.io", - "keywords": [ - "Guzzle", - "http" - ], - "support": { - "issues": "https://github.com/php-http/guzzle7-adapter/issues", - "source": "https://github.com/php-http/guzzle7-adapter/tree/1.1.0" - }, - "time": "2024-11-26T11:14:36+00:00" - }, - { - "name": "php-http/httplug", - "version": "2.4.1", - "source": { - "type": "git", - "url": "https://github.com/php-http/httplug.git", - "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/httplug/zipball/5cad731844891a4c282f3f3e1b582c46839d22f4", - "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0", - "php-http/promise": "^1.1", - "psr/http-client": "^1.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", - "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eric GELOEN", - "email": "geloen.eric@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "HTTPlug, the HTTP client abstraction for PHP", - "homepage": "http://httplug.io", - "keywords": [ - "client", - "http" - ], - "support": { - "issues": "https://github.com/php-http/httplug/issues", - "source": "https://github.com/php-http/httplug/tree/2.4.1" - }, - "time": "2024-09-23T11:39:58+00:00" - }, - { - "name": "php-http/promise", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/php-http/promise.git", - "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83", - "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3", - "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Http\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Joel Wurtz", - "email": "joel.wurtz@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Promise used for asynchronous HTTP requests", - "homepage": "http://httplug.io", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/1.3.1" - }, - "time": "2024-03-15T13:55:21+00:00" - }, - { - "name": "phpactor/test-utils", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpactor/test-utils.git", - "reference": "049f6e11a3809d5f124dd683f53cb2ab00f99b2f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpactor/test-utils/zipball/049f6e11a3809d5f124dd683f53cb2ab00f99b2f", - "reference": "049f6e11a3809d5f124dd683f53cb2ab00f99b2f", - "shasum": "" - }, - "require": { - "php": "^8.1", - "symfony/filesystem": "^4.2 || ^5.0 || ^6.0 || ^7.0" - }, - "require-dev": { - "dms/phpunit-arraysubset-asserts": "dev-master", - "ergebnis/composer-normalize": "^2.0", - "friendsofphp/php-cs-fixer": "^2.17", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "~0.12.0", - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Phpactor\\TestUtils\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Utilities for managing the test environment", - "support": { - "issues": "https://github.com/phpactor/test-utils/issues", - "source": "https://github.com/phpactor/test-utils/tree/2.0.0" - }, - "time": "2025-11-23T17:05:07+00:00" - }, - { - "name": "phpbench/container", - "version": "2.2.3", - "source": { - "type": "git", - "url": "https://github.com/phpbench/container.git", - "reference": "0c7b2d36c1ea53fe27302fb8873ded7172047196" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpbench/container/zipball/0c7b2d36c1ea53fe27302fb8873ded7172047196", - "reference": "0c7b2d36c1ea53fe27302fb8873ded7172047196", - "shasum": "" - }, - "require": { - "psr/container": "^1.0|^2.0", - "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0 || ^7.0 || ^8.0" - }, - "require-dev": { - "php-cs-fixer/shim": "^3.89", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "PhpBench\\DependencyInjection\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "Simple, configurable, service container.", - "support": { - "issues": "https://github.com/phpbench/container/issues", - "source": "https://github.com/phpbench/container/tree/2.2.3" - }, - "time": "2025-11-06T09:05:13+00:00" - }, - { - "name": "phpbench/phpbench", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/phpbench/phpbench.git", - "reference": "661c8c6abbc7734986cf7bc6062c237fbb450461" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/661c8c6abbc7734986cf7bc6062c237fbb450461", - "reference": "661c8c6abbc7734986cf7bc6062c237fbb450461", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^2.0", - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "ext-tokenizer": "*", - "php": "^8.2", - "phpbench/container": "^2.2", - "psr/log": "^1.1 || ^2.0 || ^3.0", - "seld/jsonlint": "^1.1", - "symfony/console": "^6.1 || ^7.0 || ^8.0", - "symfony/filesystem": "^6.1 || ^7.0 || ^8.0", - "symfony/finder": "^6.1 || ^7.0 || ^8.0", - "symfony/options-resolver": "^6.1 || ^7.0 || ^8.0", - "symfony/process": "^6.1 || ^7.0 || ^8.0", - "webmozart/glob": "^4.6" - }, - "require-dev": { - "dantleech/invoke": "^2.0", - "ergebnis/composer-normalize": "^2.39", - "jangregor/phpstan-prophecy": "^1.0", - "php-cs-fixer/shim": "^3.9", - "phpspec/prophecy": "^1.22", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^11.5", - "rector/rector": "^1.2", - "sebastian/exporter": "^6.3.2", - "symfony/error-handler": "^6.1 || ^7.0 || ^8.0", - "symfony/var-dumper": "^6.1 || ^7.0 || ^8.0" - }, - "suggest": { - "ext-xdebug": "For Xdebug profiling extension." - }, - "bin": [ - "bin/phpbench" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "files": [ - "lib/Report/Func/functions.php" - ], - "psr-4": { - "PhpBench\\": "lib/", - "PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "PHP Benchmarking Framework", - "keywords": [ - "benchmarking", - "optimization", - "performance", - "profiling", - "testing" - ], - "support": { - "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.6.1" - }, - "funding": [ - { - "url": "https://github.com/dantleech", - "type": "github" - } - ], - "time": "2026-03-22T10:27:20+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.6.5", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "90614c73d3800e187615e2dd236ad0e2a01bf761" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/90614c73d3800e187615e2dd236ad0e2a01bf761", - "reference": "90614c73d3800e187615e2dd236ad0e2a01bf761", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.5 || ~1.6.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.5" - }, - "time": "2025-11-27T19:50:05+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.12.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", - "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.18|^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" - }, - "time": "2025-11-21T15:09:14+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.22.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/35f1adb388946d92e6edab2aa2cb2b60e132ebd5", - "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.4 || 8.0.* || 8.1.* || 8.2.* || 8.3.* || 8.4.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.40", - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^2.1.13", - "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.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", - "dev", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.22.0" - }, - "time": "2025-04-29T14:58:06+00:00" - }, - { - "name": "phpspec/prophecy-phpunit", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy-phpunit.git", - "reference": "d3c28041d9390c9bca325a08c5b2993ac855bded" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/d3c28041d9390c9bca325a08c5b2993ac855bded", - "reference": "d3c28041d9390c9bca325a08c5b2993ac855bded", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8", - "phpspec/prophecy": "^1.18", - "phpunit/phpunit": "^9.1 || ^10.1 || ^11.0 || ^12.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.10" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\PhpUnit\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christophe Coevoet", - "email": "stof@notk.org" - } - ], - "description": "Integrating the Prophecy mocking library in PHPUnit test cases", - "homepage": "http://phpspec.net", - "keywords": [ - "phpunit", - "prophecy" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy-phpunit/issues", - "source": "https://github.com/phpspec/prophecy-phpunit/tree/v2.4.0" - }, - "time": "2025-05-13T13:52:32+00:00" - }, - { - "name": "phpstan/extension-installer", - "version": "1.4.3", - "source": { - "type": "git", - "url": "https://github.com/phpstan/extension-installer.git", - "reference": "85e90b3942d06b2326fba0403ec24fe912372936" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", - "reference": "85e90b3942d06b2326fba0403ec24fe912372936", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.0", - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.9.0 || ^2.0" - }, - "require-dev": { - "composer/composer": "^2.0", - "php-parallel-lint/php-parallel-lint": "^1.2.0", - "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" - }, - "type": "composer-plugin", - "extra": { - "class": "PHPStan\\ExtensionInstaller\\Plugin" - }, - "autoload": { - "psr-4": { - "PHPStan\\ExtensionInstaller\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Composer plugin for automatic installation of PHPStan extensions", - "keywords": [ - "dev", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpstan/extension-installer/issues", - "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" - }, - "time": "2024-09-04T20:21:43+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" - }, - "time": "2025-08-30T15:50:23+00:00" - }, - { - "name": "phpstan/phpstan", - "version": "2.1.32", - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e126cad1e30a99b137b8ed75a85a676450ebb227", - "reference": "e126cad1e30a99b137b8ed75a85a676450ebb227", - "shasum": "" - }, - "require": { - "php": "^7.4|^8.0" - }, - "conflict": { - "phpstan/phpstan-shim": "*" - }, - "bin": [ - "phpstan", - "phpstan.phar" - ], - "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPStan - PHP Static Analysis Tool", - "keywords": [ - "dev", - "static analysis" - ], - "support": { - "docs": "https://phpstan.org/user-guide/getting-started", - "forum": "https://github.com/phpstan/phpstan/discussions", - "issues": "https://github.com/phpstan/phpstan/issues", - "security": "https://github.com/phpstan/phpstan/security/policy", - "source": "https://github.com/phpstan/phpstan-src" - }, - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" - } - ], - "time": "2025-11-11T15:18:17+00:00" - }, - { - "name": "phpstan/phpstan-phpunit", - "version": "2.0.8", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "2fe9fbeceaf76dd1ebaa7bbbb25e2fb5e59db2fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/2fe9fbeceaf76dd1ebaa7bbbb25e2fb5e59db2fe", - "reference": "2fe9fbeceaf76dd1ebaa7bbbb25e2fb5e59db2fe", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.32" - }, - "conflict": { - "phpunit/phpunit": "<7.0" - }, - "require-dev": { - "nikic/php-parser": "^5", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-deprecation-rules": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6" - }, - "type": "phpstan-extension", - "extra": { - "phpstan": { - "includes": [ - "extension.neon", - "rules.neon" - ] - } - }, - "autoload": { - "psr-4": { - "PHPStan\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPUnit extensions and rules for PHPStan", - "support": { - "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.8" - }, - "time": "2025-11-11T07:55:22+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "10.1.16", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-text-template": "^3.0.1", - "sebastian/code-unit-reverse-lookup": "^3.0.0", - "sebastian/complexity": "^3.2.0", - "sebastian/environment": "^6.1.0", - "sebastian/lines-of-code": "^2.0.2", - "sebastian/version": "^4.0.1", - "theseer/tokenizer": "^1.2.3" - }, - "require-dev": { - "phpunit/phpunit": "^10.1" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.1.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 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", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-08-22T04:31:57+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "4.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "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", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-31T06:24:48+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:56:09+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "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", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-31T14:07:24+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "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/6.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:57:52+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "10.5.58", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e24fb46da450d8e6a5788670513c1af1424f16ca", - "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", - "phar-io/manifest": "^2.0.4", - "phar-io/version": "^3.2.1", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-invoker": "^4.0.0", - "phpunit/php-text-template": "^3.0.1", - "phpunit/php-timer": "^6.0.0", - "sebastian/cli-parser": "^2.0.1", - "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.4", - "sebastian/diff": "^5.1.1", - "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.4", - "sebastian/global-state": "^6.0.2", - "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.1", - "sebastian/type": "^4.0.0", - "sebastian/version": "^4.0.1" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "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", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.58" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2025-09-28T12:04:46+00:00" - }, - { - "name": "psalm/phar", - "version": "6.14.1", - "source": { - "type": "git", - "url": "https://github.com/psalm/phar.git", - "reference": "57ec52ce25ece3a00371a6d08ade8e36a61ca783" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/psalm/phar/zipball/57ec52ce25ece3a00371a6d08ade8e36a61ca783", - "reference": "57ec52ce25ece3a00371a6d08ade8e36a61ca783", - "shasum": "" - }, - "require": { - "php": "^8.2" - }, - "conflict": { - "vimeo/psalm": "*" - }, - "bin": [ - "psalm.phar" - ], - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Composer-based Psalm Phar", - "support": { - "issues": "https://github.com/psalm/phar/issues", - "source": "https://github.com/psalm/phar/tree/6.14.1" - }, - "time": "2025-12-10T09:38:52+00:00" - }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client" - }, - "time": "2023-09-23T14:17:50+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory" - }, - "time": "2024-04-15T12:06:14+00:00" - }, - { - "name": "psr/http-message", - "version": "2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" - }, - "time": "2023-04-04T09:54:51+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "react/cache", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/cache.git", - "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", - "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "react/promise": "^3.0 || ^2.0 || ^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Cache\\": "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": "Async, Promise-based cache interface for ReactPHP", - "keywords": [ - "cache", - "caching", - "promise", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/cache/issues", - "source": "https://github.com/reactphp/cache/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2022-11-30T15:59:55+00:00" - }, - { - "name": "react/child-process", - "version": "v0.6.6", - "source": { - "type": "git", - "url": "https://github.com/reactphp/child-process.git", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.0", - "react/event-loop": "^1.2", - "react/stream": "^1.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/socket": "^1.16", - "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\ChildProcess\\": "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 library for executing child processes with ReactPHP.", - "keywords": [ - "event-driven", - "process", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/child-process/issues", - "source": "https://github.com/reactphp/child-process/tree/v0.6.6" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2025-01-01T16:37:48+00:00" - }, - { - "name": "react/dns", - "version": "v1.14.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/dns.git", - "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", - "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "react/cache": "^1.0 || ^0.6 || ^0.5", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.7 || ^1.2.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3 || ^2", - "react/promise-timer": "^1.11" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Dns\\": "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": "Async DNS resolver for ReactPHP", - "keywords": [ - "async", - "dns", - "dns-resolver", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.14.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2025-11-18T19:34:28+00:00" - }, - { - "name": "react/event-loop", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", - "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" - }, - "suggest": { - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" - }, - "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.6.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2025-11-17T20:46:25+00:00" - }, - { - "name": "react/promise", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", - "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", - "shasum": "" - }, - "require": { - "php": ">=7.1.0" - }, - "require-dev": { - "phpstan/phpstan": "1.12.28 || 1.4.10", - "phpunit/phpunit": "^9.6 || ^7.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "React\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "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": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "keywords": [ - "promise", - "promises" - ], - "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.3.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2025-08-19T18:57:03+00:00" - }, - { - "name": "react/socket", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/socket.git", - "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", - "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.0", - "react/dns": "^1.13", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.6 || ^1.2.1", - "react/stream": "^1.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3.3 || ^2", - "react/promise-stream": "^1.4", - "react/promise-timer": "^1.11" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Socket\\": "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": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", - "keywords": [ - "Connection", - "Socket", - "async", - "reactphp", - "stream" - ], - "support": { - "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.17.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2025-11-19T20:47:34+00:00" - }, - { - "name": "react/stream", - "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", - "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.6 || ^5.7 || ^4.8.36" - }, - "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.4.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2024-06-11T12:45:25+00:00" - }, - { - "name": "rector/rector", - "version": "2.2.9", - "source": { - "type": "git", - "url": "https://github.com/rectorphp/rector.git", - "reference": "0b8e49ec234877b83244d2ecd0df7a4c16471f05" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/0b8e49ec234877b83244d2ecd0df7a4c16471f05", - "reference": "0b8e49ec234877b83244d2ecd0df7a4c16471f05", - "shasum": "" - }, - "require": { - "php": "^7.4|^8.0", - "phpstan/phpstan": "^2.1.32" - }, - "conflict": { - "rector/rector-doctrine": "*", - "rector/rector-downgrade-php": "*", - "rector/rector-phpunit": "*", - "rector/rector-symfony": "*" - }, - "suggest": { - "ext-dom": "To manipulate phpunit.xml via the custom-rule command" - }, - "bin": [ - "bin/rector" - ], - "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Instant Upgrade and Automated Refactoring of any PHP code", - "homepage": "https://getrector.com/", - "keywords": [ - "automation", - "dev", - "migration", - "refactoring" - ], - "support": { - "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.2.9" - }, - "funding": [ - { - "url": "https://github.com/tomasvotruba", - "type": "github" - } - ], - "time": "2025-11-28T14:21:22+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-02T07:12:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:58:43+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "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/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:59:15+00:00" - }, - { - "name": "sebastian/comparator", - "version": "5.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.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", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" - } - ], - "time": "2025-09-07T05:25:07+00:00" - }, - { - "name": "sebastian/complexity", - "version": "3.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-12-21T08:37:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "6.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.1-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": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-23T08:47:14+00:00" - }, - { - "name": "sebastian/exporter", - "version": "5.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0735b90f4da94969541dac1da743446e276defa6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", - "reference": "0735b90f4da94969541dac1da743446e276defa6", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-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": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", - "type": "tidelift" - } - ], - "time": "2025-09-24T06:09:11+00:00" - }, - { - "name": "sebastian/global-state", - "version": "6.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.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": "https://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-02T07:19:19+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-12-21T08:38:20+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-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/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:08:32+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "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/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:06:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "5.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.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": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", - "type": "tidelift" - } - ], - "time": "2025-08-10T07:50:56+00:00" - }, - { - "name": "sebastian/type", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:10:45+00:00" - }, - { - "name": "sebastian/version", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library 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/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-07T11:34:05+00:00" - }, - { - "name": "seld/jsonlint", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", - "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", - "shasum": "" - }, - "require": { - "php": "^5.3 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.11", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" - }, - "bin": [ - "bin/jsonlint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" - } - ], - "description": "JSON Linter", - "keywords": [ - "json", - "linter", - "parser", - "validator" - ], - "support": { - "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" - }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", - "type": "tidelift" - } - ], - "time": "2024-07-11T14:55:45+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.13.5", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", - "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" - }, - "bin": [ - "bin/phpcbf", - "bin/phpcs" - ], - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "Former lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "Current lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", - "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", - "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" - }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - }, - { - "url": "https://thanks.dev/u/gh/phpcsstandards", - "type": "thanks_dev" - } - ], - "time": "2025-11-04T16:30:35+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v6.4.25", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b0cf3162020603587363f0551cd3be43958611ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b0cf3162020603587363f0551cd3be43958611ff", - "reference": "b0cf3162020603587363f0551cd3be43958611ff", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/event-dispatcher-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/service-contracts": "<2.5" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "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": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.25" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-08-13T09:41:44+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/event-dispatcher": "^1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "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 dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.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": "2024-09-25T14:21:43+00:00" - }, - { - "name": "symfony/finder", - "version": "v7.4.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "e0be088d22278583a82da281886e8c3592fbf149" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", - "reference": "e0be088d22278583a82da281886e8c3592fbf149", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "symfony/filesystem": "^6.4|^7.0|^8.0" - }, - "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/v7.4.8" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-24T13:12:05+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "v7.4.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", - "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "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": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.4.8" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-24T13:12:05+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.36.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "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.36.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-04-10T16:19:22+00:00" - }, - { - "name": "symfony/polyfill-php81", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "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 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-php82", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php82.git", - "reference": "5d2ed36f7734637dacc025f179698031951b1692" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/5d2ed36f7734637dacc025f179698031951b1692", - "reference": "5d2ed36f7734637dacc025f179698031951b1692", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php82\\": "" - }, - "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 8.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php82/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-php84", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php84\\": "" - }, - "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 8.4+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-24T13:30:11+00:00" - }, - { - "name": "symfony/stopwatch", - "version": "v6.4.24", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "b67e94e06a05d9572c2fa354483b3e13e3cb1898" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b67e94e06a05d9572c2fa354483b3e13e3cb1898", - "reference": "b67e94e06a05d9572c2fa354483b3e13e3cb1898", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/service-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "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": "Provides a way to profile code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.4.24" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-10T08:14:14+00:00" - }, - { - "name": "symfony/var-dumper", - "version": "v6.4.26", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfae1497a2f1eaad78dbc0590311c599c7178d4a", - "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/console": "<5.4" - }, - "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^6.3|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/uid": "^5.4|^6.0|^7.0", - "twig/twig": "^2.13|^3.0.4" - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "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 mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.26" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-09-25T15:37:27+00:00" - }, - { - "name": "tbachert/spi", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "https://github.com/Nevay/spi.git", - "reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Nevay/spi/zipball/e7078767866d0a9e0f91d3f9d42a832df5e39002", - "reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.0", - "composer/semver": "^1.0 || ^2.0 || ^3.0", - "php": "^8.1" - }, - "require-dev": { - "composer/composer": "^2.0", - "infection/infection": "^0.27.9", - "phpunit/phpunit": "^10.5", - "psalm/phar": "^5.18" - }, - "type": "composer-plugin", - "extra": { - "class": "Nevay\\SPI\\Composer\\Plugin", - "branch-alias": { - "dev-main": "1.0.x-dev" - }, - "plugin-optional": true - }, - "autoload": { - "psr-4": { - "Nevay\\SPI\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "Service provider loading facility", - "keywords": [ - "service provider" - ], - "support": { - "issues": "https://github.com/Nevay/spi/issues", - "source": "https://github.com/Nevay/spi/tree/v1.0.5" - }, - "time": "2025-06-29T15:42:06+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", - "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.3.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2025-11-17T20:03:58+00:00" - } - ], - "aliases": [], - "minimum-stability": "dev", - "stability-flags": { - "dms/phpunit-arraysubset-asserts": 20, - "jetbrains/phpstorm-stubs": 20, - "phpactor/tolerant-php-parser": 20 - }, - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": "^8.2", - "ext-mbstring": "*", - "ext-posix": "*", - "ext-tokenizer": "*" - }, - "platform-dev": {}, - "platform-overrides": { - "php": "8.2.0" - }, - "plugin-api-version": "2.6.0" -} diff --git a/dev/bench/data.js b/dev/bench/data.js new file mode 100644 index 0000000000..292840dd40 --- /dev/null +++ b/dev/bench/data.js @@ -0,0 +1,14568 @@ +window.BENCHMARK_DATA = { + "lastUpdate": 1787400017513, + "repoUrl": "https://github.com/phpactor/phpactor", + "entries": { + "Phpactor Benchmarks": [ + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "e55467cb0a9c40e47df39051ab7b8dd34dc6ae17", + "message": "Do not use \"auto\" time unit", + "timestamp": "2026-03-21T18:28:34Z", + "tree_id": "ff700205cba0cdb57620af07493504f2f68ee723", + "url": "https://github.com/phpactor/phpactor/commit/e55467cb0a9c40e47df39051ab7b8dd34dc6ae17" + }, + "date": 1774117829034, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.24696477495105, + "range": "± 2.68%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 164.27268884539728, + "range": "± 0.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.3111193737768865, + "range": "± 0.93%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.724397260273932, + "range": "± 0.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.03316234833659558, + "range": "± 1.67%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03457581213307178, + "range": "± 1.31%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05687369863013621, + "range": "± 1.10%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01967338551859104, + "range": "± 6.54%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.09325831702543969, + "range": "± 1.00%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05740735812133071, + "range": "± 9.11%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.17440430528376, + "range": "± 1.46%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 557, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1335, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.30799412915857, + "range": "± 0.84%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.474461839530354, + "range": "± 4.02%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09189119373776895, + "range": "± 2.38%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09140371819960985, + "range": "± 0.76%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.0902465753424652, + "range": "± 1.56%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09120665362035171, + "range": "± 1.71%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09108493150684895, + "range": "± 5.12%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08842270058708455, + "range": "± 1.62%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.09047788649706354, + "range": "± 3.07%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6785103718199648, + "range": "± 3.69%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.059771624266144026, + "range": "± 4.40%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1395929549902152, + "range": "± 6.35%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.14095107632093926, + "range": "± 11.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13458317025440306, + "range": "± 5.74%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1356673189823874, + "range": "± 7.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1127323, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08983757338551857, + "range": "± 13.15%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 344, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 308, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 291, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 77211.0782778865, + "range": "± 176.48%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 314905.8082191789, + "range": "± 0.25%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 71474.36007827798, + "range": "± 0.77%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28731.281800390836, + "range": "± 0.52%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25043.837573385637, + "range": "± 0.35%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30168.066536203092, + "range": "± 0.41%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 818233.3933463655, + "range": "± 0.48%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 117079, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.6322485322896445, + "range": "± 1.02%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.093571428571465, + "range": "± 0.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17170.28180039191, + "range": "± 0.40%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 151.1743013698638, + "range": "± 0.31%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 144.85020352250413, + "range": "± 0.55%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7440410958904213, + "range": "± 1.02%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.114698630136989, + "range": "± 3.72%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.228949119373781, + "range": "± 1.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9698923679060899, + "range": "± 1.08%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4210726027397222, + "range": "± 0.56%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.78, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 97.21092465753557, + "range": "± 0.45%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 103.44733365949232, + "range": "± 0.84%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 170094, + "range": "± 194.95%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 117093.4794520555, + "range": "± 0.86%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "e55467cb0a9c40e47df39051ab7b8dd34dc6ae17", + "message": "Do not use \"auto\" time unit", + "timestamp": "2026-03-21T18:28:34Z", + "tree_id": "ff700205cba0cdb57620af07493504f2f68ee723", + "url": "https://github.com/phpactor/phpactor/commit/e55467cb0a9c40e47df39051ab7b8dd34dc6ae17" + }, + "date": 1774118058016, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.502767123287837, + "range": "± 1.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 166.3120489236794, + "range": "± 0.85%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.4262446183952635, + "range": "± 1.99%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.789095890411232, + "range": "± 0.94%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.03311502935420781, + "range": "± 1.68%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03461295499021525, + "range": "± 1.71%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05718058708414852, + "range": "± 1.72%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.019633894324853268, + "range": "± 5.23%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.09515561643835678, + "range": "± 1.47%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05828387475538144, + "range": "± 3.57%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.708176125244634, + "range": "± 7.46%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 696, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1384, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.546602739725916, + "range": "± 1.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.97086301369847, + "range": "± 1.40%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09316027397260257, + "range": "± 4.45%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09217436399217184, + "range": "± 3.20%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.0925978473581215, + "range": "± 3.92%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09245616438356105, + "range": "± 1.66%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09196712328767088, + "range": "± 2.92%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.09176986301369928, + "range": "± 1.35%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.09084324853229069, + "range": "± 1.64%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6995596868884482, + "range": "± 1.22%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05519804305283749, + "range": "± 3.30%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.14638943248532274, + "range": "± 6.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.14667906066536193, + "range": "± 10.63%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13968493150684919, + "range": "± 7.43%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13923091976516622, + "range": "± 9.67%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1210551, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.0913131115459882, + "range": "± 13.13%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 311, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 300, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 310, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 79621.98043052838, + "range": "± 176.48%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 320762.6731898254, + "range": "± 1.24%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 72635.78473581202, + "range": "± 0.81%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 29265.26418786697, + "range": "± 1.03%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25321.281800391207, + "range": "± 0.66%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30741.24657534244, + "range": "± 1.58%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 826899.4794520579, + "range": "± 1.21%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 124099, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.6655616438355918, + "range": "± 1.83%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.1330489236790764, + "range": "± 1.98%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17614.14872798441, + "range": "± 3.13%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 155.76130528375873, + "range": "± 1.32%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 148.0355714285714, + "range": "± 1.12%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.764739726027403, + "range": "± 2.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.1201174168297494, + "range": "± 2.06%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.259516634050895, + "range": "± 1.40%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 1.0065356164383494, + "range": "± 1.88%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4530289628180002, + "range": "± 1.58%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.896, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 102.82602739726302, + "range": "± 1.28%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 108.50147162426403, + "range": "± 1.06%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 180878.1937377691, + "range": "± 199.42%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 121508.528375734, + "range": "± 5.10%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "e55467cb0a9c40e47df39051ab7b8dd34dc6ae17", + "message": "Do not use \"auto\" time unit", + "timestamp": "2026-03-21T18:28:34Z", + "tree_id": "ff700205cba0cdb57620af07493504f2f68ee723", + "url": "https://github.com/phpactor/phpactor/commit/e55467cb0a9c40e47df39051ab7b8dd34dc6ae17" + }, + "date": 1774123413475, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.263712328767085, + "range": "± 1.88%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 165.25783170254576, + "range": "± 0.81%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.3455068493150266, + "range": "± 1.38%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.68949315068472, + "range": "± 1.08%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.033192876712328935, + "range": "± 1.21%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03493295499021539, + "range": "± 1.09%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05695448140900139, + "range": "± 0.89%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.019638590998042743, + "range": "± 1.50%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.09339272015655607, + "range": "± 1.36%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.057229119373776796, + "range": "± 10.11%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.201060273972573, + "range": "± 0.58%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 543, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1338, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.418234833659564, + "range": "± 0.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.599330724070485, + "range": "± 5.81%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09219158512719997, + "range": "± 2.32%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09227690802348326, + "range": "± 1.94%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09163346379647738, + "range": "± 1.90%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09240039138943187, + "range": "± 2.86%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.0908953033268101, + "range": "± 3.56%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.09029589041095949, + "range": "± 1.63%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.0914234833659499, + "range": "± 3.57%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6739019569471625, + "range": "± 1.29%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05543072407045029, + "range": "± 3.40%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1403424657534246, + "range": "± 5.62%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13986105675146762, + "range": "± 6.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13304500978473582, + "range": "± 7.43%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13411545988258314, + "range": "± 4.63%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1146360, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08787084148727935, + "range": "± 4.34%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 297, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 307, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 293, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 78819.12720156556, + "range": "± 176.66%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 316131.85714285664, + "range": "± 1.61%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 72504.62426614464, + "range": "± 2.12%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28704.75929549909, + "range": "± 0.28%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25413.414872798883, + "range": "± 0.59%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30440.75146771044, + "range": "± 0.80%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 817932.7534246517, + "range": "± 0.64%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 117827, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.6044579256360212, + "range": "± 1.10%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.1161272015655275, + "range": "± 0.97%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17245.32876712324, + "range": "± 0.57%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 151.70543248532172, + "range": "± 0.40%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 145.80695499021522, + "range": "± 0.52%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7435499021526193, + "range": "± 0.85%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.1077260273972462, + "range": "± 1.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2398786692759045, + "range": "± 0.68%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9736119373776734, + "range": "± 0.86%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4278863013698688, + "range": "± 0.74%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 6.011, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 99.09261937377451, + "range": "± 0.69%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 104.74459197651753, + "range": "± 0.78%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 170856, + "range": "± 194.65%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 118409.70450097825, + "range": "± 0.89%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "przepompownia@users.noreply.github.com", + "name": "Tomasz N", + "username": "przepompownia" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "77543faa924d1ea336a5284aa146789a3b63fbf0", + "message": "fix (BinaryExpressionResolver): null coalesce on undefined variable (#3031)", + "timestamp": "2026-03-21T21:25:17Z", + "tree_id": "e3117f5546654421addc99308fa2522ad4b43853", + "url": "https://github.com/phpactor/phpactor/commit/77543faa924d1ea336a5284aa146789a3b63fbf0" + }, + "date": 1774128418061, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.677794520547955, + "range": "± 1.91%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 166.0490489236791, + "range": "± 2.06%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.367587084148717, + "range": "± 3.19%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.755446183952998, + "range": "± 0.70%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.03308602739726055, + "range": "± 1.66%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03479041095890356, + "range": "± 1.34%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05701409001956958, + "range": "± 1.78%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.019654403131115484, + "range": "± 1.83%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.0929769863013701, + "range": "± 0.96%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.057063365949119677, + "range": "± 1.57%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.17453855185901, + "range": "± 0.63%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 587, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1348, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.207334637964705, + "range": "± 1.19%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.4304911937379, + "range": "± 0.59%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09020821917808175, + "range": "± 2.40%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09039178082191605, + "range": "± 1.60%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09009452054794514, + "range": "± 2.61%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09173933463796334, + "range": "± 2.22%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09134011741683108, + "range": "± 2.15%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.0909348336594913, + "range": "± 5.88%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.09123972602739808, + "range": "± 1.48%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.671474168297457, + "range": "± 1.67%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05648884540117396, + "range": "± 8.01%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1400547945205479, + "range": "± 5.79%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13906457925635998, + "range": "± 11.71%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13301956947162422, + "range": "± 4.94%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13421917808219172, + "range": "± 6.90%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1143783, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08737573385518578, + "range": "± 6.25%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 290, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 300, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 307, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 77358.14090019569, + "range": "± 176.79%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 313523.83953033295, + "range": "± 1.42%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 71220.28571428522, + "range": "± 0.53%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28487.65949119336, + "range": "± 0.50%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 24829.876712328747, + "range": "± 4.20%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30424.655577299083, + "range": "± 0.37%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 815173.3600782793, + "range": "± 1.29%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 118617, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5998512720156342, + "range": "± 1.15%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.0432172211350412, + "range": "± 1.13%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17012.365949119543, + "range": "± 0.93%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 149.98205479452085, + "range": "± 0.85%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 144.91742857142896, + "range": "± 1.21%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7272857142857057, + "range": "± 2.22%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.068929549902121, + "range": "± 1.18%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2027358121330995, + "range": "± 1.05%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9656375733855055, + "range": "± 0.75%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4074925636007758, + "range": "± 0.81%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.765, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 96.08085714285743, + "range": "± 0.58%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 101.49212133072362, + "range": "± 0.56%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 169425, + "range": "± 193.93%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 115327.46379647584, + "range": "± 0.76%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "cb25ad263b9d3aa87f98a6040bdc3194d715766c", + "message": "gh-3022: explictly specify byte order (#3033)\n\nIf ext-mbstring is not installed, then\nhttps://github.com/symfony/polyfill-mbstring will take over. The\npolyfill uses `iconv`\n\nThere is an off-by-one issue that happens when the ext-mbstring is not\nenabled.\n\n`mbstring` outputs UTF-16BE (first in screenshot) and `iconv` outputs UTF-16LE and also adds BOM (fffe).\n\nBy explicitly specifying the byte order we remove the ambiguity.", + "timestamp": "2026-03-21T21:25:34Z", + "tree_id": "dfd0bce59f7ac156b4ed6d2d6477b9d2342bc560", + "url": "https://github.com/phpactor/phpactor/commit/cb25ad263b9d3aa87f98a6040bdc3194d715766c" + }, + "date": 1774128434259, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.243017612524469, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 164.44654207436338, + "range": "± 1.22%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.350600782778854, + "range": "± 0.94%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.38650097847323, + "range": "± 0.60%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.03294465753424704, + "range": "± 1.58%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.0344785127201566, + "range": "± 1.94%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05667643835616438, + "range": "± 5.90%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.019685048923679116, + "range": "± 3.98%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.09330027397260365, + "range": "± 0.99%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05751863013698617, + "range": "± 2.54%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.26336360078275, + "range": "± 4.02%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 564, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1348, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.383931506849326, + "range": "± 4.45%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.643782778864784, + "range": "± 1.19%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09371819960861054, + "range": "± 2.19%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09061643835616437, + "range": "± 0.89%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09146105675146782, + "range": "± 6.54%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09048590998042941, + "range": "± 1.90%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09086497064579295, + "range": "± 3.14%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.09096712328767119, + "range": "± 6.34%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.09085048923679168, + "range": "± 1.55%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.684146966731908, + "range": "± 1.74%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.057526810176126, + "range": "± 2.46%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.14279256360078268, + "range": "± 10.59%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1394109589041095, + "range": "± 10.52%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13397260273972597, + "range": "± 8.05%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13309197651663401, + "range": "± 8.67%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1142119, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08997064579256343, + "range": "± 10.32%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 313, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 290, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 297, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 78822.38551859099, + "range": "± 176.85%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 316471.7338551874, + "range": "± 0.52%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 71948.1193737745, + "range": "± 0.59%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 29019.136986301863, + "range": "± 1.31%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 24943.92563600788, + "range": "± 0.34%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30382.87475538144, + "range": "± 0.36%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 822634.0684931572, + "range": "± 0.47%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 120976, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.619549902152654, + "range": "± 1.77%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.0846653620352247, + "range": "± 0.80%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17273.178082191676, + "range": "± 0.96%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 153.23224461839598, + "range": "± 0.30%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 146.69941487279704, + "range": "± 0.76%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7439178082191793, + "range": "± 2.26%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.1178571428571296, + "range": "± 2.07%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2376399217221152, + "range": "± 2.52%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9702248532289631, + "range": "± 0.73%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4308273972602823, + "range": "± 0.82%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.786, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 98.65015655577375, + "range": "± 0.74%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 105.02368590998172, + "range": "± 0.87%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 171456, + "range": "± 195.38%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 117940.12915851222, + "range": "± 1.73%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "613aa65ce2944b44c8fbb92281ce11bd8c9dfbd6", + "message": "Optimise index service and command (#3037)\n\nThis commit introduces a service to optimise the index.\n\nOptimising currently involces of iterating over all records and pruning\nany records that are defined in non-existing files and removing\n\n- Introduced index iterator\n- Optimizer\n- Add optimiser service that runs every hour by default\n- Add command to manually invoke the optimiser\n- Add LSP notification `phpactor/indexer/optimise` to manually invoke if\n necessary.", + "timestamp": "2026-04-13T22:22:49+01:00", + "tree_id": "ef2b52c248cda116c5ac9866d838659f54e835da", + "url": "https://github.com/phpactor/phpactor/commit/613aa65ce2944b44c8fbb92281ce11bd8c9dfbd6" + }, + "date": 1776115474955, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.120994129158479, + "range": "± 1.44%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 164.89672407044804, + "range": "± 0.47%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.3416477495107273, + "range": "± 1.33%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.488827788649985, + "range": "± 0.92%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.02774054794520524, + "range": "± 1.40%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.02945060665362051, + "range": "± 2.46%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.0510578473581215, + "range": "± 1.08%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015338943248532563, + "range": "± 2.04%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08825138943248646, + "range": "± 1.34%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05772148727984227, + "range": "± 1.28%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.27180313111546, + "range": "± 4.17%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 583, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1374, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.228802348336513, + "range": "± 0.86%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.509851272015464, + "range": "± 1.45%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09142641878669341, + "range": "± 3.26%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09080234833659487, + "range": "± 1.73%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09024794520547971, + "range": "± 1.24%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09093933463796396, + "range": "± 1.76%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.0925344422700595, + "range": "± 2.04%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.09268610567514682, + "range": "± 15.79%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.09159041095890276, + "range": "± 1.78%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.691036594911942, + "range": "± 1.48%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.055087671232876244, + "range": "± 3.58%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1373698630136985, + "range": "± 5.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13763796477495102, + "range": "± 5.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13242465753424648, + "range": "± 9.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1318160469667318, + "range": "± 9.22%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1170076, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08807045009784724, + "range": "± 7.20%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 299, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 334, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 299, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 323342.61056751467, + "range": "± 126.77%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5786868884540108, + "range": "± 1.62%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.058835616438339, + "range": "± 0.94%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17148.30919765151, + "range": "± 0.93%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 153.27115068493248, + "range": "± 0.47%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 147.4893796477463, + "range": "± 0.55%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 74137.30724070473, + "range": "± 1.60%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 119540, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7289080234833791, + "range": "± 1.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.0723483365949, + "range": "± 0.98%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.216870841487279, + "range": "± 2.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.832, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 72445.87475538198, + "range": "± 0.43%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28599.61643835641, + "range": "± 0.68%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 24907.06066536205, + "range": "± 0.67%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30153.420743640534, + "range": "± 0.70%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 839516.1780821816, + "range": "± 1.07%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9748589041095872, + "range": "± 1.30%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4050326810176224, + "range": "± 0.81%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 173887.7925636008, + "range": "± 200.96%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 117496.27397260144, + "range": "± 1.33%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 93.93509393346353, + "range": "± 1.32%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 100.61395303326775, + "range": "± 0.31%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "przepompownia@users.noreply.github.com", + "name": "Tomasz N", + "username": "przepompownia" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "77f9ff9b50c81300fdd4fecb6fe5f89067cf5cb0", + "message": "Cleanup after removing PHP 8.1 support (#3038)", + "timestamp": "2026-04-17T18:06:37+01:00", + "tree_id": "eb14d4c6199ebed69657adf29fbbec2eb78ba4ed", + "url": "https://github.com/phpactor/phpactor/commit/77f9ff9b50c81300fdd4fecb6fe5f89067cf5cb0" + }, + "date": 1776445687060, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 8.532778864970545, + "range": "± 1.38%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 139.16786301369848, + "range": "± 0.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 1.9265753424657577, + "range": "± 1.07%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 19.12568688845478, + "range": "± 1.29%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.015703326810176037, + "range": "± 1.48%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.0166421526418785, + "range": "± 1.79%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.036552093933464154, + "range": "± 1.25%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.007386810176125254, + "range": "± 3.09%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.0670994911937371, + "range": "± 0.86%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.046162191780822065, + "range": "± 1.19%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 14.957459491193763, + "range": "± 0.32%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 490, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1208, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 10.003994129158404, + "range": "± 1.13%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 10.045146771037174, + "range": "± 0.55%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07121291585127144, + "range": "± 2.27%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.06949178082191756, + "range": "± 1.88%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07142230919765043, + "range": "± 1.68%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07196673189823867, + "range": "± 1.85%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07116634050880613, + "range": "± 11.42%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07000489236790643, + "range": "± 1.72%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07134481409001836, + "range": "± 1.47%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.4373628180039117, + "range": "± 0.66%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.04874305283757334, + "range": "± 9.28%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.10993346379647743, + "range": "± 5.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1091369863013698, + "range": "± 5.36%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.10688062622309193, + "range": "± 8.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.10719178082191774, + "range": "± 4.25%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 965387, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.07403522504892278, + "range": "± 1.36%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 261, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 255, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 281, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 273317.22113502934, + "range": "± 127.32%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.2758864970645731, + "range": "± 1.42%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.5184207436399135, + "range": "± 0.91%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 14681.682974559759, + "range": "± 1.04%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 129.5318551859089, + "range": "± 0.42%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 124.34115264187753, + "range": "± 0.20%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 65357.767123288126, + "range": "± 0.77%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 101122, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.3887847358121328, + "range": "± 4.11%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.547518590998038, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 1.7998767123287571, + "range": "± 0.52%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 4.674, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 58853.36007827805, + "range": "± 0.74%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 23541.547945205544, + "range": "± 0.70%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 20997.12328767142, + "range": "± 1.17%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 26879.43835616402, + "range": "± 0.37%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 671528.387475532, + "range": "± 0.16%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.7741129158512686, + "range": "± 1.35%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.154981409001977, + "range": "± 0.44%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 134836, + "range": "± 204.50%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 93162.48532289639, + "range": "± 0.74%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 76.74860469667334, + "range": "± 0.65%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 81.55726027397215, + "range": "± 0.30%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "przepompownia@users.noreply.github.com", + "name": "Tomasz N", + "username": "przepompownia" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "4ad4307c8348df4eb525f3a20e5f8ce80a3c4623", + "message": "Fix PHP 8.5 issues (#2996)\n\nProblem: some phpunit tests fail on PHP 8.5\n\nSolution:\n- upgrade Psalm version\n- upgrade Monolog (avoid: deprecation on the one side, dependency conflicts on the other side)\n- increase test Psalm process timeout to 15 s\n- fix new deprecations\n- add 8.5 to CI matrix", + "timestamp": "2026-04-18T12:45:39+01:00", + "tree_id": "23810b35ce27120a535570957da9c4145a599185", + "url": "https://github.com/phpactor/phpactor/commit/4ad4307c8348df4eb525f3a20e5f8ce80a3c4623" + }, + "date": 1776512839469, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.953017612524494, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 163.14431702543664, + "range": "± 0.45%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.2445205479451777, + "range": "± 0.90%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.398041095890257, + "range": "± 0.90%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.027716673189823935, + "range": "± 1.89%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.02925303326810157, + "range": "± 1.84%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05049033268101807, + "range": "± 1.09%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015377612524461891, + "range": "± 4.57%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08800861056751598, + "range": "± 1.16%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05697906066536204, + "range": "± 8.70%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.11344735812139, + "range": "± 2.21%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 586, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1385, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 11.932890410959116, + "range": "± 0.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.074583170254256, + "range": "± 0.60%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.08929236790606704, + "range": "± 1.84%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.08972230919765159, + "range": "± 1.62%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.08862994129158508, + "range": "± 1.23%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.08887260273972601, + "range": "± 2.27%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.0888857142857154, + "range": "± 2.02%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08910645792563528, + "range": "± 1.39%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.08934716242661354, + "range": "± 1.33%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6944156555773005, + "range": "± 11.51%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.0570960861056747, + "range": "± 3.05%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13447945205479447, + "range": "± 9.95%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13359491193737763, + "range": "± 5.26%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.12902739726027396, + "range": "± 9.31%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1291526418786692, + "range": "± 4.84%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1124163, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08671428571428592, + "range": "± 7.59%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 291, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 311, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 315, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 317073.240704501, + "range": "± 127.04%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5629354207436637, + "range": "± 0.60%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.999716242661497, + "range": "± 0.69%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16231.571428571453, + "range": "± 0.93%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 150.56836790606735, + "range": "± 0.76%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 144.20855185909414, + "range": "± 0.76%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 72099.23679060553, + "range": "± 0.29%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 117394, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.701450097847341, + "range": "± 1.39%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.045121330724107, + "range": "± 0.78%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.177178082191731, + "range": "± 0.92%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.611, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 70660.02348336583, + "range": "± 0.51%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28050.958904109393, + "range": "± 0.37%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 24754.133072406905, + "range": "± 0.60%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 29742.117416829773, + "range": "± 3.19%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 815224.1448140729, + "range": "± 0.71%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9402428571428627, + "range": "± 0.25%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.3795949119373652, + "range": "± 0.41%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 157461, + "range": "± 198.21%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 109095.9784735787, + "range": "± 0.78%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 88.4603923679057, + "range": "± 2.26%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 95.46423972602852, + "range": "± 0.51%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "przepompownia@users.noreply.github.com", + "name": "Tomasz N", + "username": "przepompownia" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "6a46d386795040bc7974b1995108336488d90fd2", + "message": "Allow goto definition from first class callables (#3025)", + "timestamp": "2026-04-18T12:47:07+01:00", + "tree_id": "1d0f352ee7dd446f234eae329724fa9decd4a15b", + "url": "https://github.com/phpactor/phpactor/commit/6a46d386795040bc7974b1995108336488d90fd2" + }, + "date": 1776512928115, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 8.436596868884452, + "range": "± 1.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 140.61432485322968, + "range": "± 1.69%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 1.9394931506849433, + "range": "± 1.90%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 19.223739726027397, + "range": "± 13.71%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.01564082191780805, + "range": "± 1.66%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.016819452054794588, + "range": "± 2.10%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.036789119373777136, + "range": "± 1.07%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.007395694716242696, + "range": "± 2.91%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.06670880626223151, + "range": "± 1.22%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.04671534246575367, + "range": "± 2.03%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 15.03607397260273, + "range": "± 1.19%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 499, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1184, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 10.009720156555845, + "range": "± 1.24%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 10.179788649706543, + "range": "± 0.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07208473581213193, + "range": "± 1.75%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.0705835616438339, + "range": "± 1.81%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07162054794520585, + "range": "± 0.73%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07161369863013789, + "range": "± 1.94%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07150547945205557, + "range": "± 1.95%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07238121330724134, + "range": "± 2.87%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07142328767123278, + "range": "± 1.35%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.4391643835616454, + "range": "± 1.23%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.0482181996086104, + "range": "± 1.78%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.11434050880626205, + "range": "± 17.89%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.11104892367906058, + "range": "± 10.52%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.10709001956947156, + "range": "± 1.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.10906457925636, + "range": "± 6.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 983652, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.07416046966731919, + "range": "± 6.22%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 263, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 254, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 252, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 277500.4520547945, + "range": "± 127.44%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.293937377690834, + "range": "± 0.86%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.580371819960894, + "range": "± 1.04%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 13820.876712328765, + "range": "± 1.04%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 130.26135616438165, + "range": "± 0.37%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 127.88866731898463, + "range": "± 0.47%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 66969.30528376, + "range": "± 0.64%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 102460, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.3882622309197623, + "range": "± 1.29%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.552338551859102, + "range": "± 1.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 1.7994285714285747, + "range": "± 1.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 4.681, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 59225.888454010914, + "range": "± 0.35%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 23710.068493150677, + "range": "± 0.55%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 21070.939334638606, + "range": "± 0.52%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 26617.129158512827, + "range": "± 0.30%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 676307.3561643874, + "range": "± 0.29%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.7821575342465766, + "range": "± 1.05%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.156929941291596, + "range": "± 1.11%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 134346, + "range": "± 206.05%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 94251.53816047158, + "range": "± 0.61%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 76.38162426614647, + "range": "± 0.55%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 82.36578082191662, + "range": "± 0.98%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "32d4bb041374748dd623e31bfae66079fc2d88be", + "message": "gh-3039: Resolve additive stub paths consistently (#3040)\n\nUse the same, fully qualified, paths in both the validation listener and\nthe member provider", + "timestamp": "2026-04-21T11:52:10+01:00", + "tree_id": "71a86a5422524474833245eace7e275713191e7c", + "url": "https://github.com/phpactor/phpactor/commit/32d4bb041374748dd623e31bfae66079fc2d88be" + }, + "date": 1776768830944, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.027684931506954, + "range": "± 1.44%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 164.98643835616437, + "range": "± 3.49%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.2911722113502373, + "range": "± 1.81%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.971904109588873, + "range": "± 1.08%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.027568023483365938, + "range": "± 1.47%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.02944814090019572, + "range": "± 1.08%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05055170254403134, + "range": "± 7.18%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01522144814090023, + "range": "± 6.80%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08740054794520542, + "range": "± 20.58%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.057169354207436844, + "range": "± 1.43%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.51428884540117, + "range": "± 12.21%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 532, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1371, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.17629941291609, + "range": "± 0.86%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.299285714285876, + "range": "± 0.65%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09011369863013773, + "range": "± 1.47%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.0918904109589062, + "range": "± 2.34%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.08984227005870926, + "range": "± 2.24%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.08918238747553892, + "range": "± 1.92%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09114637964774733, + "range": "± 2.41%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08949569471624323, + "range": "± 1.89%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.0892863013698634, + "range": "± 1.80%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6684410958904174, + "range": "± 1.39%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.057454207436398765, + "range": "± 3.51%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1354305283757338, + "range": "± 4.29%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13716829745596856, + "range": "± 6.89%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13046183953033264, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13106457925636, + "range": "± 6.35%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1132258, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08791389432485304, + "range": "± 5.38%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 294, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 288, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 305, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 325255.9178082192, + "range": "± 126.14%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.570391389432491, + "range": "± 3.19%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.058152641878703, + "range": "± 1.06%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16663.64383561659, + "range": "± 0.80%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 151.9192113502936, + "range": "± 4.90%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 144.6169589041106, + "range": "± 0.77%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 72629.96086105611, + "range": "± 0.54%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 116250, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6920450097847315, + "range": "± 1.49%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.0768082191780595, + "range": "± 0.64%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2129178082191685, + "range": "± 2.17%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.761, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 71614, + "range": "± 0.84%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28517.794520548003, + "range": "± 0.53%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 24700.003913894296, + "range": "± 0.86%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30003.3522504891, + "range": "± 0.67%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 815288.1682974603, + "range": "± 0.70%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9484747553816064, + "range": "± 0.71%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.3964344422700665, + "range": "± 1.62%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 160596, + "range": "± 196.87%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 114321.39726027314, + "range": "± 0.98%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 91.6199070450092, + "range": "± 1.64%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 99.3857142857132, + "range": "± 0.91%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "cweiske+github.com-2025@cweiske.de", + "name": "Christian Weiske", + "username": "cweiske" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "280ca4fbac604348ce4d9221678ea356c836eb48", + "message": "Add newline at end of .phpactor.json (#3047)\n\nThis allows us to \"cat .phpactor.json\" without indenting/breaking\nthe shell prompt.\n\nResolves: https://github.com/phpactor/phpactor/issues/3046", + "timestamp": "2026-05-14T17:58:45+01:00", + "tree_id": "9387d31381540eab6c4337a6a37562d6cc3ec038", + "url": "https://github.com/phpactor/phpactor/commit/280ca4fbac604348ce4d9221678ea356c836eb48" + }, + "date": 1778778030881, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.322675146771038, + "range": "± 12.38%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.807798434442535, + "range": "± 0.86%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.644763209393377, + "range": "± 1.33%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 153.86276908023675, + "range": "± 0.80%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.031228180039139183, + "range": "± 1.49%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03279608610567508, + "range": "± 6.30%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05430692759295405, + "range": "± 1.01%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.018489628180038867, + "range": "± 0.98%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08568735812133069, + "range": "± 1.76%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.054108062622309855, + "range": "± 1.36%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.504704109589255, + "range": "± 0.78%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 551, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1365, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 11.667307240704492, + "range": "± 0.34%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 11.858790606653486, + "range": "± 0.71%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07528082191781027, + "range": "± 1.60%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07492407045009819, + "range": "± 2.24%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07482465753424712, + "range": "± 2.95%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.0751990215264183, + "range": "± 3.31%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.0745663405088063, + "range": "± 13.57%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.0744305283757343, + "range": "± 2.61%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07545283757338646, + "range": "± 2.23%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.5963532289628122, + "range": "± 1.05%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05809041095890428, + "range": "± 2.55%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1075762, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09342465753424667, + "range": "± 6.24%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13623287671232873, + "range": "± 5.57%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13750684931506843, + "range": "± 5.89%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13824070450097842, + "range": "± 1.13%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1372544031311154, + "range": "± 4.77%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 320, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 324, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 288, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 75461.91780821918, + "range": "± 176.22%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 288081.7162426652, + "range": "± 0.56%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6265420743639825, + "range": "± 3.26%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.9170117416829897, + "range": "± 1.16%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.098313111545949, + "range": "± 1.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.717, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9197317025440304, + "range": "± 0.77%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.3351802348336557, + "range": "± 0.92%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 107493, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 15103.168297456166, + "range": "± 0.25%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 135.29490410959178, + "range": "± 0.50%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 129.23354794520677, + "range": "± 0.25%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.505565557729942, + "range": "± 21.13%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.8954696673189777, + "range": "± 1.30%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 61095.85909980555, + "range": "± 0.74%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 26102.88062622257, + "range": "± 0.39%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 22802.32876712313, + "range": "± 0.43%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 29000.727984344187, + "range": "± 0.40%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 739024.9001956973, + "range": "± 0.73%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 162355, + "range": "± 196.02%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 92.86864383561598, + "range": "± 0.45%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 100.80207729941314, + "range": "± 0.22%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 113543.88062622352, + "range": "± 0.65%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "2fa45fa34e28c96ced5653aa2ab7662c3ebff3dc", + "message": "Gh 3042: No stacking code actions / code-action concurrency (#3048)\n\n- Ensure that only one code-action resolution happens at one time and that any previous operation is cancelled...\n- ... run the action in a separate process so that it's non-blocking (and can therefore also be cancelled).", + "timestamp": "2026-05-23T07:59:20+01:00", + "tree_id": "fb63e305a412659316635a8c26576ba37e8b052f", + "url": "https://github.com/phpactor/phpactor/commit/2fa45fa34e28c96ced5653aa2ab7662c3ebff3dc" + }, + "date": 1779519645614, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 1.9689823874755312, + "range": "± 1.17%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 18.716682974559692, + "range": "± 9.91%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 8.816978473581054, + "range": "± 1.65%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 142.5283307240695, + "range": "± 0.51%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.01582739726027385, + "range": "± 1.57%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.017007240704500694, + "range": "± 1.23%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.03731545988258358, + "range": "± 1.84%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.007376438356164418, + "range": "± 2.90%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.06851346379647681, + "range": "± 0.95%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.046895107632093286, + "range": "± 0.76%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 15.567398825831711, + "range": "± 0.94%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 521, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1242, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 10.174616438355988, + "range": "± 0.83%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 10.50975146771047, + "range": "± 0.68%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07332504892367785, + "range": "± 1.46%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07355479452054807, + "range": "± 13.67%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07266849315068519, + "range": "± 2.18%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07176477495107586, + "range": "± 1.71%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07403091976516799, + "range": "± 1.59%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07299099804305209, + "range": "± 2.78%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.0723275929549909, + "range": "± 2.04%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.417425440313109, + "range": "± 1.63%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.04818904109589046, + "range": "± 11.44%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 992279, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.07512915851272016, + "range": "± 8.55%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.11147945205479448, + "range": "± 9.57%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.11146575342465746, + "range": "± 4.28%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.11381017612524451, + "range": "± 7.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.11348727984344413, + "range": "± 2.86%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 274, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 266, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 274, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 71071.38160469667, + "range": "± 175.62%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 274116.16046966705, + "range": "± 0.19%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.4049960861056698, + "range": "± 1.47%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.5971761252445895, + "range": "± 0.88%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 1.8397573385518693, + "range": "± 0.88%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 4.844, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.7971802348336479, + "range": "± 0.58%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.1701091976516662, + "range": "± 0.95%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 102623, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 14126.727984344368, + "range": "± 1.28%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 132.06802739726012, + "range": "± 0.65%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 126.03719960861135, + "range": "± 0.64%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.3178238747553626, + "range": "± 1.23%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.5918160469667697, + "range": "± 1.36%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 59851.31311154552, + "range": "± 0.17%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 24375.28767123306, + "range": "± 0.84%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 21647.966731897483, + "range": "± 0.63%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 27143.138943248385, + "range": "± 0.73%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 673126.2074364037, + "range": "± 0.23%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 138957, + "range": "± 206.36%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 77.22594814090012, + "range": "± 15.37%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 83.51342563600798, + "range": "± 0.54%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 95062.1369863002, + "range": "± 1.00%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "8b3644d038cefdd8d3e125db2f0675179c43aa89", + "message": "Update CL", + "timestamp": "2026-05-30T14:46:19+01:00", + "tree_id": "c8abbfa6859354d6cf26cde55f6cb0863f897e31", + "url": "https://github.com/phpactor/phpactor/commit/8b3644d038cefdd8d3e125db2f0675179c43aa89" + }, + "date": 1780148887442, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.257767123287536, + "range": "± 1.32%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 166.87138943248567, + "range": "± 1.73%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.358318982387449, + "range": "± 1.72%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.802704500978518, + "range": "± 7.29%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.028396203522505232, + "range": "± 1.84%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029691506849315367, + "range": "± 1.33%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05136399217221141, + "range": "± 9.74%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015642739726027435, + "range": "± 5.31%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08959021526418756, + "range": "± 1.17%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05794035225048935, + "range": "± 2.67%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.562893150684665, + "range": "± 1.04%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 567, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1354, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.423432485322838, + "range": "± 2.03%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.645187866927582, + "range": "± 2.03%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09114481409001997, + "range": "± 2.51%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09241115459882683, + "range": "± 2.05%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09223737769080055, + "range": "± 2.29%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09207475538160662, + "range": "± 1.86%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09087749510763089, + "range": "± 1.13%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.09175362035225096, + "range": "± 2.96%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.09103150684931405, + "range": "± 2.42%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6536816046966696, + "range": "± 1.38%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05663170254403166, + "range": "± 2.72%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1178959, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.14116438356164374, + "range": "± 4.32%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13963796477495102, + "range": "± 7.83%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.1337945205479452, + "range": "± 7.15%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13191976516634046, + "range": "± 1.86%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08655772994129073, + "range": "± 2.18%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 293, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 295, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 296, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 325196.2133072407, + "range": "± 127.96%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16699.181996086016, + "range": "± 0.99%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 155.25426810176359, + "range": "± 0.86%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 147.51307045009779, + "range": "± 1.00%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 75163.6418786681, + "range": "± 1.09%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.788, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.6093933463796475, + "range": "± 1.21%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.1073972602738964, + "range": "± 1.28%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 73177.09197651662, + "range": "± 0.62%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28910.13502935461, + "range": "± 1.05%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25451.205479452125, + "range": "± 1.14%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30038.448140900105, + "range": "± 0.50%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 835482.2054794456, + "range": "± 0.60%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.734035225048919, + "range": "± 1.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.087545988258334, + "range": "± 0.93%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2053816046966865, + "range": "± 0.98%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 118722, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9565354207436434, + "range": "± 0.53%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.405953424657537, + "range": "± 0.71%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 91.43060469667236, + "range": "± 0.58%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 99.16330919765171, + "range": "± 0.99%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 170505.9530332681, + "range": "± 199.60%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 114118.30919765276, + "range": "± 1.32%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "902d2f0cc0093cb80048e956abb9cf48745207b5", + "message": "Include deep-copy as a production dependency", + "timestamp": "2026-06-01T13:52:38+01:00", + "tree_id": "9848682b4802746063b5f4dca30d717084f67cc1", + "url": "https://github.com/phpactor/phpactor/commit/902d2f0cc0093cb80048e956abb9cf48745207b5" + }, + "date": 1780318466827, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 8.445473581213355, + "range": "± 2.24%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 137.96150489236916, + "range": "± 0.43%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 1.8994129158512751, + "range": "± 6.06%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 18.463260273972637, + "range": "± 1.34%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.0157720547945205, + "range": "± 2.80%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.016906692759295343, + "range": "± 2.57%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.03673780821917822, + "range": "± 5.62%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.007377651663405106, + "range": "± 2.43%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.06698164383561796, + "range": "± 0.72%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.04608176125244633, + "range": "± 1.27%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 15.112683365949149, + "range": "± 1.39%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 490, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1171, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 9.996050880625946, + "range": "± 0.99%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 10.032205479452024, + "range": "± 0.82%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07152191780822013, + "range": "± 1.27%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07056849315068489, + "range": "± 1.60%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07140508806261987, + "range": "± 1.44%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07149804305283765, + "range": "± 8.71%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.0706772994129158, + "range": "± 2.46%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07169452054794574, + "range": "± 2.12%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.06996164383561561, + "range": "± 2.73%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.41392876712329, + "range": "± 1.74%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.047751467710371934, + "range": "± 3.05%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 964143, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.10932876712328761, + "range": "± 9.23%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1067534246575342, + "range": "± 5.09%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.1032583170254403, + "range": "± 1.23%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.10631506849315064, + "range": "± 11.08%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.07426810176125238, + "range": "± 10.84%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 265, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 280, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 271, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 273690.4442270059, + "range": "± 127.32%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 13778.013698630184, + "range": "± 0.28%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 128.14741291585136, + "range": "± 1.29%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 122.80767710372025, + "range": "± 0.51%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 65141.41095890408, + "range": "± 0.72%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 4.7, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.2863718199608656, + "range": "± 1.03%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.5002485322896173, + "range": "± 0.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 58531.722113503056, + "range": "± 0.45%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 23553.579256360452, + "range": "± 0.50%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 20958.424657534226, + "range": "± 1.90%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 26738.534246575415, + "range": "± 0.34%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 667181.8258316983, + "range": "± 0.20%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.4029471624266139, + "range": "± 0.97%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.556033268101731, + "range": "± 1.12%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 1.8078414872798572, + "range": "± 0.73%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 100275, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.7752080234833698, + "range": "± 1.42%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.1365082191780729, + "range": "± 1.24%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 74.18417416829745, + "range": "± 0.77%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 80.88467808219217, + "range": "± 0.58%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 133652, + "range": "± 203.23%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 91847.10763209351, + "range": "± 0.66%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "63ed184aafd23eeac340e27884182081f4af4544", + "message": "Bump composer", + "timestamp": "2026-06-08T19:18:06+01:00", + "tree_id": "f15001989dd565a42f17d92abfd5b5f33c44f1b2", + "url": "https://github.com/phpactor/phpactor/commit/63ed184aafd23eeac340e27884182081f4af4544" + }, + "date": 1780942802793, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.470015655577198, + "range": "± 4.83%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 167.57829354207541, + "range": "± 0.79%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.3874716242661647, + "range": "± 2.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.745962818003825, + "range": "± 1.22%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.02817851272015657, + "range": "± 1.39%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029688062622309247, + "range": "± 9.58%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.0542113894324854, + "range": "± 1.27%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015705988258317073, + "range": "± 3.29%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.09346551859099876, + "range": "± 1.22%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05780767123287653, + "range": "± 2.28%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.335314285714336, + "range": "± 0.62%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 566, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1355, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.287990215263976, + "range": "± 1.53%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.49547945205477, + "range": "± 1.44%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.14427925636007627, + "range": "± 2.71%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.14565557729941286, + "range": "± 1.06%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.14664500978473596, + "range": "± 3.16%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.14822328767123244, + "range": "± 1.94%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.1494731898238745, + "range": "± 3.67%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.15053835616438263, + "range": "± 1.73%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.152057534246574, + "range": "± 0.82%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6362571428571429, + "range": "± 1.72%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05610763209393355, + "range": "± 11.30%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1150691, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1372739726027396, + "range": "± 10.74%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13719765166340497, + "range": "± 7.05%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13294716242661433, + "range": "± 6.31%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.12999804305283755, + "range": "± 9.42%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08706849315068503, + "range": "± 18.77%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 298, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 334, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 300, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 325479.3659491194, + "range": "± 147.90%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16816.569471624563, + "range": "± 1.43%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 153.56690998043084, + "range": "± 0.75%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 147.18744814090002, + "range": "± 0.25%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 73269.6379647745, + "range": "± 0.78%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.763, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.587264187866939, + "range": "± 1.68%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.0650156555773393, + "range": "± 2.17%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 71974.32681017614, + "range": "± 2.64%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28655.79843444217, + "range": "± 0.66%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25206.054794520955, + "range": "± 0.61%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30326.268101760455, + "range": "± 0.63%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 834246.0684931534, + "range": "± 0.23%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.729099804305276, + "range": "± 1.78%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.098095890410975, + "range": "± 1.74%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2409549902153056, + "range": "± 1.27%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 118336, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9724534246575494, + "range": "± 0.52%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4259606653620127, + "range": "± 1.32%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 92.70541780821912, + "range": "± 0.90%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 101.64568688845131, + "range": "± 0.50%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 166043, + "range": "± 226.88%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 115641.54011741713, + "range": "± 1.48%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "3f51172968ce51cd5f4210a0db7e009ecbd897a7", + "message": "Revert \"Bump composer\"\n\nThis reverts commit 63ed184aafd23eeac340e27884182081f4af4544.", + "timestamp": "2026-06-11T17:04:32+01:00", + "tree_id": "9848682b4802746063b5f4dca30d717084f67cc1", + "url": "https://github.com/phpactor/phpactor/commit/3f51172968ce51cd5f4210a0db7e009ecbd897a7" + }, + "date": 1781193983935, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.38375342465756, + "range": "± 2.82%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.58196086105681, + "range": "± 0.76%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.715127201565618, + "range": "± 1.65%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 155.61467123287707, + "range": "± 0.82%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.030885831702544074, + "range": "± 1.28%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.032358082191780956, + "range": "± 1.05%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.054339452054794436, + "range": "± 1.99%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01860583170254412, + "range": "± 2.18%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.085620000000001, + "range": "± 0.82%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05491710371819961, + "range": "± 7.07%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.40497260273982, + "range": "± 1.43%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 603, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1389, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 11.977506849315391, + "range": "± 1.07%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.124994129158488, + "range": "± 9.09%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07615988258317086, + "range": "± 2.33%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07729471624266028, + "range": "± 3.89%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07605088062622499, + "range": "± 2.20%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07594481409001884, + "range": "± 2.84%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.0770569471624254, + "range": "± 1.34%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07722426614481301, + "range": "± 2.81%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07509628180039016, + "range": "± 2.14%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.5765504892367852, + "range": "± 1.41%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05928532289628191, + "range": "± 9.34%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1106878, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13811350293542068, + "range": "± 9.40%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.14221917808219167, + "range": "± 8.83%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13517221135029342, + "range": "± 2.59%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13293150684931504, + "range": "± 0.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09423874755381614, + "range": "± 10.19%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 293, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 298, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 313, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9258747553816061, + "range": "± 1.39%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.390265362035277, + "range": "± 0.76%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.884, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 110670, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 61387.24070450058, + "range": "± 1.09%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 26352.62230919783, + "range": "± 0.46%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 23147.019569471526, + "range": "± 1.03%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 29589.003913894318, + "range": "± 8.96%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 736329.8806262165, + "range": "± 0.78%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 15157.722113502889, + "range": "± 2.79%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 140.85323287671233, + "range": "± 159.23%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 130.67705479451922, + "range": "± 0.66%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 292578.7984344419, + "range": "± 0.77%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.530904109589056, + "range": "± 1.22%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.924383561643849, + "range": "± 2.17%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 72534.74363992245, + "range": "± 0.85%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6394774951076374, + "range": "± 1.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.982878669275928, + "range": "± 1.20%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.1422641878669557, + "range": "± 1.32%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 167043, + "range": "± 202.10%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 97.9244598825858, + "range": "± 0.51%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 104.39433855185914, + "range": "± 3.12%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 118124.37769080214, + "range": "± 0.58%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "4f082b6b734af63286c41ed6e7e73dbde3cbc3b4", + "message": "Do not show client warning when code action process is killed (#3051)\n\n* Do not show client warning when code action process is killed\n\n* Update changelog", + "timestamp": "2026-06-11T17:18:30+01:00", + "tree_id": "9ac559e21eae46137f1a163ff8758ac1189f4a9e", + "url": "https://github.com/phpactor/phpactor/commit/4f082b6b734af63286c41ed6e7e73dbde3cbc3b4" + }, + "date": 1781194811249, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.2942152641878955, + "range": "± 1.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.655708414872816, + "range": "± 2.19%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.059900195694683, + "range": "± 1.24%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 163.9765068493151, + "range": "± 1.53%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.02766273972602738, + "range": "± 2.44%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.02919941291585135, + "range": "± 1.93%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05087174168297426, + "range": "± 1.00%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015392211350293613, + "range": "± 2.32%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08844606653620378, + "range": "± 1.67%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05759123287671299, + "range": "± 1.44%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.307652054794545, + "range": "± 3.09%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 602, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1379, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.336706457925814, + "range": "± 1.16%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.24473385518597, + "range": "± 1.02%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09167318982387669, + "range": "± 1.13%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09177455968688934, + "range": "± 2.17%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09146340508806236, + "range": "± 6.86%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09257808219178071, + "range": "± 2.52%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09056301369863005, + "range": "± 2.93%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08954266144814005, + "range": "± 1.22%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.08929393346379642, + "range": "± 1.59%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.640831506849316, + "range": "± 5.82%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.058904696673189864, + "range": "± 7.04%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1132596, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1376399217221134, + "range": "± 13.18%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13804305283757332, + "range": "± 7.12%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13200587084148718, + "range": "± 1.27%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.15585518590997977, + "range": "± 10.38%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08735812133072397, + "range": "± 11.42%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 333, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 303, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 292, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 1.0120876712328732, + "range": "± 8.31%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.407212133072408, + "range": "± 1.00%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.826, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 118294, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 72050.13894324847, + "range": "± 0.55%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28552.849315068335, + "range": "± 0.60%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25078.83953033245, + "range": "± 0.83%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30130.87671232852, + "range": "± 0.61%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 817690.6340508802, + "range": "± 0.62%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16812.10958904054, + "range": "± 0.64%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 156.5851917808219, + "range": "± 156.99%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 144.99236594911807, + "range": "± 0.47%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 317521.6614481397, + "range": "± 1.17%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.593904109589056, + "range": "± 1.31%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.1000645792563364, + "range": "± 1.53%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 73569.79452054751, + "range": "± 0.59%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7475909980430733, + "range": "± 1.57%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.141346379647737, + "range": "± 1.28%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2022818003913525, + "range": "± 1.43%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 162897, + "range": "± 199.76%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 92.38544618395491, + "range": "± 1.01%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 98.89009197651717, + "range": "± 0.30%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 113993.03326810288, + "range": "± 0.61%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "Marjo.vanLier@gmail.com", + "name": "Marjo", + "username": "MarjovanLier" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "1b0834ed333f4e4cce0c74e9e71a06a3ff44d72b", + "message": "feat(mago): Add Mago diagnostics integration (#3052)\n\nSurface diagnostics from the Mago toolchain (a Rust PHP linter,\nformatter and static analysis tool) in the language server, giving\nusers a fast alternative to the existing PHPStan and Psalm\nintegrations.\n\nTwo providers are registered as an optional extension: \"mago\" runs\n\"mago analyze\" for static analysis and \"mago-lint\" runs \"mago lint\"\nfor style and code smells. The current buffer is streamed to Mago on\nstdin so diagnostics update as you type, and the JSON report is mapped\nto LSP diagnostics with precise byte-offset ranges and related\ninformation for secondary spans.\n\nThe extension is disabled by default. When enabled, analysis is on and\nthe linter is opt-in. A suggestor offers to enable it when the\ncarthage-software/mago Composer package is present.\n\nSigned-off-by: Marjo Wenzel van Lier ", + "timestamp": "2026-06-25T08:52:20+01:00", + "tree_id": "da9069554949c96b393230b3bd6a42035e2582fe", + "url": "https://github.com/phpactor/phpactor/commit/1b0834ed333f4e4cce0c74e9e71a06a3ff44d72b" + }, + "date": 1782374045041, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.4110665362035215, + "range": "± 2.92%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.993299412915203, + "range": "± 0.55%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.030673189823828, + "range": "± 0.90%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 158.17959491193685, + "range": "± 1.06%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.031236203522505116, + "range": "± 1.10%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03253502935420777, + "range": "± 1.17%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.054747279843443804, + "range": "± 0.92%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01845033268101729, + "range": "± 1.25%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08681514677103727, + "range": "± 2.15%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05668003913894324, + "range": "± 6.67%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.93917181996091, + "range": "± 4.60%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 643, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1556, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.313614481408978, + "range": "± 3.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.452493150684967, + "range": "± 0.93%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07693150684931573, + "range": "± 2.11%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07793698630136874, + "range": "± 3.38%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07685401174168224, + "range": "± 2.69%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07712837573385603, + "range": "± 2.18%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07470939334637962, + "range": "± 2.24%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07577221135029413, + "range": "± 2.53%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07624579256360017, + "range": "± 1.46%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.5976232876712455, + "range": "± 1.05%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05913561643835601, + "range": "± 8.74%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1121963, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.14242661448140886, + "range": "± 5.48%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1405479452054794, + "range": "± 4.85%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13709980430528368, + "range": "± 8.05%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13585127201565553, + "range": "± 2.50%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09280821917808205, + "range": "± 9.93%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 307, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 304, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 312, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9468119373776904, + "range": "± 0.67%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.376963013698615, + "range": "± 0.73%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.916, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 111189, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 62583.01761252482, + "range": "± 0.99%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 26540.342465753478, + "range": "± 0.96%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 23276.835616438388, + "range": "± 0.77%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 29736.373776908076, + "range": "± 4.07%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 755087.0215264314, + "range": "± 0.78%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 15553.358121330599, + "range": "± 1.04%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 144.5134481409002, + "range": "± 158.34%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 131.9270273972598, + "range": "± 0.95%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 295331.9941291603, + "range": "± 0.54%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5178219178082277, + "range": "± 1.51%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.9676164383561705, + "range": "± 1.58%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 76437.44031311154, + "range": "± 0.72%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6674168297455696, + "range": "± 1.18%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.967682974559695, + "range": "± 1.12%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.1325205479451674, + "range": "± 1.07%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 167536, + "range": "± 205.02%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 97.58942465753404, + "range": "± 0.47%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 106.50963894324883, + "range": "± 0.67%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 119114.90802348354, + "range": "± 3.66%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "be959c4e1b9f3dcefeca0b5971cbcaddff2df3e0", + "message": "Bump CL", + "timestamp": "2026-06-26T22:40:56+01:00", + "tree_id": "6c0f9cc342b1a2984b93c6f9af6334cdf8e5a45a", + "url": "https://github.com/phpactor/phpactor/commit/be959c4e1b9f3dcefeca0b5971cbcaddff2df3e0" + }, + "date": 1782510161568, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.2920117416829813, + "range": "± 1.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.30415264187848, + "range": "± 1.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.082225048923691, + "range": "± 1.52%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 165.584506849315, + "range": "± 1.32%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.027727671232876668, + "range": "± 7.69%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029059060665361812, + "range": "± 1.35%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.0508528375733855, + "range": "± 1.08%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01531506849315079, + "range": "± 1.62%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08874109589040982, + "range": "± 1.29%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05847569471624235, + "range": "± 1.01%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.26415068493132, + "range": "± 0.58%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 545, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1406, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.145727984344427, + "range": "± 0.69%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.231293542074294, + "range": "± 0.69%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.08930900195694559, + "range": "± 2.19%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.08910880626223042, + "range": "± 3.58%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.0887217221135018, + "range": "± 1.40%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.08874794520547863, + "range": "± 2.27%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.088483561643836, + "range": "± 0.99%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08894442270058453, + "range": "± 2.20%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.08723542074363984, + "range": "± 4.08%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6088158512720203, + "range": "± 1.48%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05656301369862927, + "range": "± 2.54%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1140236, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13756164383561634, + "range": "± 5.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1372700587084148, + "range": "± 10.39%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13394520547945193, + "range": "± 6.77%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13036790606653614, + "range": "± 2.10%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08479843444226998, + "range": "± 6.10%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 299, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 293, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 294, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9539796477495088, + "range": "± 0.94%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.41422407045011, + "range": "± 0.33%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 6.412, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 119884, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 72485.75146771136, + "range": "± 0.71%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28828.75146771086, + "range": "± 1.01%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25143.547945205482, + "range": "± 0.61%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30034.074363992673, + "range": "± 0.35%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 827219.4990215079, + "range": "± 0.74%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16634.42465753432, + "range": "± 0.79%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 157.08815851272016, + "range": "± 156.83%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 144.74752641878584, + "range": "± 0.51%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 317101.4520547934, + "range": "± 1.46%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5584833659491324, + "range": "± 1.13%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.0126066536203253, + "range": "± 0.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 74324.49119373773, + "range": "± 1.00%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6845107632093759, + "range": "± 1.47%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.061162426614489, + "range": "± 1.57%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.1960489236790033, + "range": "± 1.68%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 165082.78669275928, + "range": "± 197.26%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 89.25950880626341, + "range": "± 1.33%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 95.54915655577337, + "range": "± 0.90%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 112370.17416829779, + "range": "± 0.79%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "hello@pietro.camp", + "name": "Pietro Campagnano", + "username": "fain182" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "3a98f8a111b9009cae3a78c9d5fbf013f8b23954", + "message": "Require posix extension and fail early when required extensions are missing (#3054)\n\nThe language server crashed mid-session with \"Call to undefined function\nposix_kill()\" on systems without the posix extension, because ext-posix was\nnot declared in composer.json and PHAR distributions bypass Composer's\nplatform checks.\n\n- Declare ext-posix in composer.json so Composer installations fail early\n- Check required extensions on startup in bin/phpactor and exit with a\n descriptive error instead of crashing during an LSP session\n\nFixes phpactor/phpactor#3053\n\n\nClaude-Session: https://claude.ai/code/session_014Lo55A9LG5DguN2KG792aj\n\nCo-authored-by: Claude ", + "timestamp": "2026-07-05T18:28:54+01:00", + "tree_id": "88b6670895c02b07e8e0bbc016618c814586a859", + "url": "https://github.com/phpactor/phpactor/commit/3a98f8a111b9009cae3a78c9d5fbf013f8b23954" + }, + "date": 1783272641956, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.3165322896281837, + "range": "± 5.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 21.974996086105666, + "range": "± 0.91%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.517739726027376, + "range": "± 1.14%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 152.35109393346187, + "range": "± 0.68%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.030767475538160692, + "range": "± 1.07%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03256555772994134, + "range": "± 2.81%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05443589041095884, + "range": "± 3.06%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01854270058708425, + "range": "± 1.14%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08543365949119384, + "range": "± 3.19%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05501941291585158, + "range": "± 1.12%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.059828571428536, + "range": "± 0.89%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 566, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1452, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.342465753424527, + "range": "± 1.82%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 11.836630136986365, + "range": "± 1.19%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07576516634050943, + "range": "± 3.00%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07561565557729873, + "range": "± 1.73%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07715322896281678, + "range": "± 1.58%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07542407045009755, + "range": "± 1.12%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07551506849315019, + "range": "± 7.63%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.0767054794520559, + "range": "± 1.90%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07494285714285827, + "range": "± 2.66%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.5537287671232953, + "range": "± 1.59%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.061711937377691144, + "range": "± 2.36%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1075723, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1400958904109587, + "range": "± 18.72%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1341506849315068, + "range": "± 1.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13111350293542068, + "range": "± 1.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1309667318982387, + "range": "± 4.77%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09116242661448158, + "range": "± 3.96%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 285, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 284, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 296, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9043835616438349, + "range": "± 0.79%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.32688493150685, + "range": "± 4.09%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.501, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 107567, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 59996.109589041385, + "range": "± 0.99%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 25856.09197651666, + "range": "± 1.43%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 23002.15655577337, + "range": "± 0.71%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 29074.569471624236, + "range": "± 0.33%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 739881.1311154747, + "range": "± 1.18%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 14957.898238747504, + "range": "± 0.66%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 139.63541291585128, + "range": "± 158.43%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 129.0475616438355, + "range": "± 1.25%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 291936.28571429045, + "range": "± 0.89%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5237964774951487, + "range": "± 1.26%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.9308649706457746, + "range": "± 1.73%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 72378.93542074374, + "range": "± 7.53%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6185479452054787, + "range": "± 0.95%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.9079373776908177, + "range": "± 1.88%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.0662035225048836, + "range": "± 2.07%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 164084, + "range": "± 201.77%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 96.92652446184144, + "range": "± 1.05%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 103.84588943248401, + "range": "± 1.38%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 115437.33072406985, + "range": "± 1.24%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "0ba242dce76ebcb7df93dfa39d0d4a1fb05dabbb", + "message": "Add disclaimer", + "timestamp": "2026-07-16T22:27:21+01:00", + "tree_id": "145e102b37b54b769a8d53c85acc43eee2310038", + "url": "https://github.com/phpactor/phpactor/commit/0ba242dce76ebcb7df93dfa39d0d4a1fb05dabbb" + }, + "date": 1784237352742, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.807788649706483, + "range": "± 6.45%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 152.3831898238753, + "range": "± 0.64%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.34541095890409, + "range": "± 3.27%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.32214872798452, + "range": "± 0.78%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.031267788649706446, + "range": "± 4.66%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03279256360078228, + "range": "± 1.39%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.055573581213307494, + "range": "± 2.33%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.018519178082191893, + "range": "± 1.32%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08780399217221058, + "range": "± 1.64%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05647812133072408, + "range": "± 4.90%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.762213307240653, + "range": "± 0.65%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 641, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1454, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 11.86464383561647, + "range": "± 2.69%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.949, + "range": "± 1.12%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07602720156555613, + "range": "± 1.99%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07895694716242524, + "range": "± 4.01%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07593679060665427, + "range": "± 2.53%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07700587084148716, + "range": "± 3.52%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07868962818003954, + "range": "± 2.42%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.0792808219178085, + "range": "± 4.03%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07722015655577291, + "range": "± 2.08%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.5547485322896244, + "range": "± 1.82%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05809706457925636, + "range": "± 7.93%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1112973, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13936007827788632, + "range": "± 11.48%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1482739726027396, + "range": "± 16.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.15324657534246516, + "range": "± 6.42%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13128767123287668, + "range": "± 3.62%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09119178082191795, + "range": "± 8.46%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 290, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 313, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 353, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9206473581213317, + "range": "± 4.27%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.3318622309197627, + "range": "± 0.43%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 15132.849315068466, + "range": "± 1.40%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 139.4257553816047, + "range": "± 159.75%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 128.8576575342469, + "range": "± 0.77%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 60913.21135029393, + "range": "± 0.82%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 27302.800391389042, + "range": "± 2.09%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 22845.681017612485, + "range": "± 1.93%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30550.146771036554, + "range": "± 1.85%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 739320.5342465728, + "range": "± 0.99%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.985, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6398297455968822, + "range": "± 2.20%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.971138943248531, + "range": "± 1.83%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.1252974559687137, + "range": "± 2.94%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 76520.76320939271, + "range": "± 0.55%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5442074363992164, + "range": "± 1.34%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.908240704500997, + "range": "± 2.62%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 290932.6477495176, + "range": "± 0.36%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 113827, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 115264.32289628158, + "range": "± 3.15%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 169630.72211350294, + "range": "± 195.38%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 97.90610371820003, + "range": "± 2.62%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 104.19043933463679, + "range": "± 1.46%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "13ae9f1ca35b4bb0d35640c1989959341d593bd6", + "message": "Up", + "timestamp": "2026-07-16T22:28:06+01:00", + "tree_id": "8b21e135ac88f4c56a3c1fa74a6ef7168b1d71a9", + "url": "https://github.com/phpactor/phpactor/commit/13ae9f1ca35b4bb0d35640c1989959341d593bd6" + }, + "date": 1784237391843, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.59809784735812, + "range": "± 2.78%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 152.29793737769063, + "range": "± 0.65%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.3517534246575633, + "range": "± 1.84%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 21.92788258317004, + "range": "± 0.69%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.031065205479451997, + "range": "± 3.01%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03268273972602718, + "range": "± 1.11%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05424626223091982, + "range": "± 1.17%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01859178082191751, + "range": "± 1.84%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08520778864970614, + "range": "± 1.26%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.055268180039139664, + "range": "± 1.02%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 17.999845401174312, + "range": "± 0.65%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 564, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1470, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 11.665424657534336, + "range": "± 2.61%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 11.770054794520576, + "range": "± 1.40%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07613659491193829, + "range": "± 2.48%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07490136986301363, + "range": "± 1.44%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.0749943248532289, + "range": "± 21.77%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.0763250489236813, + "range": "± 2.29%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07435988258317024, + "range": "± 1.84%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07573581213307229, + "range": "± 2.25%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07426908023483349, + "range": "± 12.79%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.555475538160473, + "range": "± 5.35%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05875479452054777, + "range": "± 4.89%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1057080, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13589628180039132, + "range": "± 7.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13494129158512716, + "range": "± 3.38%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.12908219178082184, + "range": "± 4.39%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1301154598825831, + "range": "± 4.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09143052837573372, + "range": "± 11.96%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 316, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 288, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 295, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9019195694716221, + "range": "± 0.62%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.3139933463796516, + "range": "± 0.46%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 14818.240704500991, + "range": "± 1.09%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 137.8949373776908, + "range": "± 159.27%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 128.37440704501145, + "range": "± 0.31%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 60321.52641878533, + "range": "± 0.68%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 25862.424657534015, + "range": "± 0.64%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 22967.260273972097, + "range": "± 0.68%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 28920.44422700595, + "range": "± 0.81%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 725500.3561643853, + "range": "± 0.61%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.429, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.602397260273974, + "range": "± 2.30%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.8882524461839596, + "range": "± 0.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.066600782778866, + "range": "± 1.32%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 72332.67710371842, + "range": "± 0.47%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.4795596868884533, + "range": "± 1.63%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.869671232876744, + "range": "± 1.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 285832.9178082163, + "range": "± 0.19%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 106893, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 113244.87671232982, + "range": "± 0.57%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 161597, + "range": "± 196.30%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 91.74155968688859, + "range": "± 0.54%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 99.8837641878667, + "range": "± 0.26%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "3d9ca615c64df01df2e96b3c01b7ad55fff3d4db", + "message": "YMMV", + "timestamp": "2026-07-16T22:29:37+01:00", + "tree_id": "fe51bc35a417082e813da2a8bbc4c27a3fdd4ca2", + "url": "https://github.com/phpactor/phpactor/commit/3d9ca615c64df01df2e96b3c01b7ad55fff3d4db" + }, + "date": 1784237506240, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.54060078277884, + "range": "± 2.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 168.76145792563509, + "range": "± 0.84%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.4912152641878658, + "range": "± 2.06%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 23.192311154598734, + "range": "± 1.15%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.028458082191780764, + "range": "± 1.63%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.030160039138943287, + "range": "± 3.50%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.052535968688846094, + "range": "± 1.48%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015612172211350252, + "range": "± 2.29%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.0929489628180033, + "range": "± 1.70%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.058555616438355634, + "range": "± 1.27%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.6586794520549, + "range": "± 2.31%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 613, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1444, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 13.076861056751527, + "range": "± 3.62%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 13.367571428571434, + "range": "± 25.39%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09487964774951219, + "range": "± 1.95%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09278884540117414, + "range": "± 1.74%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09518825831702601, + "range": "± 1.59%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09323581213307304, + "range": "± 3.70%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09301369863013689, + "range": "± 20.08%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.09219569471624382, + "range": "± 2.99%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.0924931506849316, + "range": "± 7.47%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6261491193737703, + "range": "± 1.70%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.056698434442270185, + "range": "± 2.70%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1203789, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.14495107632093915, + "range": "± 11.64%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1467201565557728, + "range": "± 5.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.14058904109589027, + "range": "± 8.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13894520547945194, + "range": "± 10.19%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.086700587084149, + "range": "± 4.36%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 365, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 317, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 322, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9914702544031404, + "range": "± 1.32%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4968913894324731, + "range": "± 1.49%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17094.264187866847, + "range": "± 1.49%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 164.36882583170257, + "range": "± 156.80%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 147.67078473581165, + "range": "± 0.66%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 73015.211350294, + "range": "± 1.36%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28893.68493150676, + "range": "± 1.66%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25357.536203522664, + "range": "± 0.28%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 31357.444227006374, + "range": "± 0.84%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 846090.315068488, + "range": "± 1.02%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 6.047, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.798970645792585, + "range": "± 1.79%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.1808512720156576, + "range": "± 2.07%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.316397260273983, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 78996.19960861074, + "range": "± 1.04%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.6444951076320973, + "range": "± 2.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.13954207436398, + "range": "± 1.63%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 323187.2954990186, + "range": "± 0.71%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 123407, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 119961.71428571377, + "range": "± 4.72%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 178170.7964774951, + "range": "± 202.16%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 98.80685420743656, + "range": "± 0.57%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 109.33638747553633, + "range": "± 1.75%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "c8e06992eb8bc55993e0e8bf04b1451190895061", + "message": "Updat README", + "timestamp": "2026-07-16T22:32:32+01:00", + "tree_id": "858ad4306124f71138b3a37c757a3b39b7349ca1", + "url": "https://github.com/phpactor/phpactor/commit/c8e06992eb8bc55993e0e8bf04b1451190895061" + }, + "date": 1784237662178, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.590178082191859, + "range": "± 1.70%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 151.78755772994296, + "range": "± 0.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.2653502935420766, + "range": "± 1.62%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.0360156555771, + "range": "± 0.85%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.031046849315068323, + "range": "± 1.38%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.0324877103718197, + "range": "± 1.54%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.054175499021525866, + "range": "± 0.83%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.018612720156555438, + "range": "± 1.03%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08537534246575439, + "range": "± 1.10%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05498966731898228, + "range": "± 3.95%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.017198043052897, + "range": "± 0.43%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 571, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1503, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 11.530722113503007, + "range": "± 0.84%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 11.721238747553732, + "range": "± 0.50%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07548904109589054, + "range": "± 11.24%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07514344422700547, + "range": "± 5.89%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07413346379647644, + "range": "± 1.28%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07416086105675258, + "range": "± 2.09%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07539275929550115, + "range": "± 1.65%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07635499021526405, + "range": "± 2.51%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.0747653620352258, + "range": "± 1.40%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.5488246575342504, + "range": "± 1.29%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.058573972602739756, + "range": "± 9.22%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1129508, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.14418982387475524, + "range": "± 14.81%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1382876712328766, + "range": "± 13.71%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.1332348336594911, + "range": "± 3.88%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1362935420743638, + "range": "± 4.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09204305283757329, + "range": "± 9.06%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 378, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 320, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 334, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9650195694716184, + "range": "± 1.25%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.408073972602737, + "range": "± 1.73%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 15721.82191780808, + "range": "± 1.65%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 139.85716438356164, + "range": "± 159.01%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 129.01768493150703, + "range": "± 0.54%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 60622.123287671464, + "range": "± 0.54%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 25844.135029354227, + "range": "± 0.95%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 22812.729941291764, + "range": "± 0.31%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 29090.42465753445, + "range": "± 0.43%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 735192.8121330787, + "range": "± 0.56%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.529, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6293248532289617, + "range": "± 2.34%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.9140802348336545, + "range": "± 0.64%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.0982093933464117, + "range": "± 1.49%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 72652.45596868917, + "range": "± 0.75%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.517475538160468, + "range": "± 1.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.994405088062652, + "range": "± 1.60%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 291459.60273972474, + "range": "± 0.40%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 110196, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 113900.59491193743, + "range": "± 1.11%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 162697, + "range": "± 197.93%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 95.78158317025458, + "range": "± 1.44%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 104.49149021526412, + "range": "± 0.39%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "anders@jenbo.dk", + "name": "Anders Jenbo", + "username": "AJenbo" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "cb40f25bed7cdd99d3c22d0652d7900f1563d8a9", + "message": "Code action to add #[Override] (#3056)", + "timestamp": "2026-07-21T08:46:01+01:00", + "tree_id": "2d04764ee807c70f102331c659b229cf0d148e26", + "url": "https://github.com/phpactor/phpactor/commit/cb40f25bed7cdd99d3c22d0652d7900f1563d8a9" + }, + "date": 1784620056911, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.49085714285705, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 148.11889823874972, + "range": "± 0.86%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.1563424657534247, + "range": "± 24.87%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 19.32651076320909, + "range": "± 0.61%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.01611217221135028, + "range": "± 2.56%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.01751185909980411, + "range": "± 1.60%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.03808559686888406, + "range": "± 1.49%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.007475225048923644, + "range": "± 2.59%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.07070418786692763, + "range": "± 1.36%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.049468180039138984, + "range": "± 3.05%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 16.373804305283798, + "range": "± 0.92%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 545, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1312, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 10.741013698630157, + "range": "± 7.42%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 10.812301369862864, + "range": "± 1.38%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07399804305283662, + "range": "± 2.76%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.0736301369863018, + "range": "± 3.56%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07390136986301363, + "range": "± 1.57%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07308493150684904, + "range": "± 2.59%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07447455968688957, + "range": "± 1.70%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.0730136986301384, + "range": "± 1.22%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07378747553816006, + "range": "± 2.00%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.3964600782778889, + "range": "± 1.56%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.04877632093933414, + "range": "± 2.73%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1051131, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.12005870841487262, + "range": "± 5.05%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.119665362035225, + "range": "± 5.46%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.12067123287671211, + "range": "± 8.94%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.12123483365949105, + "range": "± 5.79%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.07519373776908049, + "range": "± 7.61%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 285, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 299, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 286, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.8378700587084239, + "range": "± 0.86%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.2458911937377686, + "range": "± 1.15%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 14802.908023483398, + "range": "± 0.82%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 141.4456927592955, + "range": "± 156.68%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 132.85969080234852, + "range": "± 1.39%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 61144.52837573382, + "range": "± 0.63%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 24874.305283757378, + "range": "± 1.07%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 22136.868884540087, + "range": "± 0.74%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 27808.745596868273, + "range": "± 0.26%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 690800.0547945185, + "range": "± 0.61%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.002, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.4683835616438279, + "range": "± 2.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.6617827788649593, + "range": "± 1.60%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 1.8935107632093784, + "range": "± 1.32%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 72823.64774951147, + "range": "± 1.04%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.3560919765166406, + "range": "± 1.21%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.6488610567515063, + "range": "± 1.49%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 285972.3972602791, + "range": "± 0.75%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 109860, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 106969.20939334668, + "range": "± 1.54%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 150477, + "range": "± 207.82%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 85.84050978473597, + "range": "± 0.52%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 93.2902583170284, + "range": "± 0.93%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "781aa80fd281bfa0c1d4aa973723f68e84c6e6c5", + "message": "Update CL", + "timestamp": "2026-07-21T08:47:54+01:00", + "tree_id": "ac6b3041dac6b1d9de8d92cb166d7d702aeb58bd", + "url": "https://github.com/phpactor/phpactor/commit/781aa80fd281bfa0c1d4aa973723f68e84c6e6c5" + }, + "date": 1784620175897, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.962835616438271, + "range": "± 2.76%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 162.53335616438358, + "range": "± 1.31%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.2644070450097584, + "range": "± 1.76%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.46880039138911, + "range": "± 0.82%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.02794767123287644, + "range": "± 1.94%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029855225048923458, + "range": "± 1.79%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.0510071232876721, + "range": "± 1.31%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015402583170254499, + "range": "± 2.62%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.0884295890410952, + "range": "± 1.49%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05808262230919697, + "range": "± 1.49%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.142214481409457, + "range": "± 0.55%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 538, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1394, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.121504892367943, + "range": "± 1.78%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.287655577299377, + "range": "± 2.97%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09057142857142864, + "range": "± 6.70%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.08876868884540166, + "range": "± 2.67%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.08966947162426608, + "range": "± 7.15%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.08904970645792501, + "range": "± 0.97%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.08905851272015647, + "range": "± 1.75%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08927906066536198, + "range": "± 1.73%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.08930489236790512, + "range": "± 1.95%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.628057534246574, + "range": "± 11.27%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.06180958904109533, + "range": "± 4.19%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1130089, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13879452054794514, + "range": "± 6.77%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1370136986301369, + "range": "± 7.40%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13149706457925628, + "range": "± 6.34%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13219178082191774, + "range": "± 17.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08532876712328787, + "range": "± 11.08%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 301, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 296, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 329, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9572150684931515, + "range": "± 1.03%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4110037181995856, + "range": "± 1.33%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16666.30136986327, + "range": "± 1.14%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 162.39160469667317, + "range": "± 155.80%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 144.29274755381653, + "range": "± 0.54%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 72459.0058708414, + "range": "± 0.52%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28571.1506849315, + "range": "± 1.46%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25620.47749510732, + "range": "± 0.86%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30398.84344422709, + "range": "± 0.27%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 822448.0000000042, + "range": "± 0.59%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.635, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.6828356164383456, + "range": "± 1.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.0981487279843476, + "range": "± 5.69%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.166563600782787, + "range": "± 1.40%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 74290.63209393554, + "range": "± 0.16%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5877945205479447, + "range": "± 0.82%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.0214011741682696, + "range": "± 0.98%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 314577.32485322736, + "range": "± 0.55%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 116376, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 114999.55577299185, + "range": "± 1.35%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 164659, + "range": "± 197.32%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 92.29509393346481, + "range": "± 0.49%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 101.35176027397375, + "range": "± 1.01%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "b327eec2cc4b7802b6ca0fcdfdacc2a812368b73", + "message": "gh-3058: Handle prematurely cancelled code action request (#3059)\n\nImplement cancellation for outsoutced diagnostics\n\nRest the workspace prior to using it", + "timestamp": "2026-07-21T23:20:54+01:00", + "tree_id": "e2693e49eabe46ab0c8f45ccf4ecd682d31c2dc9", + "url": "https://github.com/phpactor/phpactor/commit/b327eec2cc4b7802b6ca0fcdfdacc2a812368b73" + }, + "date": 1784672566459, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 8.922068493150691, + "range": "± 1.14%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 140.17344227005938, + "range": "± 0.76%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.090275929549914, + "range": "± 2.14%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 18.636771037182136, + "range": "± 0.58%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.015549001956947087, + "range": "± 2.85%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.016729393346379677, + "range": "± 3.47%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.03778630136986302, + "range": "± 2.08%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.007483600782778811, + "range": "± 3.67%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.07000164383561619, + "range": "± 3.28%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.04762160469667285, + "range": "± 3.16%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 14.711305675146763, + "range": "± 2.90%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 574, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1232, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 10.53556947162421, + "range": "± 4.78%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 10.519180039138796, + "range": "± 2.04%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07022857142857176, + "range": "± 4.02%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.06893111545988347, + "range": "± 2.22%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07085909980430576, + "range": "± 2.89%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.06991898238747515, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07126438356164304, + "range": "± 2.53%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.0712612524461839, + "range": "± 1.67%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.0688863013698626, + "range": "± 3.56%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.3515103718199626, + "range": "± 3.97%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.045326614481408685, + "range": "± 3.47%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 957775, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.11351663405088044, + "range": "± 7.10%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.11374755381604688, + "range": "± 6.43%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.11456555772994113, + "range": "± 4.04%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1168003913894324, + "range": "± 6.48%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.06965557729941318, + "range": "± 6.20%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 312, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 327, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 299, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.8313573385518694, + "range": "± 1.44%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.2639622309197458, + "range": "± 2.08%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 14028.851272015589, + "range": "± 0.63%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 128.46775342465753, + "range": "± 157.94%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 121.21329354207498, + "range": "± 0.54%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 61339.02935420636, + "range": "± 0.84%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 24344.15264187893, + "range": "± 0.66%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 21745.283757338555, + "range": "± 0.44%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 26833.199608610375, + "range": "± 0.60%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 665625.992172211, + "range": "± 0.27%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 4.883, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.449804305283762, + "range": "± 4.17%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.733878669275935, + "range": "± 1.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 1.9091565557730292, + "range": "± 0.95%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 64767.25831702576, + "range": "± 0.53%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.3361937377690742, + "range": "± 1.21%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.6506164383562014, + "range": "± 1.44%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 266429.6712328758, + "range": "± 0.43%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 99349, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 102418.57142856886, + "range": "± 0.91%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 146163, + "range": "± 199.32%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 82.93996966731804, + "range": "± 0.71%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 89.55038160469663, + "range": "± 0.64%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "1b3b0f3abfce8771d7cbc04eb73bf89294329994", + "message": "Bump CL", + "timestamp": "2026-07-22T21:41:58+01:00", + "tree_id": "ff0da3ad9c0f4189862b63be724ac7dd3d1621a4", + "url": "https://github.com/phpactor/phpactor/commit/1b3b0f3abfce8771d7cbc04eb73bf89294329994" + }, + "date": 1784753052575, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.315103718199614, + "range": "± 2.03%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 165.00428767123282, + "range": "± 0.85%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.326956947162407, + "range": "± 1.10%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.476228962817814, + "range": "± 1.25%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.028236908023483342, + "range": "± 8.50%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029841095890410962, + "range": "± 1.64%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05093909980430554, + "range": "± 1.71%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015552876712328887, + "range": "± 2.75%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08947675146771014, + "range": "± 1.11%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.058539138943248505, + "range": "± 1.77%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.108851663405034, + "range": "± 0.64%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 593, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1443, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.296219178082145, + "range": "± 0.90%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.528835616438366, + "range": "± 6.65%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09200039138943301, + "range": "± 2.61%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09140939334637915, + "range": "± 2.28%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09172622309197727, + "range": "± 1.49%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09084285714285768, + "range": "± 2.31%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09008219178082083, + "range": "± 2.73%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08893718199608648, + "range": "± 3.42%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.09092876712328805, + "range": "± 3.75%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.64313972602741, + "range": "± 1.43%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.0645657534246573, + "range": "± 4.70%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1150325, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.1403718199608609, + "range": "± 8.76%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13695499021526414, + "range": "± 0.72%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13381996086105669, + "range": "± 6.32%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13238356164383555, + "range": "± 3.21%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.0987514677103722, + "range": "± 10.57%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 320, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 308, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 298, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9706943248532276, + "range": "± 0.42%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4774499021525964, + "range": "± 1.05%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16886.956947161758, + "range": "± 0.74%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 155.42415264187866, + "range": "± 156.92%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 145.5731819960876, + "range": "± 0.78%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 71938.38356164341, + "range": "± 0.51%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28650.553816047053, + "range": "± 0.94%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25403.050880626328, + "range": "± 1.00%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30331.545988258353, + "range": "± 1.60%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 832979.4285714325, + "range": "± 0.48%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.784, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7603483365949495, + "range": "± 1.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.0660567514677086, + "range": "± 1.60%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2524031311154165, + "range": "± 1.45%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 76208.64970645921, + "range": "± 0.59%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.6160078277886438, + "range": "± 2.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.10696281800396, + "range": "± 0.73%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 318701.54794520605, + "range": "± 0.41%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 118811, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 117423.76320939497, + "range": "± 1.03%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 165823, + "range": "± 195.58%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 95.54645107631943, + "range": "± 0.67%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 104.23634246575203, + "range": "± 1.06%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "721f3fb87d1a1a101140d9c0d52fb4ab024bb5fd", + "message": "Fix spelling mistakes in disclaimer", + "timestamp": "2026-07-22T22:00:39+01:00", + "tree_id": "b692e7db446cfd0879fe698ff4721def38254ae3", + "url": "https://github.com/phpactor/phpactor/commit/721f3fb87d1a1a101140d9c0d52fb4ab024bb5fd" + }, + "date": 1784754167664, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.451675146770864, + "range": "± 1.93%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 167.7435068493145, + "range": "± 0.76%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.4109138943248696, + "range": "± 1.24%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.572780821917547, + "range": "± 1.20%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.027720156555772903, + "range": "± 1.84%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029376829745596727, + "range": "± 3.10%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.051434637964774745, + "range": "± 1.19%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015208180039138939, + "range": "± 7.75%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08945095890410965, + "range": "± 1.42%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.059109393346378866, + "range": "± 1.50%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.632850097847435, + "range": "± 0.82%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 616, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1443, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.539045009784724, + "range": "± 1.62%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.892272015655587, + "range": "± 5.91%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09035048923678886, + "range": "± 1.39%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.08984285714285584, + "range": "± 2.06%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.08963972602739717, + "range": "± 1.84%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.08897534246575338, + "range": "± 1.18%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.08949823874755396, + "range": "± 15.81%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08787827788649732, + "range": "± 1.48%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.08745362035225156, + "range": "± 1.72%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6033158512720154, + "range": "± 1.70%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05627827788649724, + "range": "± 3.43%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1151427, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13746183953033256, + "range": "± 9.14%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1401663405088061, + "range": "± 12.92%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13100782778864964, + "range": "± 2.00%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13617221135029337, + "range": "± 3.67%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08738160469667273, + "range": "± 4.81%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 320, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 293, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 308, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9671160469667475, + "range": "± 1.23%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.4546692759295565, + "range": "± 0.57%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16593.469667319092, + "range": "± 2.02%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 155.3619941291585, + "range": "± 156.92%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 143.56447945205574, + "range": "± 0.84%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 71233.56751467663, + "range": "± 0.34%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28131.356164383553, + "range": "± 1.04%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 24657.452054794467, + "range": "± 1.22%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30051.101761252285, + "range": "± 0.83%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 822419.9452054802, + "range": "± 0.71%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.851, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7153835616438513, + "range": "± 1.29%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.0819999999999816, + "range": "± 1.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.188223091976526, + "range": "± 3.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 74931.884540117, + "range": "± 0.87%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5886771037181742, + "range": "± 1.65%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.033027397260272, + "range": "± 1.03%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 312754.1017612546, + "range": "± 0.43%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 115410, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 113830.14090019674, + "range": "± 0.96%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 162445, + "range": "± 200.63%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 94.082835616438, + "range": "± 0.55%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 100.37955479452052, + "range": "± 1.64%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "f28b5db32fd8f08ee169ecf08dc72ada61a4fb25", + "message": "Show Phpactor version in the status LSP call (#3060)", + "timestamp": "2026-08-01T16:30:54+01:00", + "tree_id": "881145599ffdbc6b65c47e8345507206ac0fb09f", + "url": "https://github.com/phpactor/phpactor/commit/f28b5db32fd8f08ee169ecf08dc72ada61a4fb25" + }, + "date": 1785598362556, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.086254403131017, + "range": "± 1.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 165.77962230919704, + "range": "± 1.58%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.3651448140900344, + "range": "± 3.18%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.50468101761224, + "range": "± 1.00%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.02784645792563597, + "range": "± 1.96%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029373072407044985, + "range": "± 1.34%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.051575577299412874, + "range": "± 2.89%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015633228962818272, + "range": "± 1.68%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08965256360078291, + "range": "± 10.42%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 593, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1522, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.058551506849315156, + "range": "± 1.01%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.318046575342265, + "range": "± 0.68%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.162999999999963, + "range": "± 1.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.343624266144776, + "range": "± 1.26%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.09070626223091935, + "range": "± 1.70%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09074285714285746, + "range": "± 2.26%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.08954050880626165, + "range": "± 2.57%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09028512720156666, + "range": "± 1.15%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09016888454011807, + "range": "± 2.02%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.09072739726027194, + "range": "± 1.46%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.08986868884540004, + "range": "± 1.21%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.656187866927588, + "range": "± 3.06%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.055853424657534366, + "range": "± 4.13%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.14124657534246557, + "range": "± 16.14%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1376986301369862, + "range": "± 13.96%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13725636007827777, + "range": "± 15.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1326399217221134, + "range": "± 4.76%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09232289628180028, + "range": "± 10.00%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1165101, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 325, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 300, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 304, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 331803.7142857143, + "range": "± 128.21%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9655379647749496, + "range": "± 1.14%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.474212720156525, + "range": "± 0.45%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 121630, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17053.512720156694, + "range": "± 1.18%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 153.9885264187864, + "range": "± 0.71%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 148.31040313111757, + "range": "± 0.61%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.847, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 73269.64774951094, + "range": "± 0.76%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28990.80430528401, + "range": "± 0.67%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 25557.39726027442, + "range": "± 0.86%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 30255.956947162296, + "range": "± 0.52%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 840429.5459882598, + "range": "± 0.74%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 76226.8121330725, + "range": "± 1.22%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7091545988258194, + "range": "± 1.02%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.13630136986303, + "range": "± 1.08%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2458571428571656, + "range": "± 1.85%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5930802348336497, + "range": "± 3.12%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.053322896281732, + "range": "± 0.94%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 161415, + "range": "± 198.38%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 111604.65753424671, + "range": "± 0.66%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 91.2330763209386, + "range": "± 1.40%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 98.99594716242605, + "range": "± 1.35%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "anders@jenbo.dk", + "name": "Anders Jenbo", + "username": "AJenbo" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "7eb622fdd48d3b1fae87503d271038bc82aac45a", + "message": "Fix worse:analyse not finding functions (#3061)", + "timestamp": "2026-08-11T08:28:19+01:00", + "tree_id": "fdb16960799a50947515ef452494deb4de4b129a", + "url": "https://github.com/phpactor/phpactor/commit/7eb622fdd48d3b1fae87503d271038bc82aac45a" + }, + "date": 1786433393296, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 8.53499412915847, + "range": "± 1.80%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 134.35350489236905, + "range": "± 0.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 1.9418727984344362, + "range": "± 1.25%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 17.917825831702416, + "range": "± 0.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.015278043052837505, + "range": "± 2.70%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.016388023483365873, + "range": "± 1.64%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.036865831702544115, + "range": "± 1.76%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.007512211350293557, + "range": "± 3.28%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.06742367906066486, + "range": "± 2.66%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 516, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1301, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.04652727984344433, + "range": "± 1.60%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 14.935765166340795, + "range": "± 0.50%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 10.058859099804137, + "range": "± 0.48%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 10.375344422700666, + "range": "± 3.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07032641878669178, + "range": "± 2.71%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.06816673189823784, + "range": "± 2.78%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.06829060665362002, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.06899589041095883, + "range": "± 2.71%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.06806125244618319, + "range": "± 1.35%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.06869041095890477, + "range": "± 2.15%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.06802328767123106, + "range": "± 1.87%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.2821248532289597, + "range": "± 2.66%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.04533776908023493, + "range": "± 2.93%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.11279452054794513, + "range": "± 6.31%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.11110958904109586, + "range": "± 5.33%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.11139726027397254, + "range": "± 10.76%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.11200391389432474, + "range": "± 2.67%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.06981017612524447, + "range": "± 6.98%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 917973, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 274, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 287, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 283, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 479127.87279843446, + "range": "± 97.58%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.8011698630136972, + "range": "± 3.02%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.2129808219178082, + "range": "± 0.93%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 95362, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 13368.67710371835, + "range": "± 0.71%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 220.35738160469688, + "range": "± 0.41%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 217.89388845401047, + "range": "± 0.45%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 4.706, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 57904.54403131135, + "range": "± 0.56%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 23527.3796477492, + "range": "± 0.63%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 20852.75538160453, + "range": "± 0.26%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 25710.798434442815, + "range": "± 0.48%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 639090.8669275965, + "range": "± 0.30%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 63984.45009784738, + "range": "± 11.26%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.3708590998043306, + "range": "± 0.78%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.559863013698632, + "range": "± 0.78%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 1.8112837573385296, + "range": "± 0.54%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.276123287671242, + "range": "± 2.25%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.5112054794520513, + "range": "± 2.63%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 137551, + "range": "± 198.42%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 97396.86692759255, + "range": "± 0.87%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 79.54355577299414, + "range": "± 0.74%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 87.24932191780987, + "range": "± 0.65%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "anders@jenbo.dk", + "name": "Anders Jenbo", + "username": "AJenbo" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "439aa091ad8a79eb4bf15c7f465159fe5ba0fe55", + "message": "Fix a few false positives from the analyzer (#3063)", + "timestamp": "2026-08-15T12:47:52+01:00", + "tree_id": "91d343bbca7601a24e1e16cc2a733a3d4ac30862", + "url": "https://github.com/phpactor/phpactor/commit/439aa091ad8a79eb4bf15c7f465159fe5ba0fe55" + }, + "date": 1786794577772, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.4725303326809844, + "range": "± 2.14%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.92515068493213, + "range": "± 0.67%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 10.692692759295474, + "range": "± 1.77%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 173.83223483365697, + "range": "± 1.17%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.0278805870841487, + "range": "± 1.62%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029398356164383587, + "range": "± 1.62%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.05239080234833659, + "range": "± 2.18%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01540011741682973, + "range": "± 2.04%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.09160136986301319, + "range": "± 2.34%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05912575342465738, + "range": "± 2.04%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 19.176480234833665, + "range": "± 7.58%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 592, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1477, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 12.991827788649825, + "range": "± 2.24%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 13.2114637964775, + "range": "± 2.27%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.0951493150684931, + "range": "± 2.25%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.09494951076320848, + "range": "± 2.07%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.09394011741682873, + "range": "± 1.65%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.09489882583170461, + "range": "± 2.42%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.09428180039139145, + "range": "± 2.84%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.09541800391389424, + "range": "± 4.43%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.09442465753424478, + "range": "± 3.50%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6249205479451803, + "range": "± 1.33%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05686066536203538, + "range": "± 7.46%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.14732681017612517, + "range": "± 6.91%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.14663013698630128, + "range": "± 7.48%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.14627592954990207, + "range": "± 11.69%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.14551859099804296, + "range": "± 11.60%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1226603, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08770645792563612, + "range": "± 11.58%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 324, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 350, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 351, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 1.0579547945205656, + "range": "± 1.82%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.5626581213307358, + "range": "± 1.54%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 74286.8884540134, + "range": "± 0.87%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 29927.71037182035, + "range": "± 1.46%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 26388.48140900174, + "range": "± 1.28%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 31312.96086105657, + "range": "± 0.92%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 843072.4696673225, + "range": "± 0.55%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 6.036, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 17706.949119374098, + "range": "± 0.87%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 299.8582152641879, + "range": "± 132.39%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 285.6710136986249, + "range": "± 0.99%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 124574, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.63656164383563, + "range": "± 2.08%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.1955225048923728, + "range": "± 2.14%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 604003.350293546, + "range": "± 1.66%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.7475616438356227, + "range": "± 1.93%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.1584403131115235, + "range": "± 1.84%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.2808904109588837, + "range": "± 1.58%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 80995.67123287873, + "range": "± 0.58%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 119882.15068493197, + "range": "± 1.31%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 171055, + "range": "± 206.76%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 97.19107436399429, + "range": "± 0.97%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 104.27426027397217, + "range": "± 0.86%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "f67b7753b966c874735a064978dd89837a369f59", + "message": "Bump CL", + "timestamp": "2026-08-15T12:52:59+01:00", + "tree_id": "9bad45f8d35e20b583318510994f7ea0cc10a9b1", + "url": "https://github.com/phpactor/phpactor/commit/f67b7753b966c874735a064978dd89837a369f59" + }, + "date": 1786794882189, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.2857866927592894, + "range": "± 4.97%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.326160469667318, + "range": "± 2.60%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.619393346379555, + "range": "± 1.30%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 152.32946183953027, + "range": "± 0.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.031046966731898287, + "range": "± 3.64%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.03252481409002007, + "range": "± 1.04%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.054624266144813985, + "range": "± 1.15%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.01872003913894308, + "range": "± 1.07%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.0854095107632098, + "range": "± 1.20%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05552571428571411, + "range": "± 1.05%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.511599608610656, + "range": "± 0.53%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 571, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1628, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 11.69390802348333, + "range": "± 1.02%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.30820156555796, + "range": "± 1.40%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.07685518590998028, + "range": "± 2.82%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.07507181996086165, + "range": "± 1.76%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.07574207436399097, + "range": "± 2.02%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.07548317025440282, + "range": "± 2.82%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.07456066536203401, + "range": "± 1.38%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.07553679060665237, + "range": "± 2.10%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.07524363992172096, + "range": "± 1.14%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.5997745596868893, + "range": "± 1.92%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.05856673189823858, + "range": "± 2.39%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13932093933463785, + "range": "± 5.21%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.1382172211350292, + "range": "± 5.82%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.13283757338551846, + "range": "± 4.53%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.13385127201565541, + "range": "± 9.03%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1106330, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.09315655577299418, + "range": "± 8.37%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 290, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 307, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 291, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9418041095890234, + "range": "± 0.68%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.381416829745604, + "range": "± 0.54%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 60484.479452054424, + "range": "± 0.45%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 25866.293542074294, + "range": "± 0.49%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 22960.81213307225, + "range": "± 0.51%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 28933.08610567508, + "range": "± 0.42%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 734517.0763209373, + "range": "± 0.35%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.553, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 15047.465753424687, + "range": "± 0.56%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 249.7459315068493, + "range": "± 135.64%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 240.51578669276236, + "range": "± 0.57%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 108225, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.4843033268101753, + "range": "± 1.17%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.8933111545988566, + "range": "± 0.88%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 535559.7592954956, + "range": "± 1.89%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.616998043052834, + "range": "± 3.85%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.908528375733849, + "range": "± 0.91%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.0759021526418975, + "range": "± 1.56%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 75303.23287671375, + "range": "± 0.42%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 114751.45792563469, + "range": "± 0.59%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 161639, + "range": "± 194.93%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 95.85225831702448, + "range": "± 1.40%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 101.25944814089934, + "range": "± 0.25%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "14860264+mamazu@users.noreply.github.com", + "name": "mamazu", + "username": "mamazu" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "cc377db7e1443a69fc3ba36a40884aa91f002fdd", + "message": "Adding override attribute to override code action (#3057)", + "timestamp": "2026-08-22T12:07:21+01:00", + "tree_id": "421a30086779ab051fa928127aad60d4ff635110", + "url": "https://github.com/phpactor/phpactor/commit/cc377db7e1443a69fc3ba36a40884aa91f002fdd" + }, + "date": 1787396931484, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 1.7153796477494925, + "range": "± 4.75%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 15.852945205479392, + "range": "± 4.24%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 7.316706457925589, + "range": "± 2.39%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 121.59056164383476, + "range": "± 1.51%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.01324430528375726, + "range": "± 6.81%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.01403953033268093, + "range": "± 4.08%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.0315717808219177, + "range": "± 4.85%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.006236125244618378, + "range": "± 7.15%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.05803812133072425, + "range": "± 3.91%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.040121409001956834, + "range": "± 5.26%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 14.24403522504894, + "range": "± 5.84%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 502, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1143, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 9.33761839530326, + "range": "± 2.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 8.981246575342528, + "range": "± 3.99%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.059417416829745655, + "range": "± 5.32%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.06714050880626302, + "range": "± 6.98%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.06576731898238722, + "range": "± 6.81%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.06475185909980394, + "range": "± 4.98%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.06076027397260169, + "range": "± 2.86%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.05902250489236781, + "range": "± 6.85%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.05914520547945173, + "range": "± 4.74%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.2122412915851273, + "range": "± 6.50%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.04019452054794549, + "range": "± 4.59%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.09448727984344413, + "range": "± 7.74%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.09533463796477486, + "range": "± 7.42%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.1085655577299408, + "range": "± 8.02%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.09516438356164372, + "range": "± 5.17%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 837043, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.06143835616438372, + "range": "± 6.43%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 254, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 269, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 237, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.7309657534246456, + "range": "± 4.08%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.0796573385518624, + "range": "± 1.43%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 52705.20743639901, + "range": "± 2.36%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 21677.461839529955, + "range": "± 2.87%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 18515.39138943242, + "range": "± 4.86%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 24177.81017612495, + "range": "± 2.88%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 586023.6360078229, + "range": "± 2.12%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 4.137, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 11926.712328767051, + "range": "± 3.30%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 199.90479256360078, + "range": "± 136.75%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 197.00491389432727, + "range": "± 0.39%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 90788, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.260908023483331, + "range": "± 5.31%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 2.4509726027396592, + "range": "± 2.90%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 436380.2328767136, + "range": "± 3.34%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.234726027397246, + "range": "± 4.05%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 2.4692191780821515, + "range": "± 4.41%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 1.7645733855185513, + "range": "± 5.51%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 61890.97455968786, + "range": "± 1.47%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 92251.98630136959, + "range": "± 6.83%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 129305.94324853229, + "range": "± 198.32%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 72.81936692759284, + "range": "± 1.49%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 78.91089628180268, + "range": "± 2.24%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "8dc44fd24a1e407ec0bf44a5f2cafb6f7ab5dc07", + "message": "code-builder: Attribute builder", + "timestamp": "2026-08-22T12:58:28+01:00", + "tree_id": "630b46988247c65ee2f2d284f4c8109955d4614c", + "url": "https://github.com/phpactor/phpactor/commit/8dc44fd24a1e407ec0bf44a5f2cafb6f7ab5dc07" + }, + "date": 1787400016785, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "WorseLocalVariableCompletorBench::benchComplete (short)", + "value": 2.346050880626222, + "range": "± 10.93%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorseLocalVariableCompletorBench::benchComplete (long)", + "value": 22.2824794520548, + "range": "± 3.26%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (short)", + "value": 9.945720156555824, + "range": "± 1.01%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ClassMemberCompletorBench::benchComplete (long)", + "value": 163.2910430528389, + "range": "± 0.49%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfig", + "value": 0.027694168297455895, + "range": "± 1.20%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithBuilder", + "value": 0.029114363992172085, + "range": "± 1.75%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonLoadConfigWithNonExistingYaml", + "value": 0.050604540117416934, + "range": "± 4.04%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchJsonPlainPhp", + "value": 0.015340313111545913, + "range": "± 1.56%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "ConfigLoaderBench::benchYamlLoadConfig", + "value": 0.08845397260273956, + "range": "± 10.68%", + "unit": "ms", + "extra": "30 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchParse", + "value": 0.05831499021526464, + "range": "± 1.57%", + "unit": "ms", + "extra": "33 iterations, 50 revs" + }, + { + "name": "PhpactorParserBench::benchAssert", + "value": 18.512535812133056, + "range": "± 0.66%", + "unit": "ms", + "extra": "10 iterations, 5 revs" + }, + { + "name": "LexerBench::benchLex", + "value": 556, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "LexerBench::benchLex (1)", + "value": 1466, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchDiagnostics", + "value": 11.94681800391386, + "range": "± 1.08%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ImportNameProviderBench::benchCodeActions", + "value": 12.226808219178274, + "range": "± 1.27%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1)", + "value": 0.08829608610567405, + "range": "± 1.93%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 1001)", + "value": 0.08786927592955024, + "range": "± 1.22%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 2001)", + "value": 0.08912700587084137, + "range": "± 2.11%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 3001)", + "value": 0.08929256360078225, + "range": "± 2.11%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 4001)", + "value": 0.08782328767123235, + "range": "± 3.57%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 5001)", + "value": 0.08845362035224952, + "range": "± 0.82%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "WorkspaceIndexBench::benchUpdate (length: 6001)", + "value": 0.08748571428571468, + "range": "± 1.69%", + "unit": "ms", + "extra": "10 iterations, 10 revs" + }, + { + "name": "TokenExpanderBench::benchExpandTokenizedString", + "value": 1.6276704500978512, + "range": "± 1.82%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "TokenExpanderBench::benchExpandStringWithNoTokens", + "value": 0.055398238747553785, + "range": "± 6.71%", + "unit": "μs", + "extra": "33 iterations, 10000 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (A)", + "value": 0.13444031311154594, + "range": "± 9.71%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchBareFileSearch (Request)", + "value": 0.13472994129158508, + "range": "± 5.71%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (A)", + "value": 0.12992563600782767, + "range": "± 8.66%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "SearchBench::benchFullFileSearch (Request)", + "value": 0.1298199608610567, + "range": "± 6.58%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "IndexedReferenceFinderBench::benchBareFileSearch", + "value": 1128061, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ClassRecordShortNameBench::benchShortName", + "value": 0.08717808219178118, + "range": "± 5.30%", + "unit": "μs", + "extra": "33 iterations, 1000 revs" + }, + { + "name": "EfficientLineColsBench::benchLineCols", + "value": 313, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchLineColsUtf16Positions", + "value": 288, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "EfficientLineColsBench::benchIneffificentLineCols", + "value": 346, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "SelfReflectClassBench::benchMethodsAndProperties", + "value": 0.9823767123287779, + "range": "± 0.63%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "SelfReflectClassBench::benchFrames", + "value": 1.438688649706456, + "range": "± 0.87%", + "unit": "ms", + "extra": "5 iterations, 10 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_missing_methods.test)", + "value": 70521.66731898225, + "range": "± 0.65%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_generic_objects.test)", + "value": 28294.44422700651, + "range": "± 1.02%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (lots_of_new_objects.test)", + "value": 24647.859099804147, + "range": "± 0.68%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (method_chain.test)", + "value": 29547.0821917809, + "range": "± 0.43%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "DiagnosticsBench::benchDiagnostics (phpstan.test)", + "value": 818260.802348312, + "range": "± 0.49%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectionStubsBench::test_classes_and_methods", + "value": 5.63, + "range": "± 0.00%", + "unit": "ms", + "extra": "1 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case", + "value": 16481.095890410852, + "range": "± 0.81%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_methods_and_properties", + "value": 280.5499647749511, + "range": "± 133.32%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "PhpUnitReflectClassBench::test_case_method_frames", + "value": 265.0161761252463, + "range": "± 0.41%", + "unit": "ms", + "extra": "5 iterations, 1 revs" + }, + { + "name": "AnalyserBench::benchAnalyse", + "value": 115183, + "range": "± 0.00%", + "unit": "μs", + "extra": "1 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property", + "value": 1.5778121330724033, + "range": "± 4.12%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectPropertyBench::property_return_type", + "value": 3.011172211350273, + "range": "± 1.27%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "YiiBench::benchMembers", + "value": 583091.6438356128, + "range": "± 0.75%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method", + "value": 1.735904109589062, + "range": "± 1.35%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_return_type", + "value": 3.056142857142783, + "range": "± 0.92%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ReflectMethodBench::method_inferred_return_type", + "value": 2.1879784735812975, + "range": "± 1.06%", + "unit": "ms", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CarbonReflectBench::benchCarbonReflection", + "value": 76907.63796477561, + "range": "± 0.59%", + "unit": "μs", + "extra": "5 iterations, 1 revs" + }, + { + "name": "ClassSearchBench::benchClassSearch", + "value": 111467.56751467686, + "range": "± 1.54%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "CompleteBench::benchComplete", + "value": 162547, + "range": "± 198.63%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "BaseLineBench::benchVersion", + "value": 91.87702152641907, + "range": "± 0.92%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + }, + { + "name": "BaseLineBench::benchRpcEcho", + "value": 96.92484833659539, + "range": "± 1.19%", + "unit": "ms", + "extra": "4 iterations, 2 revs" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/dev/bench/index.html b/dev/bench/index.html new file mode 100644 index 0000000000..6c887805e8 --- /dev/null +++ b/dev/bench/index.html @@ -0,0 +1,281 @@ + + + + + + + Benchmarks + + + + +
+ + + + + + + diff --git a/doc/_ext/phpactor.py b/doc/_ext/phpactor.py deleted file mode 100644 index f08c629505..0000000000 --- a/doc/_ext/phpactor.py +++ /dev/null @@ -1,39 +0,0 @@ -from docutils import nodes -from docutils.parsers.rst import Directive -from docutils.parsers.rst import directives - - -class GitHubRepoDirective(Directive): - """Directive for Github Repositories.""" - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = False - has_content = False - - def run(self): - repo = self.arguments[0] - env = self.state.document.settings.env - - repo_link = nodes.reference('', repo, refuri='https://github.com/' + repo) - - title = nodes.paragraph(classes=['github-link']) - - github_icon = nodes.image(uri=directives.uri("/images/github.svg"),width="15px",height="15px") - title += github_icon, - title += nodes.emphasis(strong=True,text=' Github:') - title += nodes.inline(text=' ') - title += repo_link, - - - new_nodes = [title] - - return new_nodes - -def setup(app): - app.add_directive("github-link", GitHubRepoDirective) - - return { - 'version': '0.1', - 'parallel_read_safe': True, - 'parallel_write_safe': True, - } diff --git a/doc/_static/custom.css b/doc/_static/custom.css deleted file mode 100644 index 51f6470282..0000000000 --- a/doc/_static/custom.css +++ /dev/null @@ -1,28 +0,0 @@ -div.document { - width: 1050px; -} - -.github-link { - padding: 10px; - border-style: solid; - border-width: 1px; - border-color: #eee; - border-color: #ddd; -} - -@media screen and (max-width: 875px) { - div.document { - width: 100%; - } -} - -.logo h2 { - text-align: center; - font-family: sans-serif; -} - -#contents, .toctree-wrapper { - background-color: white; - border-style: none; - padding: 0px; -} diff --git a/doc/_static/logo.png b/doc/_static/logo.png deleted file mode 100644 index f066570360..0000000000 Binary files a/doc/_static/logo.png and /dev/null differ diff --git a/doc/_templates/about.html b/doc/_templates/about.html deleted file mode 100644 index 3fd4d42535..0000000000 --- a/doc/_templates/about.html +++ /dev/null @@ -1,9 +0,0 @@ - -

-

{{ theme_description }}

diff --git a/doc/_templates/navigation.html b/doc/_templates/navigation.html deleted file mode 100644 index 55bd7ce52e..0000000000 --- a/doc/_templates/navigation.html +++ /dev/null @@ -1,10 +0,0 @@ -
-{{ toctree(collapse=False,includehidden=False) }} -{% if theme_extra_nav_links %} -
-
    - {% for text, uri in theme_extra_nav_links.items() %} -
  • {{ text }}
  • - {% endfor %} -
-{% endif %} diff --git a/doc/_templates/searchbox.html b/doc/_templates/searchbox.html deleted file mode 100644 index bb51612d68..0000000000 --- a/doc/_templates/searchbox.html +++ /dev/null @@ -1,20 +0,0 @@ -{# - basic/searchbox.html - ~~~~~~~~~~~~~~~~~~~~ - - Sphinx sidebar template: quick search box. - - :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -#} -{%- if pagename != "search" and builder != "singlehtml" %} - - -{%- endif %} diff --git a/doc/adr.rst b/doc/adr.rst deleted file mode 100644 index 887d573766..0000000000 --- a/doc/adr.rst +++ /dev/null @@ -1,7 +0,0 @@ -ADRs -==== - -.. toctree:: - :glob: - - adr/* diff --git a/doc/adr/0001-package-structure-macro-packages.rst b/doc/adr/0001-package-structure-macro-packages.rst deleted file mode 100644 index 6b58eed9eb..0000000000 --- a/doc/adr/0001-package-structure-macro-packages.rst +++ /dev/null @@ -1,112 +0,0 @@ -Package Structure: Macro Packages -================================= - -**REJECTED** - -Context -------- - -Currently Phpactor is organized as follows: - -:: - - Phpactor\\Extension\\Extension\\ - Phpactor\\Extension\\LanguageServerExtension\\ - Phpactor\\Extension\\RpcExtension\\ - Phpactor\\\\{Adapter|Bridge}\\ - Phpactor\\\\{Model|Core}\\ - -- The ``Extension`` connects the ```` ``Model`` to the - Phpactor container. There may be an Extension which is “pure” - (abstract) and provide an extension point. Others will use the - extension point to integrate adapters (concrete implementations). -- The ``LanguageServer`` and (Phpactor) ``Rpc`` extensions provide - handlers for these two RPC methods. -- The adapters are the concrete implementations - (e.g. ``WorseReflection`` for fulfilling the completion APIs). -- The ``Model`` is the pure domain code and APIs. - -Each separate namespace has a special concern. Originally it was -intended that package stability principles be followed and each package -would live as a separate repository with a separate package (let’s call -them micro-packages). - -This has led to a situation where there are far too many packages, -integrating them all has become a huge problem and for the past year or -more it has significantly slowed down development on Phpactor: - -- Adding new features would involve adding 3 packages (model, extension - and adapter). -- As packages are young and unstable, refactoring would often affect - two or more packages. -- Maintaining the metadata over so many packages (build processes, - supported PHP versions, etc) becomes repetitive. - -Some steps were made towards a mono-repo with a sub-tree split but: - -- We would lose (or at least aim towards) semantic versioning without - new tooling. -- We risk cross-package contamination without new tooling. - -Decision --------- - -Keep separate repositories but combine extensions into the subject -namespace. - -So: - -:: - - Phpactor\\\\Extension\\ - Phpactor\\\\Adapter\\ - Phpactor\\\\Model\\ - -In a single package. All extensions will live in the ``Extension`` -namespace, e.g.: - -:: - - Phpactor\\\\Extension\\Extension - Phpactor\\\\Extension\\RpcExtension - Phpactor\\\\Extension\\LanguageServerExtension - -If it were the case that the ``Model`` never changed, and the abstract -extension points never changed, it would be fine to have separate -packages, but reality is not like this (even if the code never changes, -PHP versions do). - -Consequences ------------- - -This should significantly reduce the maintenance overhead as all -packages that change together are packaged together. - -Semantic versioning will not be as accurate as before - changes in the -``LanguageServer`` or ``Rpc`` APIs will cause a BC break for one or the -other but not both. - -The namespace changes from ``Phpactor\\Extension\\`` to -``Phpactor\\\\Extension``, which means that all external -extensions will have a BC break. - -Rejected --------- - -First of all, changing the namespace caused more trouble than -anticipated. While we could have provided stubs in the old namespace in -the packages, many packages exposed multiple public classes, so it -wasn’t practical. - -We solved this by mapping ``Phpactor\\`` to ``lib/`` and move everything -in to keep the same namespace structure as before. -(e.g. ``Phpactor\\LanguageServer``, -``Phpactor\\Extension\\LanguageServer``. - -But finally the **whole idea is flawed**: The “extension” dependencies -were in ``require-dev``, which meant that packages depending on an -extension, would need to explicitly require the package, the extensions -and any other dependencies of the extension. - -As extensions often depend on multiple other extensions, this is -completely unpractical. diff --git a/doc/adr/0002-language-server-package-structure.rst b/doc/adr/0002-language-server-package-structure.rst deleted file mode 100644 index 3f9e2f52c7..0000000000 --- a/doc/adr/0002-language-server-package-structure.rst +++ /dev/null @@ -1,75 +0,0 @@ -Language Server Package Structure -================================= - -**ACCEPTED** - -Context -------- - -For reasons described in the rejected 0001 ADR we have an issue where -adding language server features involves, if done properly, would -involve the creation of many more separate packages, increasing the -maintenance overhead significantly. - -The Language Server is made up of: - -- ``phpactor/language-server``: A generic language server package, not - coupled to Phpactor. -- ``phpactor/language-server-extension``: Command to launch the - language server, hooks to register RPC handlers. -- ``phpactor/language-server-completion-extension``: Handlers for - completion (using the existing Phpactor implementations from other - packages). -- ``phpactor/language-server-reference-finder-extension``: As with the - completion extension. -- ``phpactor/language-server-hover-extension``: This package *did* add - support for hover, but was recently merged into completion. - -In addition, there is the prospect of adding: - -- ``phpactor/language-server-indexer-extension``: Add the indexing - service to the server. -- ``phpactor/language-server-code-action-extension``: Integrate all the - code-transform actions. -- ``phpactor/language-server-worse-reflection-extension``: Add the - workspace source-locator for worse reflection. -- … - -Decision --------- - -Keep the generic language server package, but combine all the Phpactor -extensions into a single package: - -- ``phpactor/language-server``: A generic language server package, not - coupled to Phpactor. -- ``phpactor/language-server-extension``: Macro package containing all - the Phpactor Language Server extensions. - -The macro package will be organised with all extensions living in their -own namespaces, i.e. \ ``lib/``: - -:: - - lib/LanguageServer/ - lib/LanguageServerCompletion/ - lib/LanguageServerReferenceFinder/ - lib/... - -The namespace will be mapped as ``'Phpactor\\Extension\\' => 'lib/'`` - -so the extensions namespaces remain unchanged. - -Tests will also be namespaces as before, but will require additional -autoload mapping. - -Consequences ------------- - -Having all the language server functionality in one place makes it -easier to refactor, and much easier to add new features. - -In the future it should still be possible to break micro-packages out of -the macro-package. - -There is a risk that it is easier for packages to contaminate each -other. diff --git a/doc/adr/0003-generics.md b/doc/adr/0003-generics.md deleted file mode 100644 index 7eef8621af..0000000000 --- a/doc/adr/0003-generics.md +++ /dev/null @@ -1,92 +0,0 @@ -Generics -======== - -- Resolve iterable type -- Resolve method type -- Accept input - -## Iterable - -Given: - -```php -/** - * @implements IteratorAggregate - */ -class Foobar { -} - -foreach ($foobar as $bar) { -} -``` - -- Phpactor will call `resolveIterableValue` on the class type -- **Resolve template map for** `Traversable`: - 1. Foreach implement/extends reference - 2. Map any template vars (e.g. if `TKey` were a parameter `implement IteratorAggregate` => `IteratoeAggregate`) - 3. Is referenced class `Traversable`? Return template var map - 4. Switch to referenced class - 5. Goto 1 -- Return type for `TValue` from template var map - -## Method type - -Given: - -```php -/** @template T */ -class Collection { /** @return T */public function foo() {} } - -/** @extends Collection */ -class Bar {} - -$foo = new Bar(); -$foo = $bar->foo(); -``` - -- **Resolve template map for** method's declaring class `Collection` -- Return type for template var `T` from template map - -## Param/Constructor injection - -```php -/** @template T */ -class Foobar { - /** @param T $input */ - public function __construct($input) {} -} - -$foo = "hello"; -$foobar = new Foobar($foo); // Foobar -``` - -- **Resolve template map for** constructed class `Foobar` - `Map{T:}` -- Map parameters to template map `Map{T:"hello"}` -- Resolve new generic type `Foobar<"hello">` -- -## Param/Constructor injection with inheritence - -```php -/** @template T */ -class Barfoo { -} - -/** @extends Barfoo */ -class Foobar extends Barfoo { - /** @param T $input */ - public function __construct($input) {} -} - -$foo = "hello"; -$foobar = new Foobar($foo); // Foobar -``` - -- **Resolve template map for** constructed class `Foobar` - `Map{T:}` -- Map parameters to template map `Map{T:"hello"}` -- Resolve new generic type `Foobar<"hello">` - -## Method Injection - -- Method template vars are local to the template. -- Input parameters cannot mutate class-level generics unless the method is the - constructor. diff --git a/doc/completion.md b/doc/completion.md deleted file mode 100644 index 71ae894e0e..0000000000 --- a/doc/completion.md +++ /dev/null @@ -1,140 +0,0 @@ -.. _completion: - -Completion -========== - -Phpactor provides completion for: - -- **Class names**: All PSR compliant classes in the project and vendor tree. -- **Class members**: Methods, constants, properties of auto-loadable classes. -- **Functions**: Built-in and bootstrapped. -- **Constants**: Built-in and bootstrapped. -- **Parameters**: Will suggest appropriate local variables for method parameters. -- **Array Keys**: For array-shapes (`array{key1:value1}`) complete the keys. - -Uniquely, Phpactor does not pre-index anything, completion happens in _real -time_, file locations are guessed based on composer locations (or brute forced -if not using composer). For non-autoloadable entities (e.g. functions) it is -assumed that these are defined during bootstrap. - -Type inference --------------- - -Phpactors type inference is based on -[WorseReflection](https://github.com/phpactor/worse-reflection). - -### Assert - -When encountering an `assert` with `instanceof` it will cast the variable -to that type, or a union of that type. See also [#instanceof](#instanceof). - -```php -assert($foo instanceof Hello); -assert($foo instanceof Hello || $foo instanceof Goodbye) - -$foo-> // type: Hello|Goodbye -``` - -### Assignments - -Phpactor will track assignments: - -```php -$a = 'hello'; -$b = $a; -$b; // type: string -``` - -... and assignments from method calls, class properties, anything reflectable, etc. - -### Catch - -```php - -try { - // something -} catch (MyException $e) { - $e-> // type: MyException -} -``` - -### Foreach - -Understands `foreach` with the docblock array annotation: - -```php -/** @var Hello[] $foos */ -$foos = []; - -foreach ($foos as $foo) { - $foo-> // type:Hello -} -``` - -Also understands simple generics: - -```php -/** @var ArrayIterator $foos */ -$foos = new ArrayIterator([ new Hello() ]); - -foreach ($foos as $foo) { - $foo-> // type:Hello -} -``` - -### FunctionLike - -Understands anonymous functions: - -```php -$barfoo = new Barfoo(); -$function = function (Foobar $foobar) use ($barfoo) { - $foobar-> // type: Foobar - $barfoo-> // type: Barfoo -} -``` - -### InstanceOf - -`if` statements are evaluated, if they contain `instanceof` then the type is -inferred: - -```php -if ($foobar instanceof Hello) { - $foobar-> // type: Hello -} -``` - -```php -if (false === $foobar instanceof Hello) { - return; -} - -$foobar-> // type: Hello -``` - -```php -if ($foobar instanceof Hello || $foobar instanceof Goodbye) { - $foobar-> // type: Hello|Goodbye -} -``` - -### Variables - -Phpactor supports type injection via. docblock: - -```php -/** @var Foobar $foobar */ -$foobar-> // type: Foobar -``` - -and inference from parameters: - -```php -function foobar(Barfoo $foobar, $barbar = 'foofoo') -{ - $foobar; // type: Barfoo - $barbar; // type: foofoo -} -``` - diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index dd0b1af8a9..0000000000 --- a/doc/conf.py +++ /dev/null @@ -1,79 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys - -sys.path.append(os.path.abspath("./_ext")) - -# -- Project information ----------------------------------------------------- - -project = 'Phpactor' -copyright = '2020, Phpactor Community' -author = 'Phpactor Community' - -# The full version, including alpha/beta/rc tags -release = 'latest' - -master_doc = 'contents' - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx_tabs.tabs', - 'phpactor' -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affec-ts html_static_path and html_extra_path. -exclude_patterns = [] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'alabaster' -html_theme_options = { - 'logo': 'logo.png', - 'github_user': 'phpactor', - 'github_repo': 'phpactor', - 'description': 'Intelligent completion and refactoring tool for PHP', - 'logo_name': True, - 'logo_text_align': 'center', - 'description_font_style': 'italic', - 'github_banner': True, - 'travis_button': True, -} -html_sidebars = { - '**': [ - 'about.html', - 'searchbox.html', - 'navigation.html', - 'relations.html', - 'donate.html', - ] -} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] diff --git a/doc/contents.rst b/doc/contents.rst deleted file mode 100644 index 7b86ff7d13..0000000000 --- a/doc/contents.rst +++ /dev/null @@ -1,19 +0,0 @@ -Contents -======== - -.. toctree:: - :maxdepth: 2 - - usage - reference - tips - integrations - development - other - -.. toctree:: - :hidden: - - index - vim - adr diff --git a/doc/development.rst b/doc/development.rst deleted file mode 100644 index 4faec73771..0000000000 --- a/doc/development.rst +++ /dev/null @@ -1,9 +0,0 @@ -Development -=========== - -.. toctree:: - :maxdepth: 2 - :glob: - - development/* - diff --git a/doc/development/debugging.rst b/doc/development/debugging.rst deleted file mode 100644 index 555d8719f8..0000000000 --- a/doc/development/debugging.rst +++ /dev/null @@ -1,93 +0,0 @@ -Debugging -========= - -Var Dump Server ---------------- - -Phpactor includes the Symfony Var Dumper, this allows you to inspect values -while a server is running or an RPC request is being executed. - -Start the server in the Phpactor project root: - -:: - - $ ./vendor/bin/var-dump-server - - Symfony Var Dumper Server - ========================= - - [OK] Server listening on tcp://127.0.0.1:9912 - - // Quit the server with CONTROL-C - -You can use the `dump` function in the code and the variable will be shown in -the console output of the server you started: `dump($var)`. - -Logging -------- - -Logging is disabled by default, but can provide some useful information -(such as errors encountered when parsing files etc). - -Enable it as follows: - -:: - - { - "logging.enabled": true, - "logging.level": "debug", - "logging.path": "phpactor.log", - } - - -Legacy RPC ----------- - -.. note:: - - You should probably be using the language server so this is not for - you. - -When executing commands in an editor, it can be tricky to consistently -reproduce errors, or to isolate and debug them in Phpactor. - -Thankfully there is a feature called RPC replay which allows you to -replay the last RPC command received by Phpactor. - -Enable it in a Phpactor configuration file, for example -``$HOME/.config/phpactor/phpactor.yml``: - -.. code:: yaml - - rpc.store_replay: true - -Now, after you execute an RPC command via. your editor, you can execute -Phpactor from the shell and replay your last command: - -.. code:: bash - - $ phpactor rpc --replay - {"action":"open_file","parameters":{"path":"\/home\/daniel\/www\/phpactor\/phpactor\/lib\/Extension\/Rpc\/Handler\/AbstractHandler.php","offset":447}} - -To see more information, including the initial request add the -``--verbose`` option: - -.. code:: bash - - $ phpactor rpc --replay --verbose - [2018-05-23T22:14:11.486488+02:00] phpactor.DEBUG: REQUEST {"action":"goto_definition","parameters":{"source":"[removed]","offset":1913,"path":"/home/daniel/somepath/SomeClass.php"}} - [2018-05-23T22:14:11.494201+02:00] phpactor.DEBUG: Resolving: Microsoft\PhpParser\Node\Statement\ClassDeclaration [] [] - [2018-05-23T22:14:11.494545+02:00] phpactor.DEBUG: Resolving: Microsoft\PhpParser\Node\Parameter [] [] - ...[truncated]... - [2018-05-23T22:14:11.508019+02:00] phpactor.DEBUG: RESPONSE {"action":"open_file","parameters":{"path":"/home/daniel/www/phpactor/phpactor/lib/Extension/Rpc/Handler/AbstractHandler.php","offset":447}} [] - {"action":"open_file","parameters":{"path":"\/home\/daniel\/www\/phpactor\/phpactor\/lib\/Extension\/Rpc\/Handler\/AbstractHandler.php","offset":447}} - -The next time you run a command, you will lose your replay, in order to -consistently reproduce an action, you can copy the replay file and -execute it consistently as many times as required: - -.. code:: bash - - $ cp ~/.local/share/phpactor/replay.json . - $ cat replay.json | phpactor rpc --verbose - diff --git a/doc/development/developing.rst b/doc/development/developing.rst deleted file mode 100644 index ae09ccea7e..0000000000 --- a/doc/development/developing.rst +++ /dev/null @@ -1,24 +0,0 @@ -General -======= - -.. toctree:: - :maxdepth: 2 - :glob: - - language-server/* - -.. contents:: - :depth: 2 - :backlinks: none - :local: - -Package Structure ------------------ - -Phpactor is divided into _packages_. Each package occupies a directory in -`lib/`. In addition there are extensions which integrate packages with -Phpactor which are in `lib/Extension`. - -Tests and benchmarks are maintained in the package's directory, e.g. -`lib/WorseReflection/Tests`. - diff --git a/doc/development/documentation.rst b/doc/development/documentation.rst deleted file mode 100644 index fd1fcbc3c7..0000000000 --- a/doc/development/documentation.rst +++ /dev/null @@ -1,56 +0,0 @@ -Documentation -============= - -Phpactor Documentation -~~~~~~~~~~~~~~~~~~~~~~ - -Phpactor uses `Sphinx `_ (RST) for documentation. - -Docs are located in the ``docs``. - -A useful primer on RST can be found `here `_. - -.. tabs:: - - .. tab:: Debian/Ubuntu - - :: - - $ apt-get install python3-sphinx - $ pip install sphinx-tabs - -You can then build the docs with: - - - :: - - make sphinx - -Or, to watch for changes (requires ``inotifywait``): - - :: - - make sphinxwatch - -VIM Help -~~~~~~~~ - -The VIM plugin is documented in the *generated* ``doc/phpactor.txt`` -file using `vimdoc `_. - -In order to add documentation just annotate properties / methods with -comments, for example: - -.. code:: vim - - "" - " Extract the selected expression and assign it to a variable before - command! -buffer -range=% PhpactorExtractExpression call phpactor#ExtractExpression('v') - -See `vimdoc `_ for more information. - -Use the following command to both install vimdoc and build the documentation: - -.. code:: sh - - make vimdoc diff --git a/doc/development/language-server.rst b/doc/development/language-server.rst deleted file mode 100644 index 181a51e501..0000000000 --- a/doc/development/language-server.rst +++ /dev/null @@ -1,14 +0,0 @@ -Language Server -=============== - -Phpactor provides many internal extension points for the language server. - -.. note:: - - This documentation is incomplete. - -.. toctree:: - :maxdepth: 2 - :glob: - - language-server/code-action.rst diff --git a/doc/development/language-server/code-action.rst b/doc/development/language-server/code-action.rst deleted file mode 100644 index 167e10b75f..0000000000 --- a/doc/development/language-server/code-action.rst +++ /dev/null @@ -1,58 +0,0 @@ -Code actions -============ - -What is a code action ---------------------- - -A code action is an action that can be performed on the code. These can be -subdivided into Quickfixes, Refactoring and source fixes. For more information -on what the individual code action are doing please consult the documentation: -`Code action kind documentation -`_ - -How to write a code action --------------------------- - -In order to implement a new code action you need the following classes: - -- ``CodeActionProvider``: Provides a list of ``CodeAction`` instances. -- ``CodeAction``: Describes an action that should be performed, usually providing a callback to a command. -- ``CodeActionCommand``: A command which can be executed from the language client. - -.. note:: - - You can programmatically execute commands from the language client (e.g. - neovim) but it's not very intuitive. Code actions provide hints which can - normally be actioned through commands. - -Let's have a look at the concept by the example of the generate decorator -functionality. First off we have the ``GenerateDecoratorProvider`` this class -provides a list of ``CodeAction`` objects that are relevant to the file. - -The ``CodeAction`` list can then reference a command by name and provide arguments. The following is an example from the Phpactor ``GenerateDecoratorProvider.php`` class: - -.. code-block:: php - - uri, - $interfaceFQN, - ] - ) - ), - ]; - -The command is defined in our example in the ``GenerateDecoratorCommand`` which -will receive the arguments you pass and can effectively execute the action, typically in Phpactor we will delegate to a service - in much the same way you would with an MVC controller. - -In the case of the generate decorator command there is the -``WorseGenerateDecorator`` service which contains the logic for generating the -decorator or more generally to apply the code action. diff --git a/doc/development/profiling.rst b/doc/development/profiling.rst deleted file mode 100644 index 9bb63ce84d..0000000000 --- a/doc/development/profiling.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _developing_blackfire_profiling: - -Profiling the Language Server -============================= - -You can selectively profile the language server using `Blackfire `_. - -- Enable the blackfire via. :ref:`param_blackfire.enabled` -- Call the LSP methods `blackfire/start` and `blackfire/finish`, for NVIM see - :ref:`nvim_configuration_snippet_commands` diff --git a/doc/dot/class-referenes.dot b/doc/dot/class-referenes.dot deleted file mode 100644 index 28ae494998..0000000000 --- a/doc/dot/class-referenes.dot +++ /dev/null @@ -1,13 +0,0 @@ - -digraph { - Class [label="Acme\\AbstractServiceFactoryFactoryImpl"] - File1 [label="lib/Acme/Engine:32"] - File2 [label="lib/Acme/Entity/Car:100"] - File3 [label="lib/Acme/Circulator:200"] - File4 [label="lib/Acme/ContainerContainer:100,120,130"] - - File1 -> Class - File2 -> Class - File3 -> Class - File4 -> Class -} diff --git a/doc/dot/components.dot b/doc/dot/components.dot deleted file mode 100644 index 498fa37592..0000000000 --- a/doc/dot/components.dot +++ /dev/null @@ -1,26 +0,0 @@ -digraph { - CodeBuilder [label="Code Builder"] - Transform [label="Code Transform"] - Reflection [label="Worse Reflection"] - Filesystem [label="SC Filesystem"] - ClassMover [label="Class Mover"] - Phpactor [label="Phpactor"] - ClassToFile [label="Class to File"] - TestUtils [label="Test Utils"] - PathFinder [label="Path Finder"] - - Transform -> Phpactor - Reflection -> Phpactor - Filesystem -> Phpactor - ClassMover -> Phpactor - Reflection -> Transform - CodeBuilder -> Transform - ClassToFile -> Phpactor - PathFinder -> Phpactor - - Reflection -> TestUtils - Phpactor -> TestUtils - Transform -> TestUtils - CodeBuilder -> TestUtils - ClassMover -> TestUtils -} diff --git a/doc/images/components.png b/doc/images/components.png deleted file mode 100644 index 63a42c295d..0000000000 Binary files a/doc/images/components.png and /dev/null differ diff --git a/doc/images/github.svg b/doc/images/github.svg deleted file mode 100644 index 53bd7b2d2a..0000000000 --- a/doc/images/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/doc/images/logo-small.png b/doc/images/logo-small.png deleted file mode 100644 index 5c5c973503..0000000000 Binary files a/doc/images/logo-small.png and /dev/null differ diff --git a/doc/images/risky.png b/doc/images/risky.png deleted file mode 100644 index 367a8055f2..0000000000 Binary files a/doc/images/risky.png and /dev/null differ diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index bdef383ad1..0000000000 --- a/doc/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. Phpactor documentation master file, created by - sphinx-quickstart on Fri May 1 16:57:28 2020. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Phpactor -======== - -Phpactor is a PHP :ref:`language_server` providing: - -- :ref:`completion`: Provides broad and accurate context aware code - completion. -- :ref:`navigation`: Jump to class and method definitions, find - references, hover. -- :ref:`refactoring`: Move classes, complete constructors, implement - contracts, generate methods, etc. -- :ref:`diagnostics`: Diagnostics related to code actions. -- :ref:`integrations`: Integrates with popular tools and frameworks such as Symfony, PHPStan, etc. -- :ref:`vim_plugin`: Lightweight VIM plugin. - -Proceed to the :doc:`usage/getting-started` guide to find out how to be getting -started with Neovim, VS Code, and other editors. diff --git a/doc/integrations.rst b/doc/integrations.rst deleted file mode 100644 index c895ffae5d..0000000000 --- a/doc/integrations.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _integrations: - -Integrations -============ - -.. toctree:: - :maxdepth: 2 - :glob: - - integrations/* - - diff --git a/doc/integrations/behat.rst b/doc/integrations/behat.rst deleted file mode 100644 index 4207f8e32b..0000000000 --- a/doc/integrations/behat.rst +++ /dev/null @@ -1,46 +0,0 @@ -Behat -===== - -`Behat `_ is a BDD framework. - -Phpactor can provide goto definition and some completion support within feature files. - -Enabling --------- - -The extension must be via. :ref:`param_behat.enabled`: - -.. code-block:: bash - - $ phpactor config:set behat.enabled true - -Symfony Integration -------------------- - -If you are using Symfony and dependency injection to manage your contexts you -can specify the path to the XML debug file in -:ref:`param_behat.symfony.di_xml_path`: - -For example: - -.. code-block:: bash - - $ phpactor config:set behat.symfony.di_xml_path "var/cache/test/App_KernelTestDebugContainer.xml" - -Language Server Support ------------------------ - -This extension acts on cucumber files, you will need to configure your -_client_ to ensure that it will call Phpactor when in the feature files. - - -.. tabs:: - - .. tab:: Neovim LSP (via. lspconfig) - - :: - - require'lspconfig'.phpactor.setup{ - -- ... - filetypes = { 'php', 'cucumber' }, - } diff --git a/doc/integrations/drupal8.rst b/doc/integrations/drupal8.rst deleted file mode 100644 index d4690521d7..0000000000 --- a/doc/integrations/drupal8.rst +++ /dev/null @@ -1,134 +0,0 @@ -Drupal 8+ -========= - -Inc and module files -^^^^^^^^^^^^^^^^^^^^ - -By default Phpactor will not index `.inc` nor `.module` files. - -Run the following on your project to enable the indexing of `.inc` and `.module` files. - -``` -phpactor config:set indexer.supported_extensions '["php", "inc", "module"]' -``` - -``` -phpactor config:set indexer.include_patterns '["/**/*.php", "/**/*.inc", "/**/*.module"]' -``` - -Bootstrapping -~~~~~~~~~~~~~ - -Drupal automatically adds its modules to the autoloader during the -kernel boot process. It is therefore necessary to either 1) boot the -kernel to have a fully useful autoloader *or* 2) to use a different -mechanism to add the modules to the Composer autoloader. - -Depending on your setup option 1 or 2 will be preferable. - -Option 1: Bootstrap Drupal on the fly to generate the autoloader -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Create the following bootstrap file ``phpactor_autoload.php``, in (for -example) ``web/``: - -.. code:: php - - // web/phpactor_autoload.php - locateRoot($root)) { - die('DrupalConsole must be executed within a Drupal Site.'); - } - - chdir($drupalFinder->getDrupalRoot()); - - $drupalKernel = DrupalKernel::createFromRequest( - Request::createFromGlobals(), - $autoload, - 'dev', - true, - $drupalFinder->getDrupalRoot() - ); - $drupalKernel->boot(); - chdir($root); - - return $autoload; - -Then edit ``.phpactor.yml`` to use that: - -.. code:: yaml - - # Use the special autoloader above - composer.autoloader_path: web/phpactor_autoload.php - -The downside to this option is that it requires access to the DB from -your current environment which may be tricky if you are running Drupal -inside a VM. - -Option 2: Add the modules into the Composer autoloader -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This option requires merging in any modules (Drupal, contrib, custom) -into the Composer autoloader via a discovery mechanism offered by the -`Drupal Autoloader `__ -composer plugin. - -Full details are available in the README for this plugin, however, the -short version is that you will need to put the following into your -``composer.json`` file: - -.. code:: json - - { - "require": { - "fenetikm/autoload-drupal": "0.1" - }, - "extra": { - "autoload-drupal": { - "modules": [ - "app/modules/contrib/", - "app/modules/custom/", - "app/core/modules/" - ] - } - } - } - -and then rebuild your Composer autoloader e.g. - -.. code:: sh - - composer autoload-dump - -The upside to this option is that it won’t require the relatively slow -Drupal bootstrap (which will hit the DB) but the downside is that you -will have to regenerate the autoloader every time you add / remove a -module. - -Coding Standards -^^^^^^^^^^^^^^^^ - -Change your local ``.phpactor.yml`` to use 2 spaces for indentation: - -:: - - # Drupal CS is 2 spaces - code_transform.indentation: " " - -.. container:: alert alert-info - - Code will still be generated using the PSR-2 standard. It would be - necessary to override twig templates in ``.phpactor/templates`` to - rectify this (or just use a CS fixer). - - diff --git a/doc/integrations/mago.rst b/doc/integrations/mago.rst deleted file mode 100644 index 3a7605d78c..0000000000 --- a/doc/integrations/mago.rst +++ /dev/null @@ -1,27 +0,0 @@ -Mago -==== - -`Mago `_ is a fast PHP linter, formatter and static analysis tool written in Rust. - -Phpactor can integrate with Mago and provide diagnostics in your IDE from two of its tools: - -- ``mago analyze`` (static analysis, the equivalent of PHPStan or Psalm), reported under the ``mago`` source. -- ``mago lint`` (style and code smells), reported under the ``mago-lint`` source. - -To enable the integration set :ref:`param_language_server_mago.enabled`: - -.. code-block:: bash - - $ phpactor config:set language_server_mago.enabled true - -Both tools are enabled by default once the extension is on. Toggle them independently with :ref:`param_language_server_mago.analyze.enabled` and :ref:`param_language_server_mago.lint.enabled`. - -- Specify the path to Mago if different to ``vendor/bin/mago`` via :ref:`param_language_server_mago.bin`. Mago is commonly installed globally, in which case set this to ``mago``. -- Override the Mago configuration file with :ref:`param_language_server_mago.config`. -- Adjust the run timeout (milliseconds) with :ref:`param_language_server_mago.timeout`. - -Phpactor sends the current buffer to Mago on standard input, so diagnostics update as you type without saving the file. - -.. note:: - - Pointing :ref:`param_language_server_mago.config` at a ``mago.toml`` outside the project root may change how Mago resolves the relative paths declared inside that file. diff --git a/doc/integrations/php-cs-fixer.rst b/doc/integrations/php-cs-fixer.rst deleted file mode 100644 index 46b8209ea8..0000000000 --- a/doc/integrations/php-cs-fixer.rst +++ /dev/null @@ -1,22 +0,0 @@ -PHP-CS-Fixer -============ - -`PHP-Cs-Fixer `_ is a tool -fixes your code to follow standards; whether you want to follow PHP coding -standards as defined in the PSR-1, PSR-2, etc., or other community driven ones -like the Symfony one. You can also define your (team's) style through -configuration. - -Phpactor can use PHP-CS-Fixer to: - -- format your code via the LSP `textDocument/formatting` action, -- provide diagnostics for potential fixes, -- provide `source.fixAll.phpactor.phpCsFixer` code action to allow auto-fixing on save. - -To do so you set :ref:`param_language_server_php_cs_fixer.enabled`: - -.. code-block:: bash - - $ phpactor config:set language_server_php_cs_fixer.enabled true - -- Specify the path to PHP-CS-Fixer if different to ``/vendor/bin/php-cs-fixer`` via. :ref:`param_language_server_php_cs_fixer.bin`. diff --git a/doc/integrations/phpcs.rst b/doc/integrations/phpcs.rst deleted file mode 100644 index df6cb0f31e..0000000000 --- a/doc/integrations/phpcs.rst +++ /dev/null @@ -1,22 +0,0 @@ -PHP_CodeSniffer -=============== - -`PHP_CodeSniffer ` is a set of -two PHP scripts; the main phpcs script that tokenizes PHP, JavaScript and CSS -files to detect violations of a defined coding standard, and a second phpcbf -script to automatically correct coding standard violations. PHP_CodeSniffer is -an essential development tool that ensures your code remains clean and -consistent. - -Phpactor can use PHP_CodeSniffer to: - -- format your code via the LSP ``textDocument/formatting`` action, -- provide diagnostics for potential fixes, - -To do so you set :ref:`param_php_code_sniffer.enabled`: - -.. code-block:: bash - - $ phpactor config:set php_code_sniffer.enabled true - -- Specify the path to ``phpcs`` if different to ``/vendor/bin/phpcs`` via. :ref:`param_php_code_sniffer.bin`. diff --git a/doc/integrations/phpstan.rst b/doc/integrations/phpstan.rst deleted file mode 100644 index bd6a2e16df..0000000000 --- a/doc/integrations/phpstan.rst +++ /dev/null @@ -1,17 +0,0 @@ -PHPStan -======= - -`PHPStan `_ is a static analysis tool that can be used to expose bugs in your PHP code. - -Phpactor can integrate with PHPStan and provide diagnostics in your IDE. - -To do so you set :ref:`param_language_server_phpstan.enabled`: - -.. code-block:: bash - - $ phpactor config:set language_server_phpstan.enabled true - -- Override the PHPStan level with :ref:`param_language_server_phpstan.level` -- Specify the path to PHPStan if different to ``/vendor/bin/phpstan`` via. :ref:`param_language_server_phpstan.bin`. - -Keep in mind that Phpactor analyzes files with a temporary name, so defining a PHPStan baseline won't work as expected. All detected errors will be reported despite the baseline. diff --git a/doc/integrations/phpunit.rst b/doc/integrations/phpunit.rst deleted file mode 100644 index 6fb6beae09..0000000000 --- a/doc/integrations/phpunit.rst +++ /dev/null @@ -1,17 +0,0 @@ -PHPUnit -======= - -`PHPUnit `_ is a programmer-oriented -testing framework for PHP. It is an instance of the xUnit architecture for -unit testing frameworks. - -Phpactor can integrate with PHPUnit to provide: - -- Type inference from assertions. -- Generate test case. - -To do so you set :ref:`param_phpunit.enabled`: - -.. code-block:: bash - - $ phpactor config:set phpunit.enabled true diff --git a/doc/integrations/prophecy.rst b/doc/integrations/prophecy.rst deleted file mode 100644 index e4de7614e4..0000000000 --- a/doc/integrations/prophecy.rst +++ /dev/null @@ -1,12 +0,0 @@ -Prophecy -======== - -`Prophecy `_ is a highly opinionated yet very powerful and flexible PHP object mocking framework. Though initially it was created to fulfil phpspec2 needs, it is flexible enough to be used inside any testing framework out there with minimal effort. - -Phpactor can integrate with Prophecy to provide completion for mocks. - -To do so you set :ref:`param_prophecy.enabled`: - -.. code-block:: bash - - $ phpactor config:set prophecy.enabled true diff --git a/doc/integrations/psalm.rst b/doc/integrations/psalm.rst deleted file mode 100644 index 295e277448..0000000000 --- a/doc/integrations/psalm.rst +++ /dev/null @@ -1,18 +0,0 @@ -Psalm -===== - -`Psalm `_ is a static analysis tool that can be used to expose bugs in your PHP code. - -Phpactor can integrate with Psalm to provide diagnostics in your IDE. - -.. note:: - - Currently this extension will only analyse saved files. - -To do so you set :ref:`param_language_server_psalm.enabled`: - -.. code-block:: bash - - $ phpactor config:set language_server_psalm.enabled true - -- Specify the path to Psalm if different to ``/vendor/bin/psalm`` via. :ref:`param_language_server_psalm.bin`. diff --git a/doc/integrations/symfony.rst b/doc/integrations/symfony.rst deleted file mode 100644 index 03c5ed09fb..0000000000 --- a/doc/integrations/symfony.rst +++ /dev/null @@ -1,38 +0,0 @@ -Symfony -======= - -`Symfony `_ is a popular web framework. - -Phpactor can provide: - -- Service ID completion when using the container as a service locator. -- Type inference when accessing services from the container. - -Enabling --------- - -The extension must be via. :ref:`param_symfony.enabled`: - -.. code-block:: bash - - $ phpactor config:set symfony.enabled true - -Dependency Injection Features ------------------------------ - -When in development mode Symfony will dump an XML file which describes the -current container. In Symfony 5.x this file is located in -``var/cache/dev/App_KernelDevDebugContainer.xml`` but this may be different -for earlier versions. - -You can set the path to this file with :ref:`param_symfony.xml_path` - -Troubeshooting --------------- - -### I don't get any completion suggestions or type inference. - -Ensure that your Symfony cache has been warmed up and that you are in -development mode. - -Try running ``./bin/console cache:clear`` diff --git a/doc/language-server.rst b/doc/language-server.rst deleted file mode 100644 index bcefcfacb8..0000000000 --- a/doc/language-server.rst +++ /dev/null @@ -1,12 +0,0 @@ -Language Server -=============== - -See :ref:`language_server` for getting started instructions. - -.. toctree:: - :maxdepth: 2 - - lsp/clients - lsp/running - lsp/support - lsp/code-actions diff --git a/doc/lsp/clients.rst b/doc/lsp/clients.rst deleted file mode 100644 index 29b23dc577..0000000000 --- a/doc/lsp/clients.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _language_server_clients: - -Clients -======= - -.. toctree:: - :maxdepth: 2 - - vim - sublime - vscode - emacs - helix - nova - zed - -.. toctree:: - :hidden: - - vim-lsp diff --git a/doc/lsp/code-actions.rst b/doc/lsp/code-actions.rst deleted file mode 100644 index 1fd5201070..0000000000 --- a/doc/lsp/code-actions.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. _lsp_code_actions: - -LSP code actions -================ - -See `Language Server Specification (Code Action Request)`_ for details. - -List of currently available code actions: - -+---------------------------------------------+----------------------------------------+ -| Code Action | Kind | -+=============================================+========================================+ -| :ref:`refactoring_import_missing_class` | ``quickfix.import_class`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`refactoring_complete_constructor` | ``quickfix.complete_constructor`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`refactoring_add_missing_assignements` | ``quickfix.add_missing_properties`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`implement_contracts` | ``quickfix.implement_contracts`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`refactoring_fix_namespace_and_class` | ``quickfix.fix_namespace_class_name`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`generation_class_new` | ``quickfix.create_class`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`generation_class_new` | ``quickfix.create_unresolvable_class`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`generation_method` | ``quickfix.generate_method`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`generation_extract_method` | ``quickfix.extract_method`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`generation_extract_expression` | ``quickfix.extract_expression`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`generation_extract_constant` | ``quickfix.extract_constant`` | -+---------------------------------------------+----------------------------------------+ -| :ref:`generation_generate_accessors` | ``quickfix.generate_accessors`` | -+---------------------------------------------+----------------------------------------+ - -.. _Language Server Specification (Code Action Request): https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction diff --git a/doc/lsp/emacs.rst b/doc/lsp/emacs.rst deleted file mode 100644 index 3782a7011d..0000000000 --- a/doc/lsp/emacs.rst +++ /dev/null @@ -1,82 +0,0 @@ -Emacs -===== - -Client Guides -------------- - - -.. tabs:: - - .. tab:: LSP Mode - - Install Phpactor with :ref:`installation_global` then install `LSP Mode - `_. Please read `Installation - LSP Mode - `_ for client installation. - Installing ``lsp-ui`` in addition to lsp-mode integrates a rich UI for LSP into Emacs. - - For example: include it in your ``init.el`` - - :: - - ;; Add lsp or lsp-deferred function call to functions for your php-mode customization - (defun init-php-mode () - (lsp-deferred)) - - (with-eval-after-load 'php-mode - ;; If phpactor command is not installed as global, write the full path - ;; (custom-set-variables '(lsp-phpactor-path "/path/to/phpactor")) - (add-hook 'php-mode-hook #'init-php-mode)) - - If you're using use-package or leaf.el, you can add it to the ``:hook`` or ``:init`` clauses - of those blocks instead of ``with-eval-after-load``. - - Read `FAQ - LSP Mode - `_ - if you have a language server other than Phpactor in your environment. - - .. tab:: Eglot - - Install Phpactor with :ref:`installation_global` then install `Eglot - `_. Eglot installs in your Emacs by executing - ``M-x package-install eglot`` commands. - - For example: include it in your ``init.el`` - - :: - - ;; Add lsp or lsp-deferred function call to functions for your php-mode customization - (defun init-php-mode () - (eglot-ensure)) - - (with-eval-after-load 'php-mode - ;; If phpactor command is not installed as global, remove next ;; and write the full path - ;; (custom-set-variables '(lsp-phpactor-path "/path/to/phpactor")) - (add-hook 'php-mode-hook #'init-php-mode)) - - If you're using use-package or leaf.el, you can add it to the ``:hook`` or ``:init`` clauses - of those blocks instead of ``with-eval-after-load``. - - .. tab:: lsp-bridge - - Install Phpactor with :ref:`installation_global` then install `lsp-bridge - `_. Please read README for client installation. - - For example: include it in your ``init.el`` - - :: - - ;;; When enabled in all major modes supported by lsp-bridge - ;; (global-lsp-bridge-mode) - - ;;; When enabling lsp-bridge only for PHP Mode - - ;; Add lsp or lsp-deferred function call to functions for your php-mode customization - (defun init-php-mode () - (lsp-bridge-mode +1)) - - (with-eval-after-load 'php-mode - (custom-set-variables '(lsp-bridge-php-lsp-server . "phpactor")) - (add-hook 'php-mode-hook #'init-php-mode)) - - If you're using use-package or leaf.el, you can add it to the ``:hook`` or ``:init`` clauses - of those blocks instead of ``with-eval-after-load``. diff --git a/doc/lsp/helix.rst b/doc/lsp/helix.rst deleted file mode 100644 index f35eb4ef16..0000000000 --- a/doc/lsp/helix.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _lsp_client_helix: - -Helix Editor -============ - -Install Phpactor with :ref:`installation_global` then add the -Phpactor language server configuration in your `languages.toml` -as follows: - -.. code:: toml - - # in /helix/languages.toml - - [language-server.phpactor] - command = "phpactor" - args = ["language-server"] - - [[language]] - name = "php" - scope = "source.php" - injection-regex = "php" - file-types = ["php", "inc", "php4", "php5", "phtml", "ctp"] - shebangs = ["php"] - roots = ["composer.json", "index.php"] - comment-token = "//" - language-servers = [ "phpactor" ] - indent = { tab-width = 4, unit = " " } diff --git a/doc/lsp/nova.rst b/doc/lsp/nova.rst deleted file mode 100644 index 59a0382745..0000000000 --- a/doc/lsp/nova.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _lsp_client_nova: - -Nova Code Editor -================ - -Download the Phpactor extension from the `Nova Extensions Library `_. diff --git a/doc/lsp/running.rst b/doc/lsp/running.rst deleted file mode 100644 index 9f336823fc..0000000000 --- a/doc/lsp/running.rst +++ /dev/null @@ -1,37 +0,0 @@ -Manually Running the Server ---------------------------- - -Typically you should never need to run the Language Server yourself, the -following methods are useful for development. - -.. _lsp_running_stdio: - -STDIO -~~~~~ - -STDIO is typically used by clients and it the default mode. - -.. code:: bash - - $ phpactor language-server - -This is the method you should use when configuring an LSP client. - -Run with TCP Server -~~~~~~~~~~~~~~~~~~~ - -The TCP server is useful for debugging: - -.. code:: bash - - $ phpactor language-server --address=127.0.0.1:8888 -vvv - -You should see something like: - -:: - - Starting TCP server, use -vvv for verbose output - [2018-09-30 17:15:25] phpactor.INFO: listening on address 127.0.0.1:8888 [] [] - [2018-09-30 17:15:25] phpactor.INFO: starting language server with pid: 9286 [] [] - -.. _Language Server Protocol: https://microsoft.github.io/language-server-protocol/specification diff --git a/doc/lsp/sublime.rst b/doc/lsp/sublime.rst deleted file mode 100644 index 136b3e2f71..0000000000 --- a/doc/lsp/sublime.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _lsp_client_sublime: - -Sublime Text -============ - -Install Phpactor with :ref:`installation_global` then navigate to `Preferences -> Package Settings > LSP > Settings` and add the Phpactor language server -configuration as follows: - -.. code:: javascript - - "clients": - { - "phpactor": - { - "command": - [ - "phpactor", - "language-server", - - ], - "enabled": true, - "languageId": "php", - "scopes": - [ - "source.php", - "embedding.php" - ], - "syntaxes": - [ - "Packages/PHP/PHP.sublime-syntax" - ] - } - } diff --git a/doc/lsp/support.rst b/doc/lsp/support.rst deleted file mode 100644 index 8ae22c0a45..0000000000 --- a/doc/lsp/support.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. _lsp_support: - -LSP Support -=========== - -See the `Language Server Specification`_ for details. - -+-------------------------+---+-------------------------------------+ -| Feature | | | -+=========================+===+=====================================+ -| Completion | ✔ | See :ref:`completion` | -+-------------------------+---+-------------------------------------+ -| Hover | ✔ | | -+-------------------------+---+-------------------------------------+ -| Signature Help | ✔ | | -+-------------------------+---+-------------------------------------+ -| Goto Declaration | ✔ | | -+-------------------------+---+-------------------------------------+ -| Goto Type | ✔ | | -+-------------------------+---+-------------------------------------+ -| Goto Implementation | ✔ | | -+-------------------------+---+-------------------------------------+ -| Find References | ✔ | [#references]_ | -+-------------------------+---+-------------------------------------+ -| Document Highlight | ✔ | Symbol highlighting | -+-------------------------+---+-------------------------------------+ -| Workspace Symbols | ✔ | Classes, functions and constants | -+-------------------------+---+-------------------------------------+ -| Document Symbol | ✔ | For structural elements | -+-------------------------+---+-------------------------------------+ -| Selection Range | ✔ | | -+-------------------------+---+-------------------------------------+ -| Code Action | ✔ | [#code]_ | -+-------------------------+---+-------------------------------------+ -| Code Lens | ✘ | | -+-------------------------+---+-------------------------------------+ -| Document Link | ✘ | | -+-------------------------+---+-------------------------------------+ -| Document Color | ✘ | | -+-------------------------+---+-------------------------------------+ -| Color Presentation | ✘ | | -+-------------------------+---+-------------------------------------+ -| Formatting | ✔ | [#formatting]_ | -+-------------------------+---+-------------------------------------+ -| Range Formatting | ✘ | | -+-------------------------+---+-------------------------------------+ -| Rename | ✔ | Variables and members [#rename]_ | -+-------------------------+---+-------------------------------------+ -| Folding/Selection Range | ✘ | | -+-------------------------+---+-------------------------------------+ -| Diagnostics | ✔ | [#diagnostics]_ | -+-------------------------+---+-------------------------------------+ - -.. _Language Server Specification: https://microsoft.github.io/language-server-protocol/specification - -.. [#rpc] Available through RPC (i.e. non-LSP client) LSP support should be added soon. -.. [#code] See :doc:`/lsp/code-actions`. -.. [#references] For class like references, functions and member accesses (static and object instances) -.. [#rename] Native LSP support for renaming variables and class members, with support planned for renaming classes and namespaces. RPC fills the gap: :ref:`refactoring_rename_class` -.. [#formatting] With php-cs-fixer :ref:`php-cs-fixer `. -.. [#diagnostics] Basic PHP linting and also support for integrating with :ref:`phpstan `, :ref:`Psalm ` and :ref:`php-cs-fixer `. diff --git a/doc/lsp/vim-lsp.rst b/doc/lsp/vim-lsp.rst deleted file mode 100644 index dfaf9a8a22..0000000000 --- a/doc/lsp/vim-lsp.rst +++ /dev/null @@ -1,92 +0,0 @@ -NVIM LSP Configuration Snippets -=============================== - -This page contains some useful configuration snippets which are not guaranteed -to work. - -Progress Notifications ----------------------- - -Neovim does not support LSP progress notifications out-of-the-box. - -Install a dedicated plugin such as `fidget `_ - -.. _nvim_configuration_snippet_commands: - -Phpactor Commands ------------------ - -.. note:: - - This snippet depends on the "plenary" plugin (which is also required by - "telescope") - -This configuration snippet enables the following commands: - -- ``:LspPhpactorReindex``: Reindex the current project -- ``:LspPhpactorStatus``: Show some useful information and statistics -- ``:LspPhpactorConfig``: Show the config in a floating window - -If you want to profile Phpactor for debugging purposes: - -- ``:LspPhpactorBlackfireStart``: Start :ref:`developing_blackfire_profiling` (if enabled with :ref:`param_blackfire.enabled`) -- ``:LspPhpactorBlackfireFinish``: Finish profiling and get the profiling URL - -.. code-block:: text - - -- requires plenary (which is required by telescope) - local Float = require "plenary.window.float" - - vim.cmd([[ - augroup LspPhpactor - autocmd! - autocmd Filetype php command! -nargs=0 LspPhpactorReindex lua vim.lsp.buf_notify(0, "phpactor/indexer/reindex",{}) - autocmd Filetype php command! -nargs=0 LspPhpactorConfig lua LspPhpactorDumpConfig() - autocmd Filetype php command! -nargs=0 LspPhpactorStatus lua LspPhpactorStatus() - autocmd Filetype php command! -nargs=0 LspPhpactorBlackfireStart lua LspPhpactorBlackfireStart() - autocmd Filetype php command! -nargs=0 LspPhpactorBlackfireFinish lua LspPhpactorBlackfireFinish() - autocmd Filetype php command! -nargs=0 LspPhpactorIndexOptimise lua vim.lsp.buf_notify(0, "phpactor/indexer/optimise",{}) - augroup END - ]]) - - local function showWindow(title, syntax, contents) - local out = {}; - for match in string.gmatch(contents, "[^\n]+") do - table.insert(out, match); - end - - local float = Float.percentage_range_window(0.6, 0.4, { winblend = 0 }, { - title = title, - topleft = "┌", - topright = "┐", - top = "─", - left = "│", - right = "│", - botleft = "└", - botright = "┘", - bot = "─", - }) - - vim.api.nvim_buf_set_option(float.bufnr, "filetype", syntax) - vim.api.nvim_buf_set_lines(float.bufnr, 0, -1, false, out) - end - - function LspPhpactorDumpConfig() - local results, _ = vim.lsp.buf_request_sync(0, "phpactor/debug/config", {["return"]=true}) - for _, res in pairs(results or {}) do - pcall(showWindow, 'Phpactor LSP Configuration', 'json', res['result']) - end - end - function LspPhpactorStatus() - local results, _ = vim.lsp.buf_request_sync(0, "phpactor/status", {["return"]=true}) - for _, res in pairs(results or {}) do - pcall(showWindow, 'Phpactor Status', 'markdown', res['result']) - end - end - - function LspPhpactorBlackfireStart() - local _, _ = vim.lsp.buf_request_sync(0, "blackfire/start", {}) - end - function LspPhpactorBlackfireFinish() - local _, _ = vim.lsp.buf_request_sync(0, "blackfire/finish", {}) - end diff --git a/doc/lsp/vim.rst b/doc/lsp/vim.rst deleted file mode 100644 index 36fb825b37..0000000000 --- a/doc/lsp/vim.rst +++ /dev/null @@ -1,153 +0,0 @@ -VIM / NeoVim -============ - -.. _lsp_client_vim: - -Client Guides -------------- - - -.. tabs:: - - .. tab:: Neovim LSP - - Prerequisites: - - Neovim 0.11.0 or higher. - - The ``phpactor`` binary is :ref:`installed` and executable in your path - - For example: include it in your ``init.lua``: - - :: - - vim.lsp.enable('phpactor') - - Then in ``~/.config/nvim/`` (or another `&runtimepath` component) create ``lsp/phpactor.lua``, for example: - - :: - - return { - cmd = { 'phpactor', 'language-server' }, - filetypes = { 'php' }, - root_markers = { '.git', 'composer.json', '.phpactor.json', '.phpactor.yml' }, - workspace_required = true, - init_options = { - ["language_server_phpstan.enabled"] = false, - ["language_server_psalm.enabled"] = false, - } - } - - - The ``init_options`` key maps directly to Phpactors :ref:`ref_configuration`. - - Please refer to the (``:help lsp``). - - See :doc:`vim-lsp` for useful snippets (e.g. reindex, show config, etc). - - .. tab:: CoC - - - Install Phpactor with :ref:`installation_global` then install `CoC - `_: - - :: - - Plug 'neoclide/coc.nvim', {'branch': 'release'} - - Once you have both installed there are two ways of integrating `phpactor` into `coc`: - - - **Installing the coc phpactor extension**: - - Restart VIM and type ``:CocInstall coc-phpactor``. - - If Phpactor is already installed you can set ``phpactor.path`` in - ``:CocConfig`` to point to the Phpactor binary. - - At the root level: - - :: - - { - "phpactor.enable": true, - "phpactor.path": "/home/vivo/phpactor/bin/phpactor" - } - - - **Without phpactor extension**: - - Restart VIM and type `:CocConfig`, you can set up phpactor as a language server for php files directly to `coc-settings.json`: - - :: - - "languageserver": { - "phpactor": { - "command": "phpactor", - "args": ["language-server"], - "trace.server": "verbose", - "filetypes": ["php"] - } - } - - I am using the following CoC key bindings and configuration: - - :: - - " Select range based on AST - nmap r (coc-range-select) - xmap r (coc-range-select) - - " Navigations - nmap o (coc-definition) - nmap O (coc-type-definition) - nmap I (coc-implementation) - nmap R (coc-references) - - " List code actions available for the current buffer - nmap ca (coc-codeaction) - - " Use to validate completion (allows auto import on completion) - inoremap pumvisible() ? "\" : "\u\" - - " Hover - nmap K :call show_documentation() - function! s:show_documentation() - if (index(['vim','help'], &filetype) >= 0) - execute 'h '.expand('') - else - call CocAction('doHover') - endif - endfunction - - " Text objects for functions and classes (uses document symbol provider) - xmap if (coc-funcobj-i) - omap if (coc-funcobj-i) - xmap af (coc-funcobj-a) - omap af (coc-funcobj-a) - xmap ic (coc-classobj-i) - omap ic (coc-classobj-i) - xmap ac (coc-classobj-a) - omap ac (coc-classobj-a) - autocmd CursorHold * silent call CocActionAsync('highlight') - - See `coc-phpactor `_ for more - information. - -Troubleshooting ---------------- - -Two dollars on variables -~~~~~~~~~~~~~~~~~~~~~~~~ - -This can happen because of the ``iskeyword`` setting in VIM. - -You can try adding ``$`` to the list of keywords to solve the problem: - -:: - - autocmd FileType php set iskeyword+=$ - -or configure Phpactor to trim the ``$`` prefix in ``.phpactor.json``: - -:: - - { - "language_server_completion.trim_leading_dollar": true - } diff --git a/doc/lsp/vscode.rst b/doc/lsp/vscode.rst deleted file mode 100644 index 40463ebbf1..0000000000 --- a/doc/lsp/vscode.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _lsp_client_vscode: - -VS Code -======= - -Phpactor provides the `phpactor-vscode `_ extension. diff --git a/doc/lsp/zed.rst b/doc/lsp/zed.rst deleted file mode 100644 index 0ca2f6785c..0000000000 --- a/doc/lsp/zed.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _lsp_client_zed: - -Zed -=== - -Phpactor is the default LSP for Zed. Install Zed's `PHP extension `_. diff --git a/doc/other.rst b/doc/other.rst deleted file mode 100644 index cbf073e9d6..0000000000 --- a/doc/other.rst +++ /dev/null @@ -1,9 +0,0 @@ -Other Topics -============ - -.. toctree:: - :maxdepth: 2 - :glob: - - other/* - diff --git a/doc/other/links.rst b/doc/other/links.rst deleted file mode 100644 index d5bdb1c68e..0000000000 --- a/doc/other/links.rst +++ /dev/null @@ -1,23 +0,0 @@ -Links and Resources -=================== - -This page collects links to resources about, or featuring, Phpactor. - -General -------- - -- `Extending - Phpactor `__ -- `3 years of - Phpactor `__ - -VIM Guides ----------- - -- `VIM as an alternative to - PHPStorm `__ -- `VIM for PHP `__ -- `Neovim and PHP `__ -- `VimでPHPを書くならPhpactorを使うと便利そう(入力補完・ジャンプ・リファクタリング) `__ -- `Switchig from VIM to - Neovim `__ diff --git a/doc/other/rpc.rst b/doc/other/rpc.rst deleted file mode 100644 index c24ebf4b67..0000000000 --- a/doc/other/rpc.rst +++ /dev/null @@ -1,495 +0,0 @@ -.. _rpc_protocol: - -RPC Protocol -============ - -.. container:: alert alert-danger - - This document should largely be correct but is a work-in-progress - -Phpactor communicates with the editor over its own RPC protocol which -effectively allows the editor to instruct Phpactor to do things, and in -turn Phpactor can also instruct the editor to do things. - -.. figure:: https://user-images.githubusercontent.com/530801/30521464-39743352-9bc0-11e7-92ac-06b3228adf67.png - :alt: rpc - - rpc - -Requests can be sent via. ``stdin`` to the Phpactor ``rpc`` command - -.. code:: - - $ echo '{"action": "echo", "parameters": { "message": "Hello" }}' | ./bin/phpactor rpc - -Above we use the somewhat pointless ``echo`` action, which simply -returns a response asking the editor to echo a message. The result is -sent over ``stdout``: - -.. code:: javascript - - {"action":"echo","parameters":{"message":"Hello"}} - -Responses contain an action which will be executed in the editor. - -Some responses will include callbacks to Phpactor. This allows Phpactor -to ask for more information as required. - -Work-in-progress ----------------- - -This Protocol is a work-in-progress. - -TODO: - -- Parameters should not refer to file paths in class contexts: Although - classes map 1-1 to files now, this may not always be the case and - Phpactor can figure out a file-path from a class-name and vice-versa - (so ``class`` instead of ``class_path``). - -Editor Actions --------------- - -``return`` -~~~~~~~~~~ - -Return a value. This action should return the value immediately to the -caller without further dispatches. - -- **Name**: ``return`` -- **Parameters**: - - - **Value**: Value to return. - -``return_choice`` -~~~~~~~~~~~~~~~~~ - -The editor should render a dialog asking the user to select a choice: - -:: - - 1) Do this - 2) Do that - -It should then ``return`` the chosen value. - -- **Name**: ``return_choice`` -- **Parameters**: - - - ``choices``: Key to value list of choices, the key is the label - for the choice, the value is the value to return. - -``echo`` -~~~~~~~~ - -The editor should echo (or otherwise visibly display) a message. - -- **Name**: ``echo`` -- **Parameters**: - - - ``message``: Message to display. - -``error`` -~~~~~~~~~ - -The editor should show an error. - -- **Name**: ``error`` -- **Parameters**: - - - ``message``: Error message to display. - -``collection`` -~~~~~~~~~~~~~~ - -Collection of actions to be executed sequentially until there are no -more or until a ``return`` is encountered. - -- **Name**: ``collection`` -- **Parameters**: - - - ``actions``: Array of actions to dispatch. - -``open_file`` -~~~~~~~~~~~~~ - -Open a file in the editor. - -- **Name**: ``open_file`` -- **Parameters**: - - - ``path``: Path to file which should be opened. - - ``offset``: Goto this offset after opening the file. - -``close_file`` -~~~~~~~~~~~~~~ - -Close a file. - -- **Name**: ``close_file`` -- **Parameters**: - - - ``path``: Path to file which should be closed. - -``file_references`` -~~~~~~~~~~~~~~~~~~~ - -List of files and references. The editor should populate a navigable -list of the files and the references (in VIM this is a quick-fix list). - -- **Name**: ``file_references`` -- **Parameters**: - - - ``references``: Array containing file paths nested with - references: - -Example of ``references`` parameter: - -.. code:: javascript - - { - "file_references": [ - { - "file": "/path/to/File.php", - "references": [ - { - "line_no": 1234, - "col": 12 - }, - { - "line_no": 1234, - "col": 12 - } - ] - } - ] - } - -``input_callback`` -~~~~~~~~~~~~~~~~~~ - -This action will provide a callback to Phpactor and inputs which the -end-user will need to complete to populate the parameters of the -callback. - -- **Name**: ``input_callback`` -- **Parameters**: - - - ``callback``: - - - ``action``: Callback command name for Phpactor. - - ``parameters``: Array of parameters to pass. - - - ``inputs``: Array of inputs - -Example: - -.. code:: javascript - - { - "callback": { - "action": "hello", - "parameters": { - "greeting": "Hello", - "first_name": "value1" - } - } - "inputs": [ - { - "name": "first_name", - "type": "text", - "parameters": { - "default": "", - } - } - ] - } - -See the VIM plugin for the supported inputs. - -``information`` -~~~~~~~~~~~~~~~ - -Show information in a persistent and unobtrusive way (in VIM as a -preview window). - -- **Name**: ``information`` -- **Parameters**: - - - ``information``: Information to show (text) - -``replace_file_source`` -~~~~~~~~~~~~~~~~~~~~~~~ - -Replace the source code in the current file. - -- **Name**: ``replace_file_source`` -- **Parameters**: - - - ``source``: Source code. - -Phpactor Commands ------------------ - -In the following references all parameters are assumed to be required -unless otherwise stated. *optional* parameters are optional, *eventually -required* means the parameter is optional, but if not given Phpactor -will ask for it (via. a callback). - -Note that after implementing a dispatcher for standard editor actions -above it is not necessary to know the result of these commands, they -will be handled by your editor. - -``complete`` -~~~~~~~~~~~~ - -The complete RPC command returns a list of completions: - -.. code:: - - $ echo '{"actions": [ {"action": "complete", "parameters": { "source": "", "offset": 37} }] }' | ./bin/phpactor rpc - -Example response: - -:: - - { - "actions": [ - { - "action": "return", - "parameters": { - "value": [ - { - "info": "pri __clone(): void", - "name": "__clone", - "type": "f" - }, - { - "info": "pub getMessage(): string", - "name": "getMessage", - "type": "f" - }, - { - "info": "pub getCode()", - "name": "getCode", - "type": "f" - }, - ] - } - } - ] - } - -``class_search`` -~~~~~~~~~~~~~~~~ - -Searches for a class with a given class name. No need for the fully qualified class name. - -- **Name**: ``class_search`` -- **Parameters**: - - - ``short_name``: Name of the class that you want to find - -.. code:: - - $ echo '{"actions": [ {"action": "class_search", "parameters": { "short_name": "InputInterface" } }] }' | ./bin/phpactor rpc - -``goto_definition`` -~~~~~~~~~~~~~~~~~~~ - -Open file and goto offset of symbol at the given offset in the given -source code: - -- **Name**: ``goto_definition`` -- **Parameters**: - - - ``source``: Source code as a string - - ``offset``: Offset of symbol (int) - -.. code:: - - $ echo '{"actions": [ {"action": "goto_definition", "parameters": { "source": "getMessage()", "offset": 37} }] }' | ./bin/phpactor rpc - -Will return an action to open the file containing the definition and -goto the offset. - -``copy_class`` -~~~~~~~~~~~~~~ - -Copy a class to a new location and update its name accordingly: - -- **Name**: ``copy_class`` -- **Parameters**: - - - ``source_path``: File containing class to copy.. - - ``dest_path``: (eventually required) Destination path - -.. code:: - - $ echo '{"actions": [ {"action": "copy_class", "parameters": { "source_path": "/path/to/class.php" } } ] }| ./bin/phpactor rpc - -Will return an action to open the new file. - -``move_class`` -~~~~~~~~~~~~~~ - -Move a class and update all references to it in the project. - -- **Name**: ``move_class`` -- **Parameters**: - - - ``source_path``: File containing class to move. - - ``dest_path``: (eventually required) File to move class to. - -Will eventually return an action to open the new file, and a command to -forget about the old one. - -``offset_info`` -~~~~~~~~~~~~~~~ - -Return debug information about the symbol and the state of the frame at -the given offset. - -- **Name**: ``offset_info`` -- **Parameters**: - - - ``source``: Source code as a string - - ``offset``: Offset of symbol (int) - -Returns an (information) action to show a pretty-printed JSON string -containing the debug information. - -``transform`` -~~~~~~~~~~~~~ - -Perform a transformation on the class in the given file. - -- **Name**: ``transform`` -- **Parameters**: - - - ``source``: Source - - ``path``: Path for file containing class to transform (required - only in order to reload the file in the editor later). - - ``transform``: (eventually required) Name of transformation to - make. - -If no transformation Phpactor will offer a choice of available -transformations, then make the transformation and ask the editor to -reload the file. - -``class_new`` -~~~~~~~~~~~~~ - -Generate a new class. - -- **Name**: ``class_new`` -- **Parameters**: - - - ``current_path``: Path to current file (used as default for new - path) - - ``new_path``: (eventually required) Path to new class. - - ``variant``: (eventually required) Variant to create. - - ``overwrite``: (conditionally eventually required) If file already - exists then this should be true in order that it is overwritten. - -Will return an action to open the new file. - -``class_inflect`` -~~~~~~~~~~~~~~~~~ - -Generate a new class based on the current (given) class. - -- **Name**: ``class_new`` -- **Parameters**: - - - ``current_path``: Path to current file (used as default for new - path) - - ``new_path``: (eventually required) Path to new class. - - ``variant``: (eventually required) Variant to create. - - ``overwrite``: (conditionally eventually required) If file already - exists then this should be true in order that it is overwritten. - -Will return an action to open the new file. - -``references`` -~~~~~~~~~~~~~~ - -Find references to the symbol under the cursor. - -- **Name**: ``references`` -- **Parameters**: - - - ``source``: Source code as a string - - ``offset``: Offset of symbol (int) - -Will return a file-list action, containing a list of all the files in -which references can be found (and the position of all the references). - -``extract_constant`` -~~~~~~~~~~~~~~~~~~~~ - -Extract a constant from the value at the given offset and replace all -identical values with a reference to the constant. - -- **Name**: ``extract_constant`` -- **Parameters**: - - - ``source``: Source code as a string - - ``offset``: Offset of symbol (int) - - ``constant_name``: Name for constant. - - ``constant_suggestion_name``: (optional) Use this as a suggestion - when interactive. - -Will return an action to replace the file with the updated code. - -``generate_method`` -~~~~~~~~~~~~~~~~~~~ - -Generate (or update) a method from the **method call** at the given -offset in the given source. - -- **Name**: ``generate_method`` -- **Parameters**: - - - ``source``: Source code as a string - - ``path``: Path to source - - ``offset``: Offset of symbol (int) - -Will return an action to replace the file with the updated code. - -``generate_accessor`` -~~~~~~~~~~~~~~~~~~~~~ - -Generate (or update) an accessor for the property under the cursor. - -- **Name**: ``generate_accessor`` -- **Parameters**: - - - ``source``: Source code as a string - - ``offset``: Offset of symbol (int) - -Will return an action to replace the file with the updated code. - -``context_menu`` -~~~~~~~~~~~~~~~~ - -Return a menu for selecting an action to perform on the current symbol - -- **Name**: ``context_menu`` -- **Parameters**: - - - ``source``: Source code as a string - - ``offset``: Offset of symbol (int) - -``navigate`` -~~~~~~~~~~~~ - -Navigate to related source files. - -- **Name**: ``navigate`` -- **Parameters**: - - - ``source_path``: Source code as a string - - ``destination``: (eventually required) Destination path - - ``confirm_create``: (conditionally eventually required) Confirm - file creation diff --git a/doc/other/support.rst b/doc/other/support.rst deleted file mode 100644 index 897f2a28ce..0000000000 --- a/doc/other/support.rst +++ /dev/null @@ -1,7 +0,0 @@ -Support -======= - -Need help? Create a Github issue or ping the Phpactor Mastodon account: - -- `Github Issues `__ -- `Mastodon [@phpactor] `__ diff --git a/doc/phpactor.txt b/doc/phpactor.txt deleted file mode 100644 index 661ccbd3f0..0000000000 --- a/doc/phpactor.txt +++ /dev/null @@ -1,280 +0,0 @@ -*phpactor.txt* - *phpactor* - -============================================================================== -CONTENTS *phpactor-contents* - 1. Introduction.............................................|phpactor-intro| - 2. Configuration...........................................|phpactor-config| - 3. Completion..........................................|phpactor-completion| - 4. Commands..............................................|phpactor-commands| - 1. Window targets..............................|phpactor-window-targets| - 5. Mappings..............................................|phpactor-mappings| - -============================================================================== -INTRODUCTION *phpactor-intro* - - -Phpactor is a auto-completion, refactoring and code-navigation tool for PHP. -This is the help file for the VIM client. For more information see the -official website: https://phpactor.github.io/phpactor/ - -NOTE: This help is auto-generated from the VimScript using -https://github.com/google/vimdoc. See -https://phpactor.github.io/phpactor/developing.html#vim-help - -============================================================================== -CONFIGURATION *phpactor-config* - - *g:phpactorPhpBin* -Path to the PHP binary used by Phpactor - - *g:phpactorBranch* -The Phpactor branch to use when calling |:PhpactorUpdate| - - *g:phpactorOmniAutoClassImport* -Automatically import classes when using VIM native omni-completion - - *g:phpactorCompletionIgnoreCase* -Ignore case when suggestion completion results - - *g:phpactorQuickfixStrategy* -Function to use when populating a list of code references. The default is to -use the VIM quick-fix list. - - *g:phpactorInputListStrategy* -Function to use when presenting a user with a choice of options. The default -is to use the VIM inputlist. - - *g:phpactorUseOpenWindows* -When jumping to a file location: if the target file open in a window, switch -to that window instead of switching buffers. The default is false. - - *g:PhpactorRootDirectoryStrategy* -Each Phpactor request requires the project's root directory to be known. By -default it will assume the directory in which you started VIM, but this may -not suit all workflows. - -This setting allows |Funcref| to be specified. This function should return the -working directory in whichever way is required. No arguments are passed to -this function. - -============================================================================== -COMPLETION *phpactor-completion* - - -You will need to explicitly configure Phpactor to provide completion -capabilities. - -OMNI-COMPLETION - -Use VIMs native omni-completion (|compl-omni|) - -Enable omni-completion for PHP files: -> - - autocmd FileType php setlocal omnifunc=phpactor#Complete - -< -For case sensitive searching see |g:phpactorCompletionIgnoreCase| - -NCM2 - -Nvim Completion Manager is a completion manager for Neovim. - -Install the integration plugin to get started: -https://github.com/phpactor/ncm2-phpactor - -DEOPLETE - -Deoplete is another completion plugin. - -Install the Deoplete Phpactor integration to get started: -https://github.com/kristijanhusak/deoplete-phpactor - -============================================================================== -COMMANDS *phpactor-commands* - -:[range][N]PhpactorExtractMethod *:PhpactorExtractMethod* - Extract a new method from the current selection - -:[range][N]PhpactorExtractExpression *:PhpactorExtractExpression* - Extract the selected expression and assign it to a variable before (placing - it before the current statement) - -:[N]PhpactorExtractConstant *:PhpactorExtractConstant* - Extract a constant from a literal - -:[N]PhpactorImportClass *:PhpactorImportClass* - Import the name under the cursor. If multiple options are available, you are - able to choose one. - -:[N]PhpactorImportMissingClasses *:PhpactorImportMissingClasses* - Attempt to import all non-resolvable classes in the current class (based on - offset position) - -:[N]PhpactorHover *:PhpactorHover* - Show information about the symbol under the cursor. - -:[N]PhpactorContextMenu *:PhpactorContextMenu* - Show the context menu for the current cursor position. - -:[N]PhpactorCopyFile *:PhpactorCopyFile* - Copy the current file - updating the namespace and class name according to - the new file location and name - -:[N]PhpactorCopyClassName *:PhpactorCopyClassName* - Copy the current class FQN (based on current filename) to the clipboard - -:[N]PhpactorMoveFile *:PhpactorMoveFile* - Move the current file - updating the namespace and class name according to - the new file location and name - -:[N]PhpactorClassInflect *:PhpactorClassInflect* - Inflect a new class from the current class (e.g. generate an interface for - the current class) - -:[N]PhpactorFindReferences *:PhpactorFindReferences* - Attempt to find all references to the class name or method under the cursor. - The results will be loaded into the quik-fix list - -:[N]PhpactorNavigate *:PhpactorNavigate* - Navigate - jump to the parent class, interface, or any of the relationships - defined in `navigation.destinations` - https://phpactor.github.io/phpactor/configuration.html#reference - -:[N]PhpactorChangeVisibility *:PhpactorChangeVisibility* - Rotate the visiblity of the method under the cursor - -:[N]PhpactorGenerateAccessors *:PhpactorGenerateAccessors* - Generate accessors for the current class - -:[N]PhpactorGenerateMutators *:PhpactorGenerateMutators* - Generate mutators for the current class - -:[N]PhpactorTransform *:PhpactorTransform* - Automatically add any missing properties to a class - -:[N]PhpactorTrust *:PhpactorTrust* - Trust configuration in the current working directory - -:PhpactorUpdate *:PhpactorUpdate* - Update Phpactor to the latest version using the branch defined with - |g:phpactorBranch| - -:PhpactorCacheClear *:PhpactorCacheClear* - Clear the entire cache - this will take effect for all projects. - -:PhpactorStatus *:PhpactorStatus* - Show some information about Phpactor's status - -:PhpactorConfig *:PhpactorConfig* - Dump Phpactor's configuration - -:PhpactorClassExpand *:PhpactorClassExpand* - Expand the class name under the cursor to it's fully-qualified-name - -:PhpactorClassNew *:PhpactorClassNew* - Create a new class. You will be offered a choice of templates. - -:PhpactorGotoDefinition [target] *:PhpactorGotoDefinition* - [target] is `edit` if omitted. - - Goto the definition of the symbol under the cursor. Opens in the [target] - window, see |phpactor-window-target| for the list of possible targets. - || can be provided to the command to change how the window will be - opened. - - Examples: -> - " Opens in the current buffer - PhpactorGotoDefinition - - " Opens in a vertical split opened on the right side - botright PhpactorGotoDefinition vsplit - vertical botright PhpactorGotoDefinition split - - " Opens in a new tab - PhpactorGotoDefinition tabnew -< - -:PhpactorGotoDefinitionVsplit *:PhpactorGotoDefinitionVsplit* - deprecated, use |:PhpactorGotoDefinition| instead - - As with |:PhpactorGotoDefinition| but open in a vertical split. - -:PhpactorGotoDefinitionHsplit *:PhpactorGotoDefinitionHsplit* - deprecated, use |:PhpactorGotoDefinition| instead - - As with |:PhpactorGotoDefinition| but open in an horizontal split. - -:PhpactorGotoDefinitionTab *:PhpactorGotoDefinitionTab* - deprecated, use |:PhpactorGotoDefinition| instead - - As with |:PhpactorGotoDefinition| but open in a new tab. - -:PhpactorGotoType [target] *:PhpactorGotoType* - - Same as |:PhpactorGotoDefinition| but goto the type of the symbol under the - cursor. - -:PhpactorGotoImplementations [target] *:PhpactorGotoImplementations* - - Same as |:PhpactorGotoDefinition| but goto the implementation of the symbol - under the cursor. - - If there is more than one result the quickfix strategy will be used and - [target] will be ignored, see |g:phpactorQuickfixStrategy|. - -============================================================================== -WINDOW TARGETS *phpactor-window-targets* - - -Phpactor provide a few window targets to use with some commands. See -|:PhpactorGotoDefinition| for an example of how to use them. - -Possible values are: - * `e`, `edit`, `ex` - * `new`, `vne`, `vnew` - * `sp`, `split`, `vs`, `vsplit` - * `vie`, `view`, `sv`, `sview`, `splitview` - * `tabe`, `tabedit`, `tabnew` - -============================================================================== -MAPPINGS *phpactor-mappings* - - -Phpactor does not assume any mappings automatically, the following mappings -are available for you to copy: -> - - augroup PhpactorMappings - au! - au FileType php nmap u :PhpactorImportClass - au FileType php nmap e :PhpactorClassExpand - au FileType php nmap ua :PhpactorImportMissingClasses - au FileType php nmap mm :PhpactorContextMenu - au FileType php nmap nn :PhpactorNavigate - au FileType php,cucumber nmap o - \ :PhpactorGotoDefinition edit - au FileType php nmap K :PhpactorHover - au FileType php nmap tt :PhpactorTransform - au FileType php nmap cc :PhpactorClassNew - au FileType php nmap ci :PhpactorClassInflect - au FileType php nmap fr :PhpactorFindReferences - au FileType php nmap mf :PhpactorMoveFile - au FileType php nmap cf :PhpactorCopyFile - au FileType php nmap ee - \ :PhpactorExtractExpression - au FileType php vmap ee - \ :PhpactorExtractExpression - au FileType php vmap em - \ :PhpactorExtractMethod - augroup END -< - -Note: the cucumber mappings are for the Behat extension: - - https://github.com/phpactor/behat-extension - - -vim:tw=78:ts=8:ft=help:norl: diff --git a/doc/reference.rst b/doc/reference.rst deleted file mode 100644 index 2d74f87375..0000000000 --- a/doc/reference.rst +++ /dev/null @@ -1,9 +0,0 @@ -Reference -========= - -.. toctree:: - :maxdepth: 2 - :glob: - - reference/* - language-server diff --git a/doc/reference/completion.rst b/doc/reference/completion.rst deleted file mode 100644 index 3f49982186..0000000000 --- a/doc/reference/completion.rst +++ /dev/null @@ -1,328 +0,0 @@ -.. _completion: - -Completion -========== - -.. contents:: - :depth: 2 - :backlinks: none - :local: - -Completors ----------- - -.. note:: - - Completors can be enabled or disabled in configuration f.e. :ref:`param_completion_worse.completor.worse_parameter.enabled` configuration. - -``worse_parameter`` -~~~~~~~~~~~~~~~~~~~ - -Provides suggestions for arguments. - -If there are suitable variables in the current scope they will be given -priority if they match the type of the method parameter. - -.. code:: php - - `` ``$foobar`` will be suggested with a higher -priority than ``$barfoo`` and the parameter index will be shown in the -completion description. - -``worse_constructor`` -~~~~~~~~~~~~~~~~~~~~~ - -As with ``worse_parameter`` but for constructor arguments. - -``worse_class_member`` -~~~~~~~~~~~~~~~~~~~~~~ - -Provides class member (methods, properties and constants) suggestions. -Triggered on ``::`` and ``->``. - -``indexed_name`` -~~~~~~~~~~~~~~~~ - -Provides class and function name completion from the :ref:`indexer`. - -``scf_class`` -~~~~~~~~~~~~~ - -This completor will provide class names by *scanning the vendor directory* and -transposing the file names into class names. - -This completor is disabled by default when using the :ref:`language_server`. - -``worse_local_variable`` -~~~~~~~~~~~~~~~~~~~~~~~~ - -Provide completion for local variables in a scope. Triggered on ``$``. - -``declared_function`` -~~~~~~~~~~~~~~~~~~~~~ - -Provide function name completion based on functions defined at _runtime_ in the -Phpactor process. - -Note that any functions which are not loaded when _Phpactor_ -loads will not be available. So this is mainly useful for built-in functions. - -This completor is disabled by default when using the :ref:`language_server`. - -``declared_constant`` -~~~~~~~~~~~~~~~~~~~~~~ - -Provide constant name completion based on constants defined at _runtime_ in the -Phpactor process. - -This is mainly useful for built-in constants (e.g. ``JSON_PRETTY_PRINT`` or -``PHP_INT_MAX``). - -``worse_class_alias`` -~~~~~~~~~~~~~~~~~~~~~~ - -Provide suggestions for any classes imported into the current class with -aliases. - - -``declared_class`` -~~~~~~~~~~~~~~~~~~ - -Provide completion for class names from class names defined in the Phpactor -process. - -This is mainly useful when used with the ``scf_class`` completor to provide -built-in classes. - -This completor is disabled by default when using the :ref:`language_server`. - -Type inference --------------- - -Assert -~~~~~~ - -When encountering an ``assert`` with ``instanceof`` it will cast the -variable to that type, or a union of that type. See also -`#instanceof <#instanceof>`__. - -.. code:: php - - // type: Hello|Goodbye - -Assignments -~~~~~~~~~~~ - -Phpactor will track assignemnts: - -.. code:: php - - // type: MyException - } - -Docblocks -~~~~~~~~~ - -Docblocks are supported for method parameters, return types, class properties -and inline declartaions - -.. code:: php - - - */ - private $iterableOfMyThing; - - -Foreach -~~~~~~~ - -Understands ``foreach`` with the docblock array annotation: - -.. code:: php - - // type:Hello - } - -Also understands simple generics: - -.. code:: php - - $foos */ - $foos = new ArrayIterator([ new Hello() ]); - - foreach ($foos as $foo) { - $foo-> // type:Hello - } - -FunctionLike -~~~~~~~~~~~~ - -Understands anonymous functions: - -.. code:: php - - // type: Foobar - $barfoo-> // type: Barfoo - } - -InstanceOf -~~~~~~~~~~ - -``if`` statements are evaluated, if they contain ``instanceof`` then the -type is inferred: - -.. code:: php - - // type: Hello - } - -.. code:: php - - // type: Hello - -.. code:: php - - // type: Hello|Goodbye - } - -Variables -~~~~~~~~~ - -Phpactor supports type injection via docblock: - -.. code:: php - - // type: Foobar - -and inference from parameters: - -.. code:: php - - - */ - interface ReflectionCollection extends \IteratorAggregate, \Countable - { - } - - /** - * @template T of ReflectionMember - * @extends ReflectionCollection - */ - interface ReflectionMemberCollection extends ReflectionCollection - { - /** - * @return ReflectionMemberCollection - */ - public function byName(string $name): ReflectionMemberCollection; - - /** - * @return ReflectionMemberCollection - */ - public function byMemberType(string $type): ReflectionMemberCollection; - } - - interface ReflectionClassLike - { - public function members(): ReflectionMemberCollection; - } - - - /** @var ReflectionClassLike $reflection */ - $reflection; - foreach ($reflection->members()->byMemberType('fii')->byName('__construct') as $constructor) { - $reflection-><> - } diff --git a/doc/reference/configuration.rst b/doc/reference/configuration.rst deleted file mode 100644 index d41a2f243d..0000000000 --- a/doc/reference/configuration.rst +++ /dev/null @@ -1,2680 +0,0 @@ -.. _ref_configuration: - -Configuration -============= - - -.. This document is generated via the `development:generate-documentation` command - - -.. contents:: - :depth: 2 - :backlinks: none - :local: - - -.. _CoreExtension: - - -CoreExtension -------------- - - -.. _param_console_dumper_default: - - -``console_dumper_default`` -"""""""""""""""""""""""""" - - -Name of the "dumper" (renderer) to use for some CLI commands - - -**Default**: ``"indented"`` - - -.. _param_xdebug_disable: - - -``xdebug_disable`` -"""""""""""""""""" - - -If XDebug should be automatically disabled - - -**Default**: ``true`` - - -.. _param_command: - - -``command`` -""""""""""" - - -Internal use only - name of the command which was executed - - -**Default**: ``null`` - - -.. _param_core.min_memory_limit: - - -``core.min_memory_limit`` -""""""""""""""""""""""""" - - -Ensure that PHP has a memory_limit of at least this amount in bytes - - -**Default**: ``1610612736`` - - -.. _param_$schema: - - -``$schema`` -""""""""""" - - -Path to JSON schema, which can be used for config autocompletion, use phpactor config:initialize to update - - -**Default**: ``""`` - - -.. _param_core.project_config_candidates: - - -``core.project_config_candidates`` -"""""""""""""""""""""""""""""""""" - - -(internal) list of potential project-level configuration files - - -**Default**: ``[]`` - - -.. _param_core.trust: - - -``core.trust`` -"""""""""""""" - - -(internal) map of trusted project directories - - -**Default**: ``{"trust":[],"path":null}`` - - -.. _param_core.trusted: - - -``core.trusted`` -"""""""""""""""" - - -(internal) if the configuration is trusted - - -**Default**: ``false`` - - -.. _ClassToFileExtension: - - -ClassToFileExtension --------------------- - - -.. _param_class_to_file.project_root: - - -``class_to_file.project_root`` -"""""""""""""""""""""""""""""" - - -Root path of the project (e.g. where composer.json is) - - -**Default**: ``"%project_root%"`` - - -.. _param_class_to_file.brute_force_conversion: - - -``class_to_file.brute_force_conversion`` -"""""""""""""""""""""""""""""""""""""""" - - -If composer not found, fallback to scanning all files (very time consuming depending on project size) - - -**Default**: ``true`` - - -.. _CodeTransformExtension: - - -CodeTransformExtension ----------------------- - - -.. _param_code_transform.class_new.variants: - - -``code_transform.class_new.variants`` -""""""""""""""""""""""""""""""""""""" - - -Variants which should be suggested when class-create is invoked - - -**Default**: ``[]`` - - -.. _param_code_transform.template_paths: - - -``code_transform.template_paths`` -""""""""""""""""""""""""""""""""" - - -Paths in which to look for code templates - - -**Default**: ``["%project_config%\/templates","%config%\/templates"]`` - - -.. _param_code_transform.indentation: - - -``code_transform.indentation`` -"""""""""""""""""""""""""""""" - - -Indentation chars to use in code generation and transformation - - -**Default**: ``" "`` - - -.. _param_code_transform.refactor.generate_accessor.prefix: - - -``code_transform.refactor.generate_accessor.prefix`` -"""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Prefix to use for generated accessors - - -**Default**: ``""`` - - -.. _param_code_transform.refactor.generate_accessor.upper_case_first: - - -``code_transform.refactor.generate_accessor.upper_case_first`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -If the first letter of a generated accessor should be made uppercase - - -**Default**: ``false`` - - -.. _param_code_transform.refactor.generate_mutator.prefix: - - -``code_transform.refactor.generate_mutator.prefix`` -""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Prefix to use for generated mutators - - -**Default**: ``"set"`` - - -.. _param_code_transform.refactor.generate_mutator.upper_case_first: - - -``code_transform.refactor.generate_mutator.upper_case_first`` -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -If the first letter of a generated mutator should be made uppercase - - -**Default**: ``true`` - - -.. _param_code_transform.refactor.generate_mutator.fluent: - - -``code_transform.refactor.generate_mutator.fluent`` -""""""""""""""""""""""""""""""""""""""""""""""""""" - - -If the mutator should be fluent - - -**Default**: ``false`` - - -.. _param_code_transform.import_globals: - - -``code_transform.import_globals`` -""""""""""""""""""""""""""""""""" - - -Import functions even if they are in the global namespace - - -**Default**: ``false`` - - -.. _param_code_transform.refactor.object_fill.hint: - - -``code_transform.refactor.object_fill.hint`` -"""""""""""""""""""""""""""""""""""""""""""" - - -Object fill refactoring: show hint as a comment - - -**Default**: ``true`` - - -.. _param_code_transform.refactor.object_fill.named_parameters: - - -``code_transform.refactor.object_fill.named_parameters`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Object fill refactoring: use named parameters - - -**Default**: ``true`` - - -.. _CompletionWorseExtension: - - -CompletionWorseExtension ------------------------- - - -.. _param_completion_worse.completor.doctrine_annotation.enabled: - - -``completion_worse.completor.doctrine_annotation.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``doctrine_annotation`` completor. - -Completion for annotations provided by the Doctrine annotation library. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.imported_names.enabled: - - -``completion_worse.completor.imported_names.enabled`` -""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``imported_names`` completor. - -Completion for names imported into the current namespace. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.worse_parameter.enabled: - - -``completion_worse.completor.worse_parameter.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``worse_parameter`` completor. - -Completion for method or function parameters. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.named_parameter.enabled: - - -``completion_worse.completor.named_parameter.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``named_parameter`` completor. - -Completion for named parameters. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.constructor.enabled: - - -``completion_worse.completor.constructor.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``constructor`` completor. - -Completion for constructors. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.class_member.enabled: - - -``completion_worse.completor.class_member.enabled`` -""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``class_member`` completor. - -Completion for class members. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.scf_class.enabled: - - -``completion_worse.completor.scf_class.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``scf_class`` completor. - -Brute force completion for class names (not recommended). - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.local_variable.enabled: - - -``completion_worse.completor.local_variable.enabled`` -""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``local_variable`` completor. - -Completion for local variables. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.subscript.enabled: - - -``completion_worse.completor.subscript.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``subscript`` completor. - -Completion for subscript (array access from array shapes). - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.declared_function.enabled: - - -``completion_worse.completor.declared_function.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``declared_function`` completor. - -Completion for functions defined in the Phpactor runtime. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.declared_constant.enabled: - - -``completion_worse.completor.declared_constant.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``declared_constant`` completor. - -Completion for constants defined in the Phpactor runtime. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.declared_class.enabled: - - -``completion_worse.completor.declared_class.enabled`` -""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``declared_class`` completor. - -Completion for classes defined in the Phpactor runtime. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.expression_name_search.enabled: - - -``completion_worse.completor.expression_name_search.enabled`` -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``expression_name_search`` completor. - -Completion for class names, constants and functions at expression positions that are located in the index. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.use.enabled: - - -``completion_worse.completor.use.enabled`` -"""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``use`` completor. - -Completion for use imports. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.attribute.enabled: - - -``completion_worse.completor.attribute.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``attribute`` completor. - -Completion for attribute class names. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.class_like.enabled: - - -``completion_worse.completor.class_like.enabled`` -""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``class_like`` completor. - -Completion for class like contexts. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.type.enabled: - - -``completion_worse.completor.type.enabled`` -""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``type`` completor. - -Completion for scalar types. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.keyword.enabled: - - -``completion_worse.completor.keyword.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``keyword`` completor. - -Completion for keywords (not very accurate). - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.docblock.enabled: - - -``completion_worse.completor.docblock.enabled`` -""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable or disable the ``docblock`` completor. - -Docblock completion. - - -**Default**: ``true`` - - -.. _param_completion_worse.completor.constant.enabled: - - -``completion_worse.completor.constant.enabled`` -""""""""""""""""""""""""""""""""""""""""""""""" - - -**Default**: ``false`` - - -.. _param_completion_worse.completor.class.limit: - - -``completion_worse.completor.class.limit`` -"""""""""""""""""""""""""""""""""""""""""" - - -Suggestion limit for the filesystem based SCF class_completor - - -**Default**: ``100`` - - -.. _param_completion_worse.name_completion_priority: - - -``completion_worse.name_completion_priority`` -""""""""""""""""""""""""""""""""""""""""""""" - - -Strategy to use when ordering completion results for classes and functions: - -- `proximity`: Classes and functions will be ordered by their proximity to the text document being edited. -- `none`: No ordering will be applied. - - -**Default**: ``"proximity"`` - - -.. _param_completion_worse.snippets: - - -``completion_worse.snippets`` -""""""""""""""""""""""""""""" - - -Enable or disable completion snippets - - -**Default**: ``true`` - - -.. _param_completion_worse.experimantal: - - -``completion_worse.experimantal`` -""""""""""""""""""""""""""""""""" - - -Enable experimental functionality - - -**Default**: ``false`` - - -.. _param_completion_worse.debug: - - -``completion_worse.debug`` -"""""""""""""""""""""""""" - - -Include debug info in completion results - - -**Default**: ``false`` - - -.. _CompletionExtension: - - -CompletionExtension -------------------- - - -.. _param_completion.dedupe: - - -``completion.dedupe`` -""""""""""""""""""""" - - -If results should be de-duplicated - - -**Default**: ``true`` - - -.. _param_completion.dedupe_match_fqn: - - -``completion.dedupe_match_fqn`` -""""""""""""""""""""""""""""""" - - -If ``completion.dedupe``, consider the class FQN in addition to the completion suggestion - - -**Default**: ``true`` - - -.. _param_completion.limit: - - -``completion.limit`` -"""""""""""""""""""" - - -Sets a limit on the number of completion suggestions for any request - - -**Default**: ``null`` - - -.. _param_completion.label_formatter: - - -``completion.label_formatter`` -"""""""""""""""""""""""""""""" - - -Definition of how to format entries in the completion list - - -**Default**: ``"helpful"`` - - -**Allowed values**: "helpful", "fqn" - - -.. _NavigationExtension: - - -NavigationExtension -------------------- - - -.. _param_navigator.destinations: - - -``navigator.destinations`` -"""""""""""""""""""""""""" - - -**Default**: ``[]`` - - -.. _param_navigator.autocreate: - - -``navigator.autocreate`` -"""""""""""""""""""""""" - - -**Default**: ``[]`` - - -.. _RpcExtension: - - -RpcExtension ------------- - - -.. _param_rpc.store_replay: - - -``rpc.store_replay`` -"""""""""""""""""""" - - -Should replays be stored? - - -**Default**: ``false`` - - -.. _param_rpc.replay_path: - - -``rpc.replay_path`` -""""""""""""""""""" - - -Path where the replays should be stored - - -**Default**: ``"%cache%\/replay.json"`` - - -.. _SourceCodeFilesystemExtension: - - -SourceCodeFilesystemExtension ------------------------------ - - -.. _param_source_code_filesystem.project_root: - - -``source_code_filesystem.project_root`` -""""""""""""""""""""""""""""""""""""""" - - -**Default**: ``"%project_root%"`` - - -.. _WorseReflectionExtension: - - -WorseReflectionExtension ------------------------- - - -.. _param_language_server_code_transform.import_globals: - - -``language_server_code_transform.import_globals`` -""""""""""""""""""""""""""""""""""""""""""""""""" - - -Show hints for non-imported global classes and functions - - -**Default**: ``false`` - - -.. _param_worse_reflection.enable_cache: - - -``worse_reflection.enable_cache`` -""""""""""""""""""""""""""""""""" - - -If reflection caching should be enabled - - -**Default**: ``true`` - - -.. _param_worse_reflection.cache_lifetime: - - -``worse_reflection.cache_lifetime`` -""""""""""""""""""""""""""""""""""" - - -If caching is enabled, limit the amount of time a cache entry can stay alive - - -**Default**: ``1`` - - -.. _param_worse_reflection.enable_context_location: - - -``worse_reflection.enable_context_location`` -"""""""""""""""""""""""""""""""""""""""""""" - - -If source code is passed to a ``Reflector`` then temporarily make it available as a -source location. Note this should NOT be enabled if the source code can be -located in another (e.g. when running a Language Server) - - -**Default**: ``true`` - - -.. _param_worse_reflection.cache_dir: - - -``worse_reflection.cache_dir`` -"""""""""""""""""""""""""""""" - - -Cache directory for stubs - - -**Default**: ``"%cache%\/worse-reflection"`` - - -.. _param_worse_reflection.stub_dir: - - -``worse_reflection.stub_dir`` -""""""""""""""""""""""""""""" - - -Location of the core PHP stubs - these will be scanned and cached on the first request - - -**Default**: ``"%application_root%\/vendor\/jetbrains\/phpstorm-stubs"`` - - -.. _param_worse_reflection.additive_stubs: - - -``worse_reflection.additive_stubs`` -""""""""""""""""""""""""""""""""""" - - -Additive stubs files relative to the project root. These stubs augment existing defininitions. - - -**Default**: ``[]`` - - -.. _param_worse_reflection.diagnostics.undefined_variable.suggestion_levenshtein_disatance: - - -``worse_reflection.diagnostics.undefined_variable.suggestion_levenshtein_disatance`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Type: integer - - -Levenshtein distance to use when suggesting corrections for variable names - - -**Default**: ``4`` - - -.. _FilePathResolverExtension: - - -FilePathResolverExtension -------------------------- - - -.. _param_file_path_resolver.project_root: - - -``file_path_resolver.project_root`` -""""""""""""""""""""""""""""""""""" - - -**Default**: ``"\/home\/daniel\/www\/phpactor\/phpactor"`` - - -.. _param_file_path_resolver.app_name: - - -``file_path_resolver.app_name`` -""""""""""""""""""""""""""""""" - - -**Default**: ``"phpactor"`` - - -.. _param_file_path_resolver.application_root: - - -``file_path_resolver.application_root`` -""""""""""""""""""""""""""""""""""""""" - - -**Default**: ``null`` - - -.. _param_file_path_resolver.enable_cache: - - -``file_path_resolver.enable_cache`` -""""""""""""""""""""""""""""""""""" - - -**Default**: ``true`` - - -.. _param_file_path_resolver.enable_logging: - - -``file_path_resolver.enable_logging`` -""""""""""""""""""""""""""""""""""""" - - -**Default**: ``true`` - - -.. _LoggingExtension: - - -LoggingExtension ----------------- - - -.. _param_logging.enabled: - - -``logging.enabled`` -""""""""""""""""""" - - -Type: boolean - - -**Default**: ``false`` - - -.. _param_logging.fingers_crossed: - - -``logging.fingers_crossed`` -""""""""""""""""""""""""""" - - -Type: boolean - - -**Default**: ``false`` - - -.. _param_logging.path: - - -``logging.path`` -"""""""""""""""" - - -Type: string - - -**Default**: ``"application.log"`` - - -.. _param_logging.level: - - -``logging.level`` -""""""""""""""""" - - -Type: string - - -**Default**: ``"warning"`` - - -**Allowed values**: "emergency", "alert", "critical", "error", "warning", "notice", "info", "debug" - - -.. _param_logger.name: - - -``logger.name`` -""""""""""""""" - - -Type: string - - -**Default**: ``"logger"`` - - -.. _param_logging.formatter: - - -``logging.formatter`` -""""""""""""""""""""" - - -**Default**: ``null`` - - -.. _ComposerAutoloaderExtension: - - -ComposerAutoloaderExtension ---------------------------- - - -.. _param_composer.enable: - - -``composer.enable`` -""""""""""""""""""" - - -Include of the projects autoloader to facilitate class location. Note that when including an autoloader code _may_ be executed. This option may be disabled when using the indexer - - -**Default**: ``true`` - - -.. _param_composer.autoloader_path: - - -``composer.autoloader_path`` -"""""""""""""""""""""""""""" - - -Path to project's autoloader, can be an array - - -**Default**: ``"%project_root%\/vendor\/autoload.php"`` - - -.. _param_composer.autoload_deregister: - - -``composer.autoload_deregister`` -"""""""""""""""""""""""""""""""" - - -Immediately de-register the autoloader once it has been included (prevent conflicts with Phpactor's autoloader). Some platforms may require this to be disabled - - -**Default**: ``true`` - - -.. _param_composer.class_maps_only: - - -``composer.class_maps_only`` -"""""""""""""""""""""""""""" - - -Register the composer class maps only, do not register the autoloader - RECOMMENDED - - -**Default**: ``true`` - - -.. _ConsoleExtension: - - -ConsoleExtension ----------------- - - -.. _param_console.verbosity: - - -``console.verbosity`` -""""""""""""""""""""" - - -Verbosity level - - -**Default**: ``32`` - - -**Allowed values**: 16, 32, 64, 128, 256 - - -.. _param_console.decorated: - - -``console.decorated`` -""""""""""""""""""""" - - -Whether to decorate messages (null for auto-guessing) - - -**Default**: ``null`` - - -**Allowed values**: true, false, null - - -.. _PhpExtension: - - -PhpExtension ------------- - - -.. _param_php.version: - - -``php.version`` -""""""""""""""" - - -Consider this value to be the project\'s version of PHP (e.g. `7.4`). If omitted -it will check `composer.json` (by the configured platform then the PHP requirement) before -falling back to the PHP version of the current process. - - -**Default**: ``null`` - - -.. _LanguageServerExtension: - - -LanguageServerExtension ------------------------ - - -.. _param_language_server.catch_errors: - - -``language_server.catch_errors`` -"""""""""""""""""""""""""""""""" - - -**Default**: ``true`` - - -.. _param_language_server.enable_workspace: - - -``language_server.enable_workspace`` -"""""""""""""""""""""""""""""""""""" - - -If workspace management / text synchronization should be enabled (this isn't required for some language server implementations, e.g. static analyzers) - - -**Default**: ``true`` - - -.. _param_language_server.session_parameters: - - -``language_server.session_parameters`` -"""""""""""""""""""""""""""""""""""""" - - -Phpactor parameters (config) that apply only to the language server session - - -**Default**: ``[]`` - - -.. _param_language_server.method_alias_map: - - -``language_server.method_alias_map`` -"""""""""""""""""""""""""""""""""""" - - -Allow method names to be re-mapped. Useful for maintaining backwards compatibility - - -**Default**: ``[]`` - - -.. _param_language_server.diagnostic_sleep_time: - - -``language_server.diagnostic_sleep_time`` -""""""""""""""""""""""""""""""""""""""""" - - -Amount of time to wait before analyzing the code again for diagnostics - - -**Default**: ``1000`` - - -.. _param_language_server.diagnostics_on_update: - - -``language_server.diagnostics_on_update`` -""""""""""""""""""""""""""""""""""""""""" - - -Perform diagnostics when the text document is updated - - -**Default**: ``true`` - - -.. _param_language_server.diagnostics_on_save: - - -``language_server.diagnostics_on_save`` -""""""""""""""""""""""""""""""""""""""" - - -Perform diagnostics when the text document is saved - - -**Default**: ``true`` - - -.. _param_language_server.diagnostics_on_open: - - -``language_server.diagnostics_on_open`` -""""""""""""""""""""""""""""""""""""""" - - -Perform diagnostics when opening a text document - - -**Default**: ``true`` - - -.. _param_language_server.diagnostic_providers: - - -``language_server.diagnostic_providers`` -"""""""""""""""""""""""""""""""""""""""" - - -Specify which diagnostic providers should be active (default to all) - - -**Default**: ``null`` - - -.. _param_language_server.diagnostic_outsource: - - -``language_server.diagnostic_outsource`` -"""""""""""""""""""""""""""""""""""""""" - - -If applicable diagnostics should be "outsourced" to a different process - - -**Default**: ``true`` - - -.. _param_language_server.code_action_outsource: - - -``language_server.code_action_outsource`` -""""""""""""""""""""""""""""""""""""""""" - - -Code actions will be "outsourced" to a different process - - -**Default**: ``true`` - - -.. _param_language_server.diagnostic_exclude_paths: - - -``language_server.diagnostic_exclude_paths`` -"""""""""""""""""""""""""""""""""""""""""""" - - -List of paths to exclude from diagnostics, e.g. `vendor/**/*` - - -**Default**: ``[]`` - - -.. _param_language_server.diagnostic_ignore_codes: - - -``language_server.diagnostic_ignore_codes`` -""""""""""""""""""""""""""""""""""""""""""" - - -Ignore diagnostics that have the codes listed here, e.g. ["fix_namespace_class_name"]. The codes match those shown in the LSP client. - - -**Default**: ``[]`` - - -.. _param_language_server.enable_trust_check: - - -``language_server.enable_trust_check`` -"""""""""""""""""""""""""""""""""""""" - - -Check to see if project path is trusted before loading configurations from it - - -**Default**: ``true`` - - -.. _param_language_server.file_events: - - -``language_server.file_events`` -""""""""""""""""""""""""""""""" - - -Register to receive file events - - -**Default**: ``true`` - - -.. _param_language_server.file_event_globs: - - -``language_server.file_event_globs`` -"""""""""""""""""""""""""""""""""""" - - -**Default**: ``["**\/*.php"]`` - - -.. _param_language_server.profile: - - -``language_server.profile`` -""""""""""""""""""""""""""" - - -Logs timing information for incoming LSP requests - - -**Default**: ``false`` - - -.. _param_language_server.trace: - - -``language_server.trace`` -""""""""""""""""""""""""" - - -Log incoming and outgoing messages (needs log formatter to be set to ``json``) - - -**Default**: ``false`` - - -.. _param_language_server.shutdown_grace_period: - - -``language_server.shutdown_grace_period`` -""""""""""""""""""""""""""""""""""""""""" - - -Amount of time (in milliseconds) to wait before responding to a shutdown notification - - -**Default**: ``200`` - - -.. _param_language_server.phpactor_bin: - - -``language_server.phpactor_bin`` -"""""""""""""""""""""""""""""""" - - -Internal use only - name path to Phpactor binary - - -**Default**: ``"\/home\/daniel\/www\/phpactor\/phpactor\/lib\/Extension\/LanguageServer\/..\/..\/..\/bin\/phpactor"`` - - -.. _param_language_server.self_destruct_timeout: - - -``language_server.self_destruct_timeout`` -""""""""""""""""""""""""""""""""""""""""" - - -Wait this amount of time (in milliseconds) after a shutdown request before self-destructing - - -**Default**: ``2500`` - - -.. _param_language_server.diagnostic_outsource_timeout: - - -``language_server.diagnostic_outsource_timeout`` -"""""""""""""""""""""""""""""""""""""""""""""""" - - -Kill the diagnostics or code action processes if they outlive this timeout - - -**Default**: ``5`` - - -.. _LanguageServerCompletionExtension: - - -LanguageServerCompletionExtension ---------------------------------- - - -.. _param_language_server_completion.trim_leading_dollar: - - -``language_server_completion.trim_leading_dollar`` -"""""""""""""""""""""""""""""""""""""""""""""""""" - - -If the leading dollar should be trimmed for variable completion suggestions - - -**Default**: ``false`` - - -.. _LanguageServerReferenceFinderExtension: - - -LanguageServerReferenceFinderExtension --------------------------------------- - - -.. _param_language_server_reference_reference_finder.reference_timeout: - - -``language_server_reference_reference_finder.reference_timeout`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Stop searching for references after this time (in seconds) has expired - - -**Default**: ``60`` - - -.. _param_language_server_reference_finder.soft_timeout: - - -``language_server_reference_finder.soft_timeout`` -""""""""""""""""""""""""""""""""""""""""""""""""" - - -Interupt and ask for confirmation to continue after this timeout (in seconds) - - -**Default**: ``10`` - - -.. _LanguageServerWorseReflectionExtension: - - -LanguageServerWorseReflectionExtension --------------------------------------- - - -.. _param_language_server_worse_reflection.workspace_index.update_interval: - - -``language_server_worse_reflection.workspace_index.update_interval`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Minimum interval to update the workspace index as documents are updated (in milliseconds) - - -**Default**: ``100`` - - -.. _param_language_server_worse_reflection.inlay_hints.enable: - - -``language_server_worse_reflection.inlay_hints.enable`` -""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable inlay hints (experimental) - - -**Default**: ``false`` - - -.. _param_language_server_worse_reflection.inlay_hints.types: - - -``language_server_worse_reflection.inlay_hints.types`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Show inlay type hints for variables - - -**Default**: ``false`` - - -.. _param_language_server_worse_reflection.inlay_hints.params: - - -``language_server_worse_reflection.inlay_hints.params`` -""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Show inlay hints for parameters - - -**Default**: ``true`` - - -.. _param_language_server_worse_reflection.diagnostics.enable: - - -``language_server_worse_reflection.diagnostics.enable`` -""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Enable diagnostics - - -**Default**: ``true`` - - -.. _LanguageServerIndexerExtension: - - -LanguageServerIndexerExtension ------------------------------- - - -.. _param_language_server_indexer.workspace_symbol_search_limit: - - -``language_server_indexer.workspace_symbol_search_limit`` -""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -**Default**: ``250`` - - -.. _param_language_server_indexer.reindex_timeout: - - -``language_server_indexer.reindex_timeout`` -""""""""""""""""""""""""""""""""""""""""""" - - -Unconditionally reindex modified files every N seconds - - -**Default**: ``300`` - - -.. _param_language_server_indexer.optimiser_timeout: - - -``language_server_indexer.optimiser_timeout`` -""""""""""""""""""""""""""""""""""""""""""""" - - -Type: integer - - -Optimise the index every N seconds - - -**Default**: ``3600`` - - -.. _LanguageServerCodeTransformExtension: - - -LanguageServerCodeTransformExtension ------------------------------------- - - -.. _param_language_server_code_transform.import_name.report_non_existing_names: - - -``language_server_code_transform.import_name.report_non_existing_names`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - - -Show an error if a diagnostic name cannot be resolved - can produce false positives - - -**Default**: ``true`` - - -.. _LanguageServerConfigurationExtension: - - -LanguageServerConfigurationExtension ------------------------------------- - - -.. _param_language_server_configuration.auto_config: - - -``language_server_configuration.auto_config`` -""""""""""""""""""""""""""""""""""""""""""""" - - -Type: boolean - - -Prompt to enable extensions which apply to your project on language server start - - -**Default**: ``true`` - - -.. _IndexerExtension: - - -IndexerExtension ----------------- - - -.. _param_indexer.enabled_watchers: - - -``indexer.enabled_watchers`` -"""""""""""""""""""""""""""" - - -Type: array - - -List of allowed watchers. The first watcher that supports the current system will be used - - -**Default**: ``["inotify","watchman","find","php"]`` - - -.. _param_indexer.index_path: - - -``indexer.index_path`` -"""""""""""""""""""""" - - -Type: string - - -Path where the index should be saved - - -**Default**: ``"%cache%\/index\/%project_id%"`` - - -.. _param_indexer.include_patterns: - - -``indexer.include_patterns`` -"""""""""""""""""""""""""""" - - -Type: array - - -Glob patterns to include while indexing - - -**Default**: ``["\/**\/*.php","\/**\/*.phar"]`` - - -.. _param_indexer.exclude_patterns: - - -``indexer.exclude_patterns`` -"""""""""""""""""""""""""""" - - -Type: array - - -Glob patterns to exclude while indexing - - -**Default**: ``["\/vendor\/**\/Tests\/**\/*","\/vendor\/**\/tests\/**\/*","\/vendor\/composer\/**\/*","\/vendor\/rector\/rector\/stubs-rector"]`` - - -.. _param_indexer.stub_paths: - - -``indexer.stub_paths`` -"""""""""""""""""""""" - - -Type: array - - -Paths to external folders to index. They will be indexed only once, if you want to take any changes into account you will have to reindex your project manually. - - -**Default**: ``[]`` - - -.. _param_indexer.poll_time: - - -``indexer.poll_time`` -""""""""""""""""""""" - - -Type: integer - - -For polling indexers only: the time, in milliseconds, between polls (e.g. filesystem scans) - - -**Default**: ``5000`` - - -.. _param_indexer.buffer_time: - - -``indexer.buffer_time`` -""""""""""""""""""""""" - - -Type: integer - - -For real-time indexers only: the time, in milliseconds, to buffer the results - - -**Default**: ``500`` - - -.. _param_indexer.follow_symlinks: - - -``indexer.follow_symlinks`` -""""""""""""""""""""""""""" - - -Type: boolean - - -To allow indexer to follow symlinks - - -**Default**: ``false`` - - -.. _param_indexer.max_filesize_to_index: - - -``indexer.max_filesize_to_index`` -""""""""""""""""""""""""""""""""" - - -Type: integer - - -Files larger than this will not be indexed. (Size in bytes) - - -**Default**: ``1000000`` - - -.. _param_indexer.project_root: - - -``indexer.project_root`` -"""""""""""""""""""""""" - - -Type: string - - -The root path to use for scanning the index - - -**Default**: ``"%project_root%"`` - - -.. _param_indexer.reference_finder.deep: - - -``indexer.reference_finder.deep`` -""""""""""""""""""""""""""""""""" - - -Type: boolean - - -Recurse over class implementations to resolve all references - - -**Default**: ``true`` - - -.. _param_indexer.implementation_finder.deep: - - -``indexer.implementation_finder.deep`` -"""""""""""""""""""""""""""""""""""""" - - -Type: boolean - - -Recurse over class implementations to resolve all class implementations (not just the classes directly implementing the subject) - - -**Default**: ``true`` - - -.. _param_indexer.supported_extensions: - - -``indexer.supported_extensions`` -"""""""""""""""""""""""""""""""" - - -Type: array - - -File extensions (e.g. `php`) for files that should be indexed - - -**Default**: ``["php","phar"]`` - - -.. _param_indexer.search_include_patterns: - - -``indexer.search_include_patterns`` -""""""""""""""""""""""""""""""""""" - - -Type: array - - -When searching the index exclude records whose fully qualified names match any of these regex patterns (use to exclude suggestions from search results). Namespace separators must be escaped as `\\\\` for example `^Foo\\\\` to include all namespaces whose first segment is `Foo` - - -**Default**: ``[]`` - - -.. _ObjectRendererExtension: - - -ObjectRendererExtension ------------------------ - - -.. _param_object_renderer.template_paths.markdown: - - -``object_renderer.template_paths.markdown`` -""""""""""""""""""""""""""""""""""""""""""" - - -Paths in which to look for templates for hover information. - - -**Default**: ``["%project_config%\/templates\/markdown","%config%\/templates\/markdown"]`` - - -.. _LanguageServerPhpstanExtension: - - -LanguageServerPhpstanExtension ------------------------------- - - -.. _param_language_server_phpstan.enabled: - - -``language_server_phpstan.enabled`` -""""""""""""""""""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _param_language_server_phpstan.bin: - - -``language_server_phpstan.bin`` -""""""""""""""""""""""""""""""" - - -Path to the PHPStan executable - - -**Default**: ``"%project_root%\/vendor\/bin\/phpstan"`` - - -.. _param_language_server_phpstan.severity: - - -``language_server_phpstan.severity`` -"""""""""""""""""""""""""""""""""""" - - -Severity at which PHPStan diagnostics should be reported. Ranges from 1 (error) to 4 (hint). - - -**Default**: ``1`` - - -.. _param_language_server_phpstan.level: - - -``language_server_phpstan.level`` -""""""""""""""""""""""""""""""""" - - -Override the PHPStan level - - -**Default**: ``null`` - - -.. _param_language_server_phpstan.config: - - -``language_server_phpstan.config`` -"""""""""""""""""""""""""""""""""" - - -Override the PHPStan configuration file - - -**Default**: ``null`` - - -.. _param_language_server_phpstan.mem_limit: - - -``language_server_phpstan.mem_limit`` -""""""""""""""""""""""""""""""""""""" - - -Override the PHPStan memory limit - - -**Default**: ``null`` - - -.. _param_language_server_phpstan.tmp_file_disabled: - - -``language_server_phpstan.tmp_file_disabled`` -""""""""""""""""""""""""""""""""""""""""""""" - - -Disable the use of temporary files when. This prevents as-you-type diagnostics, but ensures paths in phpstan config are respected. See https://github.com/phpactor/phpactor/issues/2763 - - -**Default**: ``false`` - - -.. _param_language_server_phpstan.editor_mode: - - -``language_server_phpstan.editor_mode`` -""""""""""""""""""""""""""""""""""""""" - - -DEPRECATED. Editor mode of Phpstan is used automatically when it's supported. - - -**Default**: ``false`` - - -.. _LanguageServerPsalmExtension: - - -LanguageServerPsalmExtension ----------------------------- - - -.. _param_language_server_psalm.enabled: - - -``language_server_psalm.enabled`` -""""""""""""""""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _param_language_server_psalm.bin: - - -``language_server_psalm.bin`` -""""""""""""""""""""""""""""" - - -Type: string - - -Path to psalm if different from vendor/bin/psalm - - -**Default**: ``"%project_root%\/vendor\/bin\/psalm"`` - - -.. _param_language_server_psalm.config: - - -``language_server_psalm.config`` -"""""""""""""""""""""""""""""""" - - -Type: string - - -Path to psalm config. Like %project_root%/psalm.xml - - -**Default**: ``""`` - - -.. _param_language_server_psalm.show_info: - - -``language_server_psalm.show_info`` -""""""""""""""""""""""""""""""""""" - - -Type: boolean - - -If infos from psalm should be displayed - - -**Default**: ``true`` - - -.. _param_language_server_psalm.use_cache: - - -``language_server_psalm.use_cache`` -""""""""""""""""""""""""""""""""""" - - -Type: boolean - - -If the Psalm cache should be used (see the `--no-cache` option) - - -**Default**: ``true`` - - -.. _param_language_server_psalm.error_level: - - -``language_server_psalm.error_level`` -""""""""""""""""""""""""""""""""""""" - - -Override level at which Psalm should report errors (lower => more errors) - - -**Default**: ``null`` - - -.. _param_language_server_psalm.threads: - - -``language_server_psalm.threads`` -""""""""""""""""""""""""""""""""" - - -Type: integer - - -Set the number of threads Psalm should use. Warning: NULL will use as many as possible and may crash your computer - - -**Default**: ``1`` - - -.. _param_language_server_psalm.timeout: - - -``language_server_psalm.timeout`` -""""""""""""""""""""""""""""""""" - - -Type: integer - - -Kill the psalm process after this number of seconds - - -**Default**: ``15`` - - -.. _LanguageServerMagoExtension: - - -LanguageServerMagoExtension ---------------------------- - - -.. _param_language_server_mago.enabled: - - -``language_server_mago.enabled`` -"""""""""""""""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _param_language_server_mago.bin: - - -``language_server_mago.bin`` -"""""""""""""""""""""""""""" - - -Path to the Mago executable - - -**Default**: ``"%project_root%\/vendor\/bin\/mago"`` - - -.. _param_language_server_mago.config: - - -``language_server_mago.config`` -""""""""""""""""""""""""""""""" - - -Override the Mago configuration file (mago.toml) - - -**Default**: ``null`` - - -.. _param_language_server_mago.timeout: - - -``language_server_mago.timeout`` -"""""""""""""""""""""""""""""""" - - -Maximum time in milliseconds to wait for a Mago run - - -**Default**: ``10000`` - - -.. _param_language_server_mago.analyze.enabled: - - -``language_server_mago.analyze.enabled`` -"""""""""""""""""""""""""""""""""""""""" - - -Show diagnostics from `mago analyze` (static analysis) - - -**Default**: ``true`` - - -.. _param_language_server_mago.lint.enabled: - - -``language_server_mago.lint.enabled`` -""""""""""""""""""""""""""""""""""""" - - -Show diagnostics from `mago lint` (style and code smells) - - -**Default**: ``true`` - - -.. _LanguageServerPhpCsFixerExtension: - - -LanguageServerPhpCsFixerExtension ---------------------------------- - - -.. _param_language_server_php_cs_fixer.enabled: - - -``language_server_php_cs_fixer.enabled`` -"""""""""""""""""""""""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _param_language_server_php_cs_fixer.bin: - - -``language_server_php_cs_fixer.bin`` -"""""""""""""""""""""""""""""""""""" - - -Path to the php-cs-fixer executable - - -**Default**: ``"%project_root%\/vendor\/bin\/php-cs-fixer"`` - - -.. _param_language_server_php_cs_fixer.version: - - -``language_server_php_cs_fixer.version`` -"""""""""""""""""""""""""""""""""""""""" - - -Arbitrary version (if not provided, phpactor tries to detect it - only to run it on unsupported PHP versions) - - -**Default**: ``null`` - - -.. _param_language_server_php_cs_fixer.env: - - -``language_server_php_cs_fixer.env`` -"""""""""""""""""""""""""""""""""""" - - -Environment for PHP CS Fixer - - -**Default**: ``{"XDEBUG_MODE":"off"}`` - - -.. _param_language_server_php_cs_fixer.show_diagnostics: - - -``language_server_php_cs_fixer.show_diagnostics`` -""""""""""""""""""""""""""""""""""""""""""""""""" - - -Whether PHP CS Fixer diagnostics are shown - - -**Default**: ``true`` - - -.. _param_language_server_php_cs_fixer.config: - - -``language_server_php_cs_fixer.config`` -""""""""""""""""""""""""""""""""""""""" - - -Set custom PHP CS config path. Ex., %project_root%/.php-cs-fixer.php - - -**Default**: ``null`` - - -.. _LanguageServerHighlightExtension: - - -LanguageServerHighlightExtension --------------------------------- - - -.. _param_language_server_highlight.enabled: - - -``language_server_highlight.enabled`` -""""""""""""""""""""""""""""""""""""" - - -Enable or disable the highlighter (can be expensive on large documents) - - -**Default**: ``true`` - - -.. _PhpCodeSnifferExtension: - - -PhpCodeSnifferExtension ------------------------ - - -.. _param_php_code_sniffer.enabled: - - -``php_code_sniffer.enabled`` -"""""""""""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _param_php_code_sniffer.bin: - - -``php_code_sniffer.bin`` -"""""""""""""""""""""""" - - -Path to the phpcs executable - - -**Default**: ``"%project_root%\/vendor\/bin\/phpcs"`` - - -.. _param_php_code_sniffer.env: - - -``php_code_sniffer.env`` -"""""""""""""""""""""""" - - -Environment for PHP_CodeSniffer (e.g. to set XDEBUG_MODE) - - -**Default**: ``{"XDEBUG_MODE":"off"}`` - - -.. _param_php_code_sniffer.show_diagnostics: - - -``php_code_sniffer.show_diagnostics`` -""""""""""""""""""""""""""""""""""""" - - -Whether PHP_CodeSniffer diagnostics are shown - - -**Default**: ``true`` - - -.. _param_php_code_sniffer.args: - - -``php_code_sniffer.args`` -""""""""""""""""""""""""" - - -Additional arguments to pass to the PHPCS process - - -**Default**: ``[]`` - - -.. _param_php_code_sniffer.cwd: - - -``php_code_sniffer.cwd`` -"""""""""""""""""""""""" - - -Working directory for PHPCS - - -**Default**: ``null`` - - -.. _LanguageServerBlackfireExtension: - - -LanguageServerBlackfireExtension --------------------------------- - - -.. _param_blackfire.enabled: - - -``blackfire.enabled`` -""""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _ProphecyExtension: - - -ProphecyExtension ------------------ - - -.. _param_prophecy.enabled: - - -``prophecy.enabled`` -"""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _OpenTelemetryExtension: - - -OpenTelemetryExtension ----------------------- - - -.. _param_open_telemetry.enabled: - - -``open_telemetry.enabled`` -"""""""""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _BehatExtension: - - -BehatExtension --------------- - - -.. _param_behat.enabled: - - -``behat.enabled`` -""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _param_behat.config_path: - - -``behat.config_path`` -""""""""""""""""""""" - - -Path to the main behat.yml (including the filename behat.yml) - - -**Default**: ``"%project_root%\/behat.yml"`` - - -.. _param_behat.symfony.di_xml_path: - - -``behat.symfony.di_xml_path`` -""""""""""""""""""""""""""""" - - -If using Symfony, set this path to the XML container dump to find contexts which are defined as services - - -**Default**: ``null`` - - -.. _SymfonyExtension: - - -SymfonyExtension ----------------- - - -.. _param_symfony.enabled: - - -``symfony.enabled`` -""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - - -.. _param_symfony.xml_path: - - -``symfony.xml_path`` -"""""""""""""""""""" - - -Path to the Symfony container XML dump file - - -**Default**: ``"%project_root%\/var\/cache\/dev\/App_KernelDevDebugContainer.xml"`` - - -.. _param_completion_worse.completor.symfony.enabled: - - -``completion_worse.completor.symfony.enabled`` -"""""""""""""""""""""""""""""""""""""""""""""" - - -Enable/disable the Symfony completor - depends on Symfony extension being enabled - - -**Default**: ``true`` - - -.. _param_public_services_only: - - -``public_services_only`` -"""""""""""""""""""""""" - - -Only consider public services when providing analysis for the service locator - - -**Default**: ``false`` - - -.. _PHPUnitExtension: - - -PHPUnitExtension ----------------- - - -.. _param_phpunit.enabled: - - -``phpunit.enabled`` -""""""""""""""""""" - - -Type: boolean - - -Enable or disable this extension - - -**Default**: ``false`` - diff --git a/doc/reference/diagnostic.rst b/doc/reference/diagnostic.rst deleted file mode 100644 index 9de2f4e5ef..0000000000 --- a/doc/reference/diagnostic.rst +++ /dev/null @@ -1,1011 +0,0 @@ -.. _diagnostics: - -Diagnostics -=========== - - -.. This document is generated via the `development:generate-documentation` command - - -.. contents:: - :depth: 2 - :backlinks: none - :local: - - -MissingMemberProvider ---------------------- - -Report if trying to call a class method which does not exist. - -.. tabs:: - - .. tab:: missing method on instance - - .. code-block:: php - - bar(); - - Diagnostic(s): - - - ``ERROR``: ``Method "bar" does not exist on class "Foobar"`` - - .. tab:: missing method for static invocation - - .. code-block:: php - - foo = 12; - $f->barfoo = 'string'; - -DocblockMissingReturnTypeProvider ---------------------------------- - -Report when a method has a return type should be augmented by a docblock tag - -.. tabs:: - - .. tab:: method without return type - - .. code-block:: php - - - */ - private array $foobar, - private array $barfoo - ) { - } - } - - Diagnostic(s): - - - ``WARN``: ``Method "__construct" is missing @param $barfoo`` - -AssignmentToMissingPropertyProvider ------------------------------------ - -Report when assigning to a missing property definition. - -.. tabs:: - - .. tab:: to non-existing property - - .. code-block:: php - - bar = 'foo'; - } - } - - Diagnostic(s): - - - ``WARN``: ``Property "bar" has not been defined`` - -MissingReturnTypeProvider -------------------------- - -Report if a method is missing a return type. - -.. tabs:: - - .. tab:: missing return type - - .. code-block:: php - - deprecated(); - $this->notDeprecated(); - } - - /** @deprecated This is deprecated */ - public function deprecated(): void {} - - public function notDeprecated(): void {} - } - - Diagnostic(s): - - - ``WARN``: ``Call to deprecated method "deprecated": This is deprecated`` - - .. tab:: deprecated on trait - - .. code-block:: php - - deprecated(); - $this->notDeprecated(); - } - - public function notDeprecated(): void {} - } - - Diagnostic(s): - - - ``WARN``: ``Call to deprecated method "deprecated": This is deprecated`` - - .. tab:: deprecated on property - - .. code-block:: php - - deprecated; - $ba = $this->notDeprecated; - } - } - - Diagnostic(s): - - - ``WARN``: ``Call to deprecated property "deprecated": This is deprecated`` - -UndefinedVariableProvider -------------------------- - -Report if a variable is undefined and suggest variables with similar names. - -.. tabs:: - - .. tab:: undefined variable - - .. code-block:: php - - $data) { - $list[$index] = $data; - } - - return $list; - -DocblockMissingExtendsTagProvider ---------------------------------- - -Report when a class extends a generic class but does not provide an @extends tag. - -.. tabs:: - - .. tab:: extends class requiring generic annotation - - .. code-block:: php - - ``` - - .. tab:: does not provide enough arguments - - .. code-block:: php - - - */ - class Foobar extends NeedGeneric - { - } - - Diagnostic(s): - - - ``WARN``: ``Generic tag `@extends NeedGeneric` should be compatible with `@extends NeedGeneric``` - - .. tab:: does not provide any arguments - - .. code-block:: php - - ``` - - .. tab:: provides empty arguments - - .. code-block:: php - - - */ - class Foobar extends NeedGeneric - { - } - - Diagnostic(s): - - - ``WARN``: ``Missing generic tag `@extends NeedGeneric``` - - .. tab:: wrong class - - .. code-block:: php - - - */ - class Foobar extends NeedGeneric - { - } - - Diagnostic(s): - - - ``WARN``: ``Missing generic tag `@extends NeedGeneric``` - - .. tab:: does not provide multiple arguments - - .. code-block:: php - - - */ - class Foobar extends NeedGeneric - { - } - - Diagnostic(s): - - - ``WARN``: ``Generic tag `@extends NeedGeneric` should be compatible with `@extends NeedGeneric``` - -DocblockMissingImplementsTagProvider ------------------------------------- - -Report when a class extends a generic class but does not provide an @extends tag. - -.. tabs:: - - .. tab:: implements class requiring generic annotation - - .. code-block:: php - - ``` - - .. tab:: does not provide enough arguments - - .. code-block:: php - - - */ - class Foobar implements NeedGeneric - { - } - - Diagnostic(s): - - - ``WARN``: ``Generic tag `@implements NeedGeneric` should be compatible with `@implements NeedGeneric``` - - .. tab:: provides one but not another - - .. code-block:: php - - - */ - class Foobar implements NeedGeneric1, NeedGeneric2 - { - } - - Diagnostic(s): - - - ``WARN``: ``Missing generic tag `@implements NeedGeneric2``` - \ No newline at end of file diff --git a/doc/reference/images/class-referenes.png b/doc/reference/images/class-referenes.png deleted file mode 100644 index c67437fe99..0000000000 Binary files a/doc/reference/images/class-referenes.png and /dev/null differ diff --git a/doc/reference/images/risky.png b/doc/reference/images/risky.png deleted file mode 100644 index 367a8055f2..0000000000 Binary files a/doc/reference/images/risky.png and /dev/null differ diff --git a/doc/reference/indexer.rst b/doc/reference/indexer.rst deleted file mode 100644 index 657e6c400e..0000000000 --- a/doc/reference/indexer.rst +++ /dev/null @@ -1,193 +0,0 @@ -.. _indexer: - -Indexer -======= - -The indexer scans your project directory and records meta-information about -classes and functions in your project. - -The indexer required only for some features (such as -:ref:`navigation_goto_implementation`). - -.. _indexer_building: - -Building the index ------------------- - -It will be *automatically enabled* when used with the language server but can -also be used with RPC if run manually. - -.. tabs:: - - .. tab:: CLI - - Build index and watch for changes - - .. code:: sh - - $ phpactor index:build --watch - - Build from scratch - - .. code:: sh - - $ phpactor index:build --reset - - .. tab:: Language Server CoC - - The index is built automatically on LS initialize and subsequently - updated as necessary. - - You can however force a reindex: - - Build from scratch: - - .. code:: sh - - :CocCommand phpactor.reindex - - .. tab:: Language Server General - - Make a request to `indexer/reindex`. - - -.. _watcher: - -Watching --------- - -File watchers are used to keep the index up-to-date. - -Several watching systems can be used, by default Phpactor will choose the -first supported one: - -lsp -~~~ - -**Any platform** - -This watcher depends on file events from the LSP client (e.g. VSCode). - -inotifywait -~~~~~~~~~~~ - -**Linux** only, react immediately to file changes. - -Installation - -.. tabs:: - - .. tab:: Debian/Ubuntu - - .. code-block:: bash - - apt install inotify-tools - -watchman -~~~~~~~~ - -**Linux/Mac** cross platform, reacts immediately to file changes, see Watchman_ documentation. - -Watchman is the recommended watcher. - -Installation: - -.. tabs:: - - .. tab:: Debian/Ubuntu - - .. code-block:: bash - - apt install watchman - - .. tab:: MacOS - - .. code-block:: bash - - brew install watchman - -find -~~~~ - -**Linux/Mac/POSIX** Poll the system for changes every 5 seconds. - -This tool should be installed by default. - -php -~~~ - -**Any system**: Poll system using PHP (slow) every 5 seconds. - -.. _indexer_querying: - -Querying from the CLI ---------------------- - -You can query the index from the CLI: - -.. tabs:: - - .. tab:: Show class index information - - .. code:: sh - - $ phpactor index:query "Symfony\\Component\\Console\\Output\\OutputInterface" - - .. tab:: Show function information - - .. code:: sh - - $ phpactor index:query "sprintf" - - .. tab:: Show member information - - .. code:: sh - - $ phpactor index:query "method#createFoobar" - $ phpactor index:query "property#createFoobar" - $ phpactor index:query "constant#createFoobar" - -Note that this information is primarily intended for the indexer and is not -yet intended to provide a true "querying" facility. - -Configuration -------------- - -List the possible configuration options with ``phpactor config:dump | grep -indexer``, explanations of some important ones: - -- :ref:`param_indexer.enabled_watchers`: List of watchers to enable (e.g. `inotify`, - `find`). -- :ref:`param_indexer.include_patterns`: List of glob patterns to include -- :ref:`param_indexer.exclude_patterns`: List of glob patterns to exclude -- :ref:`param_indexer.stub_paths`: List of external paths to index -- :ref:`param_indexer.poll_time`: Poll time used for polling watchers (e.g. ``find``, ``php``) -- :ref:`param_indexer.buffer_time`: Time to wait to collect batch messages from - "realtime" watchers (e.g. ``inotify``) - -Troubleshooting ---------------- - -Inotify: Why isn't ``inotifywait`` used when I'm on Linux? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It may not be installed, on Debian/Ubuntu - -.. code:: sh - - $ sudo apt install inotify-tools - -Inotify: ``inotify`` limit reached -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The default number of watchers is quite low by default, try increasing the -number of watchers: - -.. code:: sh - - $ sudo sysctl fs.inotify.max_user_watches=100000 - -Note this still may not be sufficient, so increase as necessary, make the -change permanent by writing to ``/etc/sysctl.conf`` - -.. _Watchman: https://facebook.github.io/watchman/ diff --git a/doc/reference/navigation.rst b/doc/reference/navigation.rst deleted file mode 100644 index 6ebf0345ca..0000000000 --- a/doc/reference/navigation.rst +++ /dev/null @@ -1,305 +0,0 @@ -.. _navigation: - -Navigation -========== - -Phpactor provides some functionality for navigating to (and generating) -contextually relevant files such as parent classes, definitions, unit -tests, references etc. - -.. contents:: - :depth: 1 - :backlinks: none - :local: - -.. _navigation_class_references: - -Class References ----------------- - -Navigate / list all references to a given class. - -.. tabs:: - - .. tab:: CLI - - .. code-block:: sh - - $ phpactor references:class path/to/Class.php - - .. tab:: VIM Context Menu - - *Class context menu > Find references*. - - .. tab:: VIM Plugin - - .. code:: sh - - :PhpactorFindReferences - - .. tab:: LSP - - Supported via. the `textDocument/references` action. - -Description -~~~~~~~~~~~ - -Keep track of where a class is being used or perform an initial survey -before deciding to rename a class. - -The VIM plugin will load the class references into a quick fix list -which you can navigate through (see ``:help quickfix``). - -The CLI command will list the references and show a highlighted line -where the references were found. - -.. figure:: images/class-referenes.png - :alt: Class references - - Class references - -.. _navigation_class_member_references: - -Class Member References ------------------------ - -Navigate / list all references to a given class member (method, property -or constant). - -.. tabs:: - - .. tab:: CLI - - .. code-block:: sh - - $ phpactor references:member path/to/Class.php memberName - - .. tab:: VIM Context Menu - - *Member context menu > Find references*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorFindReferences - - .. tab:: Language Server - - Supported via. the `textDocument/references` action. - -.. _description-1: - -Description -~~~~~~~~~~~ - -Scan for all references to a class member in the project. - -This functionality is very similar to `Class -References <#class-references>`__ with the exception that it is possible -that not all members will be found as PHP is a loosely typed language -and it may not be possible to determine all the class types of methods -matching the query. - -Hover ------ - -While not a navigation function as such, this RPC command will show -brief information about the symbol underneath the cursor. - -.. tabs:: - - .. tab:: VIM Context Menu - - *Context menu* > Hover_. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorHover - - .. tab:: LSP - - Supported via. the `textDocument/hover` action. - - -Jump to definition ------------------- - -Jump to the definition of a class or class member. - -.. tabs:: - - .. tab:: VIM Context Menu - - *Member/class context menu > Goto definition*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorGotoDefinition - - .. tab:: LSP - - Supported via. the `textDocument/definition` action. - - -.. _description-2: - -Description -~~~~~~~~~~~ - -Open the file containing the class or class member under the cursor and -move the cursor to the place where class or class member is defined. - -This feature is **extremely useful**! Be sure to map it to a keyboard -shortcut and use it often to quickly navigate through your source code. - -Jump to type ------------- - -Jump to the type of the symbol under the cursor. - -.. tabs:: - - .. tab:: VIM Context Menu - - \_Member/class context menu > Goto type. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorGotoType() - - .. tab:: LSP - - Supported via. the `textDocument/typeDefinition` action. - -.. _description-3: - -Description -~~~~~~~~~~~ - -Sometimes you will want to jump to the type (i.e. the class) of a -symbol, for example if you reference a property in code, -``$this->locator``, you can invoke *goto type* on the property and jump -to the, for example, ``Locator`` type. - -.. _navigation_goto_implementation: - -Jump to Implementation ----------------------- - -Jump to the implementatoin(s) of an interface or class - -.. tabs:: - - .. tab:: VIM Context Menu - - *Member/class context menu > Goto implementation*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorGotoImplementations - - .. tab:: LSP - - Supported via. the `textDocument/implementation` action. - - -Jump to implementations of the interface or class under the cursor. - -Note that this feature only works when used with the :ref:`indexer`. - -Jump to or generate related file --------------------------------- - -Jump to a related file (e.g. parent class, interfaces, unit test, -integration test, benchmark), and optionally generate it if it doesn't -exist (where supported). - -Jumping -~~~~~~~ - -.. tabs:: - - .. tab:: VIM Context Menu - - *Class context menu > Navigate*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorNavigate - -You specify the jump patterns in ``.phpactor.json`` with :ref:`param_navigator.destinations`: - -:: - - { - "navigator.destinations": - { - "source": "lib/.php", - "unit_test": "tests/Unit/Test.php" - } - } - -This would enable you to jump from - ``lib/Acme/Post.php`` to ``tests/Unit/Acme/Post.php`` and vice-versa. - -Generating -~~~~~~~~~~ - -If the file doesn't exist you automatically create it by mapping the -navigation targets to template :ref:`variants `: - -:: - - { - "code_transform.class_new.variants": - { - "source": "default", - "unit_test": "phpunit_test", - "exception": "exception", - "symfony_command": "symfony_command" - } - } - -Now Phpactor should prompt you to create the navigation target if it doesn't exist. - -.. _description-4: - -Description -~~~~~~~~~~~ - -Often classes will have a one-to-one relationship with another class, -for example a single class will often have a matching unit test. - -Phpactor provides a way to define this relationship: - -.. code:: yaml - - # .phpactor.yml - navigator.destinations: - source: lib/.php - unit_test: tests/Unit/Test.php - - navigator.autocreate: - source: default - unit_test: phpunit_test - -Above we define a pattern which will match the source code of the -project (and assign it an identifier ``source``). We also identify a -pattern to identify ``unit_test`` classes. - -When you are in a ``source`` file, the navigate option will offer you -the possibility of jumping to the unit test, and vice-versa. - -Above we additionally (and optionally) tell Phpactor that it can -auto generate these classes based on `templates `__. diff --git a/doc/reference/refactorings.rst b/doc/reference/refactorings.rst deleted file mode 100644 index fabd824411..0000000000 --- a/doc/reference/refactorings.rst +++ /dev/null @@ -1,1852 +0,0 @@ -.. _refactoring: - -Refactoring -*********** - -.. contents:: - :depth: 2 - :backlinks: none - :local: - -Fixes -===== - -.. _refactoring_add_missing_assignements: - -Add Missing Assignments ------------------------ - -Automatically add any missing properties to a class. - -.. tabs:: - - .. tab:: CLI - - .. code-block:: - - $ phpactor class:transform path/to/Class.php --transform=add_missing_assignments - - .. tab:: VIM Context Menu - - *Class context menu > Transform > Add missing properties*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorTransform - - -Motivation -~~~~~~~~~~ - -When authoring a class it is redundant effort to add a property and -documentation tag when making an assignment. This refactoring will scan -for any assignments which have do not have corresponding properties and -add the required properties with docblocks based on inferred types where -possible. - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - blog = new Blog(); - } - } - -Becomes: - -.. code:: php - - blog = new Blog(); - } - } - -.. _refactoring_add_missing_docblock: - -Add Missing Docblock --------------------- - -This refactoring will add docblocks: - -- If there is an array return type and an iterator value can be inferred from - the function's return statement. -- If there is an class return type and a generic type can be inferred from the - function's return statement. - -.. tabs:: - - .. tab:: CLI - - .. code-block:: - - $ phpactor class:transform path/to/Class.php --transform=add_missing_docblocks - - .. tab:: VIM Context Menu - - *Class context menu > Transform > Add missing docblocks*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorTransform - - .. tab:: Language Server - - Request code actions when there is a candidate - -.. _refactoring_complete_constructor: - -Add Missing Return Types ------------------------- - -This refactoring add missing return types. - -.. tabs:: - - .. tab:: CLI - - .. code-block:: - - $ phpactor class:transform path/to/Class.php --transform=add_missing_return_types - - .. tab:: VIM Context Menu - - *Class context menu > Transform > Add missing return types*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorTransform - - .. tab:: Language Server - - Request code actions when there is a candidate - -.. _refactoring_add_override_attribute: - -Add Override Attribute ----------------------- - -Add the ``#[\Override]`` attribute to methods which override a method from a -parent class or implemented interface. Only applies when the project's PHP -version is 8.3 or higher. - -.. tabs:: - - .. tab:: CLI - - .. code-block:: - - $ phpactor class:transform path/to/Class.php --transform=add_override_attribute - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorTransform - - .. tab:: Language Server - - Request code actions when there is a candidate - -Complete Constructor --------------------- - -Complete the assignments and add properties for an incomplete -constructor. - -.. tabs:: - - .. tab:: CLI - - .. code-block:: - - $ phpactor class:transform path/to/class.php --transform=complete_constructor - - .. tab:: VIM Context Menu - - *Class context menu > Transform > Complete Constructor*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorTransform - - -.. _motivation-5: - -Motivation -~~~~~~~~~~ - -When authoring a new class, it is often required to: - -1. Create a constructor method with typed arguments. -2. Assign the arguments to class properties. -3. Create the class properties with docblocks. - -This refactoring will automatically take care of 2 and 3. - -.. _before-and-after-5: - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - hello = $hello; - $this->goodbye = $goodbye; - } - } - -.. _refactoring_fix_namespace_and_class: - -Fix Namespace or Class Name ---------------------------- - -Update a file’s namespace (and/or class name) based on the composer -configuration. - -.. tabs:: - - .. tab:: CLI - - .. code-block:: - - $ phpactor class:transform path/to/class.php --transform=fix_namespace_class_name - - .. tab:: VIM Context Menu - - *Class context menu > Transform > Fix namespace or class name*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorTransform - -.. warning:: - - This refactoring will currently only work fully on Composer based - projects. - -.. _motivation-6: - -Motivation -~~~~~~~~~~ - -Phpactor already has the possibility of generating new classes, and -moving classes. But sometimes your project may get into a state where -class-containing files have an incorrect namespace or class name. - -This refactoring will: - -- Update the namespace based on the file path (and the autoloading - config). -- Update the class name. -- When given an empty file, it will generate a PHP tag and the - namespace. - -.. _before-and-after-6: - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - // lib/Barfoo/Hello.php - Generate accessor*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorGenerateAccessor - - -.. _motivation-11: - -Motivation -~~~~~~~~~~ - -When creating entities and value objects it is frequently necessary to -add accessors. - -This refactoring automates the generation of accessors. - -.. _before-and-after-11: - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - bar - { - /** - * @var Barfoo - */ - private $barfoo; - } - -After selecting `one or more -accessors `__ - -.. code:: php - - barfoo; - } - } - -Note the accessor template can be customized see -`Templates `__. - -.. _generation_method: - -Generate Method ---------------- - -Generate or update a method based on the method call under the cursor. - -.. tabs:: - - .. tab:: CLI - - *RPC only* - - .. tab:: VIM Context Menu - - *Method context menu > Generate method*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorContextMenu - - -.. _motivation-12: - -Motivation -~~~~~~~~~~ - -When initially authoring a package you will often write a method call -which doesn't exist and then add the method to the corresponding class. - -This refactoring will automatically generate the method inferring any -type information that it can. - -.. _before-and-after-12: - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - barfoo->good<>bye($hello); - } - } - - class Barfoo - { - } - -After generating the method: - -.. code:: php - - barfoo->goodbye($hello); - } - } - - class Barfoo - { - public function goodbye(Hello $hello) - { - } - } - -.. _generateo_constructor: - -Generate Constructor --------------------- - -Generate a constructor from a new object instance expression - -.. tabs:: - - .. tab:: LSP - - Invoke code action on new class expression for class with no constructor - - - -Before and After -~~~~~~~~~~~~~~~~ - -Assuming `MyFancyObject` exists and has no constructor. - -Cursor position shown as ``<>``: - -.. code:: php - - FancyObject($barfoo, 'foobar', 1234); - -After choosing the "Generate Constructor" code action the `MyFancyObject` -class should have a constructor: - -.. code:: php - - Transform > Implement contracts*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorTransform - -.. _motivation-13: - -Motivation -~~~~~~~~~~ - -It can be very tiresome to manually implement contracts for interfaces -and abstract classes, especially interfaces with many methods -(e.g. ``ArrayAccess``). - -This refactoring will automatically add the required methods to your -class. If the interface uses any foreign classes, the necessary ``use`` -statements will also be added. - -.. _before-and-after-13: - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - `_. - -.. tabs:: - - .. tab:: LSP - - Invoke code action on a class which has implemented no methods and - implements one or more interfaces. - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - innerCounter = $innerCounter; - } - - public function count(): int - { - return $this->innerCounter->count(); - } - } - -.. _refactoring_import_missing_class: - -Import Class ------------- - -Import a class into the current namespace based on the class name under -the cursor. - -.. tabs:: - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorImportClass - -.. _motivation-14: - -Motivation -~~~~~~~~~~ - -It is easy to remember the name of a class, but more difficult to -remember its namespace, and certainly it is time consuming to manually -code class imports: - -Manually one would: - -1. Perform a fuzzy search for the class by its short name. -2. Identify the class you want to import. -3. Copy the namespace. -4. Paste it into your current file -5. Add the class name to the new ``use`` statement. - -This refactoring covers steps 1, 3, 4 and 5. - -.. _before-and-after-14: - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - quest $request) - { - } - - } - -After selecting ``Symfony\Component\HttpFoundation\Request`` from the -list of candidates: - -.. code:: php - - ``: - -.. code:: php - - ToNewUser::class - ]; - } - -After selecting ``App\Listeners\AssignDefaultRoleToNewUser`` from the -list of candidates: - -.. code:: php - - Import Missing* - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorImportMissingClasses - - -.. _motivation-16: - -Motivation -~~~~~~~~~~ - -You may copy and paste some code from one file to another and -subsequently need to import all the foreign classes into the current -namespace. This refactoring will identify all unresolvable classes and -import them. - -Fill Object ------------ - -Fill a new objects constructor with default arguments. - -.. tabs:: - - .. tab:: LSP - - Invoke code action on new class expression with no constructor arguments - - -Motivation -~~~~~~~~~~ - -This refactoring is especially useful if you need to either create or map a -DTO (data transfer object). - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - FancyDTO(); - -After choosing the "Fill Object" code action: - -.. code:: php - - Override method*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorContextMenu - -**Multiple selection**: Supports selecting multiple methods. - -.. _motivation-17: - -Motivation -~~~~~~~~~~ - -Sometimes it is expected or necessary that you override a parent class's -method (for example when authoring a Symfony Command class). - -This refactoring will allow you to select a method to override and -generate that method in your class. - -.. _before-and-after-16: - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - New Class* - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorClassNew - -.. _motivation-4: - -Motivation -~~~~~~~~~~ - -Creating classes is one of the most general actions we perform: - -1. Create a new file. -2. Code the namespace, ensuring that it is compatible with the - autoloading scheme. -3. Code the class name, ensuring that it is the same as the file name. - -This refactoring will perform steps 1, 2 and 3 for: - -- Any given file name. -- Any given class name. -- A class name under the cursor. - -It is also possible to choose a class template, see -`templates `__ for more information. - -.. _before-and-after-4: - -Before and After -~~~~~~~~~~~~~~~~ - -.. container:: alert alert-success - - This example is from an existing, empty, file. Note that you can also - use the context menu to generate classes from non-existing class - names in the current file - -Given a new file: - -.. code:: php - - # src/Blog/Post.php - -After invoking *class new* using the ``default`` variant: - -.. code:: php - - Copy Class* - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorCopyFile - -.. _motivation-1: - -Motivation -~~~~~~~~~~ - -Sometimes you find that an existing class is a good starting point for a -new class. In this situation you may: - -1. Copy the class to a new file location. -2. Update the class name and namespace. -3. Adjust the copied class as necessary. - -This refactoring performs steps 1 and 2. - -.. _before-and-after-1: - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - # src/Blog/Post.php - Inflect > Extract interface*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorClassInflect - - -.. _motivation-10: - -Motivation -~~~~~~~~~~ - -It is sometimes unwise to preemptively create interfaces for all your -classes, as doing so adds maintenance overhead, and the interfaces may -never be needed. - -This refactoring allows you to generate an interface from an existing -class. All public methods will be added to generated interface. - -.. _before-and-after-10: - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - Change Visibility* - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorChangeVisibility - - -Currently this will cycle through the 3 visibilities: ``public``, -``protected`` and ``private``. - -.. _motivation-2: - -Motivation -~~~~~~~~~~ - -Sometimes you may want to extract a class from an existing class in -order to isolate some of it’s responsibility. When doing this you may: - -1. Create a new class using `Class New <#class-new>`__. -2. Copy the method(s) which you want to extract to the new class. -3. Change the visibility of the main method from ``private`` to - ``public``. - -.. _before-and-after-2: - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - # src/Blog/FoobarResolver.php - - } - } - -After invoking “change visibility” on or within the method. - -.. code:: php - - # src/Blog/FoobarResolver.php - Move Class* - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorMoveFile - -.. _motivation-3: - -Motivation -~~~~~~~~~~ - -When authoring classes, it is often difficult to determine really -appropriate names and namespaces, this is unfortunate as a class name -can quickly propagate through your code, making the class name harder to -change as time goes on. - -This problem is multiplied if you have chosen an incorrect namespace. - -This refactoring will move either a class, class-containing-file or -folder to a new location, updating the classes namespace and all -references to that class where possible in a given *scope* (i.e. files -known by GIT: ``git``, files known by Composer: ``composer``, or all PHP -files under the current CWD: ``simple``). - -If you have defined file relationships with -`navigator.destinations `__, -then you have the option to move the related files in addition to the -specified file. If using the command then specify ``--related``, or if -using the RPC interface (f.e. VIM) you will be prompted. - -.. container:: alert alert-danger - - This is a dangerous refactoring! Ensure that you commit your work - before executing it and be aware that success is not guaranteed - (e.g. class references in non-PHP files or docblocks are not - currently updated). - - This refactoring works best when you have a well tested code base. - -.. _before-and-after-3: - -Before and After -~~~~~~~~~~~~~~~~ - -.. code:: php - - # src/Blog/Post.php - Extract Constant*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorContextMenu - -.. _motivation-7: - -Motivation -~~~~~~~~~~ - -Each time a value is duplicated in a class a fairy dies. Duplicated -values increase the fragility of your code. Replacing them with a -constant helps to ensure runtime integrity. - -This refactoring includes `Replace Magic Number with Symbolic -Constant `__ -(Fowler, Refactoring). - -.. _before-and-after-7: - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - es') { - return true; - } - - return false; - } - - public function yes() - { - return 'yes'; - } - } - -After: - -.. code:: php - - ``: - -.. code:: php - - 1 + 2 + 3 + 5 === 6) { - echo 'You win!'; - } - -After (entering ``$hasWon`` as a variable name): - -.. code:: php - - 1 + 2 + 3 + 5<> === 6) { - echo 'You win!'; - } - -After (using ``$winningCombination`` as a variable name): - -.. code:: php - - `__ or invoke it manually. - -.. _motivation-9: - -Motivation -~~~~~~~~~~ - -This is one of the most common refactorings. Decomposing code into -discrete methods helps to make code understandable and maintainable. - -Extracting a method manually involves: - -1. Creating a new method -2. Moving the relevant block of code to that method. -3. Scanning the code for variables which are from the original code. -4. Adding these variables as parameters to your new method. -5. Calling the new method in place of the moved code. - -This refactoring takes care of steps 1 through 5 and: - -- If a *single* variable that is declared in the selection which is - used in the parent scope, it will be returned. -- If *multiple* variables are used, the extracted method will return a - tuple. -- In both cases the variable(s) will be assigned at the point of - extraction. -- Any class parameters which are not already imported, will be - imported. - -.. _before-and-after-9: - -Before and After -~~~~~~~~~~~~~~~~ - -Selection shown between the two ``<>`` markers: - -.. code:: php - - - if ($foobar) { - return 'yes'; - } - - return $foobar; - <> - - } - } - -After extracting method ``newMethod``: - -.. code:: php - - newMethod($foobar); - - } - - private function newMethod(string $foobar) - { - if ($foobar) { - return 'yes'; - } - - return $foobar; - } - } - -.. _refactoring_rename_variable: - -Rename Variable ---------------- - -Rename a variable in the local or class scope. - -.. tabs:: - - .. tab:: VIM Context Menu - - *Variable context menu > Rename*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorContextMenu - -.. _motivation-18: - -Motivation -~~~~~~~~~~ - -Having meaningful and descriptive variable names makes the intention of -code clearer and therefore easier to maintain. Renaming variables is a -frequent refactoring, but doing this with a simple search and replace -can often have unintended consequences (e.g. renaming the variable -``$class`` also changes the ``class`` keyword). - -This refactoring will rename a variable, and only variables, in either -the method scope or the class scope. - -.. _before-and-after-17: - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - os) - { - foreach ($hellos as $greeting) { - echo $greeting; - } - - return $hellos; - } - - } - -Rename the variable ``$hellos`` to ``$foobars`` in the local scope: - -.. code:: php - - Replace references*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorContextMenu - -.. _motivation-19: - -Motivation -~~~~~~~~~~ - -This refactoring is *similar* to `Move Class <#class-move>`__, but -without renaming the file. This is a useful refactoring when a dependant -library has changed a class name and you need to update that class name -in your project. - -.. _before-and-after-18: - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - lo - { - public function say() - { - - } - - } - - $hello = new Hello(); - $hello->say(); - -Rename ``Hello`` to ``Goodbye`` - -.. code:: php - - say(); - -.. container:: alert alert-danger - - When renaming classes in your project use Class Move. - -.. _refactoring_rename_member: - -Rename Class Member -------------------- - -Rename a class member. - -.. tabs:: - - .. tab:: CLI - - .. code-block:: - - $ phpactor references:member path/to/Class.php memberName --type="method" --replace="newMemberName" - - Class FQNs are also accepted - - .. tab:: VIM Context Menu - - *Member context menu > Replace references*. - - .. tab:: VIM Plugin - - .. code-block:: - - :PhpactorContextMenu - -.. _motivation-20: - -Motivation -~~~~~~~~~~ - -Having an API which is expressive of the intent of the class is -important, and contributes to making your code more consistent and -maintainable. - -When renaming members global search and replace can be used, but is a -shotgun approach and you may end up replacing many things you did not -mean to replace (e.g. imagine renaming the method ``name()``). - -This refactoring will: - -1. Scan for files in your project which contain the member name text. -2. Parse all of the candidate files. -3. Identify the members, and try and identify the containing class. -4. Replace only the members which certainly belong to the target class. - -When replacing *private* and *protected* members, only the related -classes will be updated. - -Due to the loosely typed nature of PHP this refactoring may not find all -of the member accesses for the given class. Run your tests before and -after applying this refactoring. - -.. container:: alert alert-info - - Hint: Use the CLI command to list all of the risky references. Risky - references are those member accesses which match the query but whose - containing classes could not be resolved. - -.. figure:: images/risky.png - :alt: Risky references - - Risky references - -.. _before-and-after-19: - -Before and After -~~~~~~~~~~~~~~~~ - -Cursor position shown as ``<>``: - -.. code:: php - - y() - { - - } - - } - - $hello = new Hello(); - $hello->say(); - -Rename ``Hello#say()`` to ``Hello#speak()`` - -.. code:: php - - speak(); - diff --git a/doc/reference/rpc_command.rst b/doc/reference/rpc_command.rst deleted file mode 100644 index 05bb682176..0000000000 --- a/doc/reference/rpc_command.rst +++ /dev/null @@ -1,1366 +0,0 @@ -Legacy RPC Commands -=================== - - -.. This document is generated via the `development:generate-documentation` command - - -.. contents:: - :depth: 2 - :backlinks: none - :local: - - -.. _RpcHandler_status: - - -_RpcHandler_status ------------------- - - -.. _RpcCommand_status_type: - - -``type`` -"""""""" - - -**Default**: ``"formatted"`` - - -.. _RpcHandler_trust: - - -_RpcHandler_trust ------------------ - - -.. _RpcCommand_trust_trust: - - -``trust`` -""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_file_info: - - -_RpcHandler_file_info ---------------------- - - -.. _RpcCommand_file_info_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_references: - - -_RpcHandler_references ----------------------- - - -.. _RpcCommand_references_mode: - - -``mode`` -"""""""" - - -**Default**: ``"find"`` - - -.. _RpcCommand_references_filesystem: - - -``filesystem`` -"""""""""""""" - - -**Default**: ``"git"`` - - -.. _RpcCommand_references_replacement: - - -``replacement`` -""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_references_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_references_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_references_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_copy_class: - - -_RpcHandler_copy_class ----------------------- - - -.. _RpcCommand_copy_class_dest_path: - - -``dest_path`` -""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_copy_class_source_path: - - -``source_path`` -""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_move_class: - - -_RpcHandler_move_class ----------------------- - - -.. _RpcCommand_move_class_dest_path: - - -``dest_path`` -""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_move_class_confirmed: - - -``confirmed`` -""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_move_class_move_related: - - -``move_related`` -"""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_move_class_source_path: - - -``source_path`` -""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_class_inflect: - - -_RpcHandler_class_inflect -------------------------- - - -.. _RpcCommand_class_inflect_new_path: - - -``new_path`` -"""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_class_inflect_variant: - - -``variant`` -""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_class_inflect_overwrite_existing: - - -``overwrite_existing`` -"""""""""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_class_inflect_current_path: - - -``current_path`` -"""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_class_new: - - -_RpcHandler_class_new ---------------------- - - -.. _RpcCommand_class_new_new_path: - - -``new_path`` -"""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_class_new_variant: - - -``variant`` -""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_class_new_overwrite_existing: - - -``overwrite_existing`` -"""""""""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_class_new_current_path: - - -``current_path`` -"""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_transform: - - -_RpcHandler_transform ---------------------- - - -.. _RpcCommand_transform_transform: - - -``transform`` -""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_transform_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_transform_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_extract_constant: - - -_RpcHandler_extract_constant ----------------------------- - - -.. _RpcCommand_extract_constant_constant_name: - - -``constant_name`` -""""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_constant_constant_name_suggestion: - - -``constant_name_suggestion`` -"""""""""""""""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_constant_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_constant_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_constant_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_extract_method: - - -_RpcHandler_extract_method --------------------------- - - -.. _RpcCommand_extract_method_method_name: - - -``method_name`` -""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_method_offset_start: - - -``offset_start`` -"""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_method_offset_end: - - -``offset_end`` -"""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_method_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_method_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_generate_accessor: - - -_RpcHandler_generate_accessor ------------------------------ - - -.. _RpcCommand_generate_accessor_names: - - -``names`` -""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_generate_accessor_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_generate_accessor_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_generate_accessor_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_generate_mutator: - - -_RpcHandler_generate_mutator ----------------------------- - - -.. _RpcCommand_generate_mutator_names: - - -``names`` -""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_generate_mutator_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_generate_mutator_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_generate_mutator_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_generate_method: - - -_RpcHandler_generate_method ---------------------------- - - -.. _RpcCommand_generate_method_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_generate_method_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_generate_method_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_import_class: - - -_RpcHandler_import_class ------------------------- - - -.. _RpcCommand_import_class_qualified_name: - - -``qualified_name`` -"""""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_import_class_alias: - - -``alias`` -""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_import_class_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_import_class_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_import_class_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_rename_variable: - - -_RpcHandler_rename_variable ---------------------------- - - -.. _RpcCommand_rename_variable_name: - - -``name`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_rename_variable_name_suggestion: - - -``name_suggestion`` -""""""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_rename_variable_scope: - - -``scope`` -""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_rename_variable_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_rename_variable_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_rename_variable_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_change_visibility: - - -_RpcHandler_change_visibility ------------------------------ - - -.. _RpcCommand_change_visibility_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_change_visibility_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_change_visibility_offset: - - -``offset`` -"""""""""" - - -Type: integer - - -**Default**: ``null`` - - -.. _RpcHandler_override_method: - - -_RpcHandler_override_method ---------------------------- - - -.. _RpcCommand_override_method_method_name: - - -``method_name`` -""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_override_method_class_name: - - -``class_name`` -"""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_override_method_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_override_method_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_extract_expression: - - -_RpcHandler_extract_expression ------------------------------- - - -.. _RpcCommand_extract_expression_variable_name: - - -``variable_name`` -""""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_expression_offset_start: - - -``offset_start`` -"""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_expression_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_expression_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_extract_expression_offset_end: - - -``offset_end`` -"""""""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_import_missing_classes: - - -_RpcHandler_import_missing_classes ----------------------------------- - - -.. _RpcCommand_import_missing_classes_path: - - -``path`` -"""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_import_missing_classes_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_hover: - - -_RpcHandler_hover ------------------ - - -.. _RpcCommand_hover_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_hover_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_complete: - - -_RpcHandler_complete --------------------- - - -.. _RpcCommand_complete_type: - - -``type`` -"""""""" - - -**Default**: ``"php"`` - - -.. _RpcCommand_complete_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_complete_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_navigate: - - -_RpcHandler_navigate --------------------- - - -.. _RpcCommand_navigate_source_path: - - -``source_path`` -""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_navigate_destination: - - -``destination`` -""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_navigate_confirm_create: - - -``confirm_create`` -"""""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_context_menu: - - -_RpcHandler_context_menu ------------------------- - - -.. _RpcCommand_context_menu_action: - - -``action`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_context_menu_current_path: - - -``current_path`` -"""""""""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_context_menu_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_context_menu_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_echo: - - -_RpcHandler_echo ----------------- - - -.. _RpcCommand_echo_message: - - -``message`` -""""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_class_search: - - -_RpcHandler_class_search ------------------------- - - -.. _RpcCommand_class_search_short_name: - - -``short_name`` -"""""""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_offset_info: - - -_RpcHandler_offset_info ------------------------ - - -.. _RpcCommand_offset_info_offset: - - -``offset`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcCommand_offset_info_source: - - -``source`` -"""""""""" - - -**Default**: ``null`` - - -.. _RpcHandler_goto_definition: - - -_RpcHandler_goto_definition ---------------------------- - - -.. _RpcCommand_goto_definition_language: - - -``language`` -"""""""""""" - - -Type: string - - -Language of the current file - - -**Default**: ``"php"`` - - -.. _RpcCommand_goto_definition_target: - - -``target`` -"""""""""" - - -Type: string - - -Where should the reference be opened - - -**Default**: ``"focused_window"`` - - -**Allowed values**: "focused_window", "vsplit", "hsplit", "new_tab" - - -.. _RpcCommand_goto_definition_offset: - - -``offset`` -"""""""""" - - -Type: integer - - -Number of character into the buffer - - -**Default**: ``null`` - - -.. _RpcCommand_goto_definition_source: - - -``source`` -"""""""""" - - -Content of the current file - - -**Default**: ``null`` - - -.. _RpcCommand_goto_definition_path: - - -``path`` -"""""""" - - -Path of the current file - - -**Default**: ``null`` - - -.. _RpcHandler_goto_type: - - -_RpcHandler_goto_type ---------------------- - - -.. _RpcCommand_goto_type_language: - - -``language`` -"""""""""""" - - -Type: string - - -Language of the current file - - -**Default**: ``"php"`` - - -.. _RpcCommand_goto_type_target: - - -``target`` -"""""""""" - - -Type: string - - -Where should the reference be opened - - -**Default**: ``"focused_window"`` - - -**Allowed values**: "focused_window", "vsplit", "hsplit", "new_tab" - - -.. _RpcCommand_goto_type_offset: - - -``offset`` -"""""""""" - - -Type: integer - - -Number of character into the buffer - - -**Default**: ``null`` - - -.. _RpcCommand_goto_type_source: - - -``source`` -"""""""""" - - -Content of the current file - - -**Default**: ``null`` - - -.. _RpcCommand_goto_type_path: - - -``path`` -"""""""" - - -Path of the current file - - -**Default**: ``null`` - - -.. _RpcHandler_goto_implementation: - - -_RpcHandler_goto_implementation -------------------------------- - - -.. _RpcCommand_goto_implementation_language: - - -``language`` -"""""""""""" - - -Type: string - - -Language of the current file - - -**Default**: ``"php"`` - - -.. _RpcCommand_goto_implementation_target: - - -``target`` -"""""""""" - - -Type: string - - -Where should the reference be opened - - -**Default**: ``"focused_window"`` - - -**Allowed values**: "focused_window", "vsplit", "hsplit", "new_tab" - - -.. _RpcCommand_goto_implementation_offset: - - -``offset`` -"""""""""" - - -Type: integer - - -Number of character into the buffer - - -**Default**: ``null`` - - -.. _RpcCommand_goto_implementation_source: - - -``source`` -"""""""""" - - -Content of the current file - - -**Default**: ``null`` - - -.. _RpcCommand_goto_implementation_path: - - -``path`` -"""""""" - - -Path of the current file - - -**Default**: ``null`` - - -.. _RpcHandler_index: - - -_RpcHandler_index ------------------ - - -.. _RpcCommand_index_watch: - - -``watch`` -""""""""" - - -**Default**: ``false`` - - -.. _RpcCommand_index_interval: - - -``interval`` -"""""""""""" - - -Type: integer - - -**Default**: ``5000`` - diff --git a/doc/reference/stubs.rst b/doc/reference/stubs.rst deleted file mode 100644 index 1fb019a1e5..0000000000 --- a/doc/reference/stubs.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _stubs: - -Stubs -===== - -Phpactor has two types of "stubs": - -PHP Core Stubs --------------- - -These stubs fill in for definitions (classes, functions and constants) that are -defined in the PHP core or in PHP extensions. Phpactor expects the path to be a -single directory and that directory is configured by default to be the one -containing the `PHPStorm stubs `_ -that are bundled with Phpactor. - -You probably do **not** want to change this setting, but the directory *can* be -changed at as :ref:`param_worse_reflection.additive_stubs`. - -Additive Stubs --------------- - -These stubs **augment** existing classes. For example, in Laravel you may use the `Laravel IDE Helper `_ package to provide missing "magic" methods and properties. New definitions cannot be provided with additive stubs. - - -You can specify these stub files with :ref:`param_worse_reflection.additive_stubs` and the stubs will merge onto the existing definitions. - -.. note:: - - Additive stubs should *not* be indexed as Phpactor will be unable to - determine which definition is the canonical one. If you use additive stubs - ensure that you also manually specify which directories contain your project code (typically `src` and `tests`). - - The indexer include patterns can be specified with :ref:`param_indexer.include_patterns`. diff --git a/doc/reference/templates.rst b/doc/reference/templates.rst deleted file mode 100644 index a60f59f586..0000000000 --- a/doc/reference/templates.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _template_variants: - -Templates -========= - -Phpactor allows you to provide your own templates for some class and -code generation. - -When :ref:`generating a new class ` you can specify a **variant**. - -Variants are registered in ``.phpactor.yml`` with -:ref:`param_code_transform.class_new.variants`: - -.. code:: yaml - - code_transform.class_new.variants: - unit: phpunit_test - -This will make the variant ``unit`` available. - -Implement the templates by placing template files in -``.phpactor/templates/phpunit_test``: - -.. code:: twig - - `_ -- `PHPStan Types `_ -- `Psalm Types `_ - -Basic Types ------------ - -.. table:: - :align: left - - ============== ================== ========= ======== - Name Example PHP Phpactor - ============== ================== ========= ======== - Array ``array`` ``*`` ✔ - Boolean ``bool`` ``*`` ✔ - Float ``float`` ``*`` ✔ - Int ``int`` ``*`` ✔ - Resource (internal type) ``*`` ✔ - String ``string`` ``*`` ✔ - Self ``self`` ``*`` ✔ - Parent ``parent`` ``*`` ✔ - Callable ``callable`` ``*`` ✔ - Iterable ``iterable`` ``7.1`` ✔ - Nullable ``?Foor`` ``7.1`` ✔ - Object ``object`` ``7.2`` ✔ - Union ``Foo|Bar`` ``8.0`` ✔ - Mixed ``mixed`` ``8.0`` ✔ - Intersection ``Foo&Bar`` ``8.1`` ✔ - ============== ================== ========= ======== - -Return Only Types -~~~~~~~~~~~~~~~~~ - -.. table:: - :align: left - - ============== ================== ========= ======== ======================== - Name Example PHP Phpactor Notes - ============== ================== ========= ======== ======================== - Void ``void`` ``7.4+`` ✔ - Static ``static`` ``8.0`` ✔ - Never ``never`` ``8.1+`` ✔ - False ``false`` ``8.2+`` ✔ Pseudo-type before 8.2 - Null ``null`` ``8.2+`` ✔ - ============== ================== ========= ======== ======================== - -Docblock Types -~~~~~~~~~~~~~~ - -.. table:: - :align: left - - =============== ============================== ======== - Name Example Phpactor - =============== ============================== ======== - Array Key ``array-key`` ✔ - Array Literal ``array{string,int}`` ✔ - Array Shape ``array{foo:string,baz:int}`` ✔ - List Syntax ``string[]`` ✔ - Class String ``class-string`` ✔ - Closure ``Closure(string, int): void`` ✔ - Float Literal ``1234.12`` ✔ - Generics ``Foobar`` ✔ - Int Literal ``1234`` ✔ - Int Range ``int<0,max>`` ✔ - Int Positive ``positive-int`` ✔ - Int Negative ``negative-int`` ✔ - List ``list`` ✔ - Parenthesized ``(Foo&Bar)|object`` ✔ - String Literal ``"hello"`` ✔ - This ``$this`` (same as ``static``) ✔ - =============== ============================== ======== - -Integer Types -------------- - -.. table:: - :align: left - - ============== ============= ========= =========== - Example PHP Supported Description - ============== ============= ========= =========== - ``123`` ``*`` ✔ Integer - ``0b0110`` ``*`` ✔ Binary type - ``0x1a`` ``*`` ✔ Hexidecimal - ``0123`` ``*`` ✔ Octal - ``123_123`` ``7.4`` ✔ Decimal - ``0o123`` ``8.1`` ✘ Octal - ============== ============= ========= =========== - -Conditional Types ------------------ - -Phpactor undestands conditional return types of the form: - - -.. code-block:: php - - /** - * @return ( - * $array is array - * ? int - * : ($array is array - * ? float - * : float|int - * ) - * ) - */ - function array_some(array $array) { - return array_sum($array); - } - -Generic Types -------------- - -Phpactor understands Generic (or templated) types. See `PHPStan `_ or -`Psalm `_ -documentation for what these are and how they work. - -Phpactor supports: - -- ``@implements`` and ``@extends`` in addition to ``@template-extends`` and - ``@template-implements``. -- ``@template`` and ``@template T of Foo`` -- Injecting template variables into the constructor. -- Method level template vars. -- ``class-string`` - -For example: - -.. code-block:: php - - a = $a; - } - - /** - * @return T - */ - public function a() - { - return $this->a; - } - } - - $f = new Foo(new Bar()); - $bar = $f->a(); // Phpactor now knows that `$bar` is Bar - -In addition Phpactor supports `class-string` which allows you to capture a -class type from a class string (e.g. ``MyClass::class`` is interpreted as a -`class-string`. The following extract is from the Phpactor Container. - -.. code-block:: php - - |string $id - * @return ($id is class-string ? T : mixed) - */ - public function get($id); - } - -The conditional type enables the return value of ``get`` to be an object of -class ``T`` if the ``$id`` is a ``class-string`` or ``mixed`` in any other -case. diff --git a/doc/tips.rst b/doc/tips.rst deleted file mode 100644 index 41a102614e..0000000000 --- a/doc/tips.rst +++ /dev/null @@ -1,7 +0,0 @@ -Tips -==== - -.. toctree:: - :maxdepth: 2 - - tips/performance diff --git a/doc/tips/performance.rst b/doc/tips/performance.rst deleted file mode 100644 index 84427e9be3..0000000000 --- a/doc/tips/performance.rst +++ /dev/null @@ -1,35 +0,0 @@ -Performance -=========== - -Large Files ------------ - -Phpactor is not currently very performant as a **language server** when used on -large and complex files. This is due to the (blocking) static analysis overhead -from diagnostics. - -You can improve performance by : - -- :ref:`disabling diagnostics` when documents are _updated_ -- :ref:`disabling document highlighting` when documents are _updated_ - -You can disable both settings with: - -.. code-block:: bash - - $ phpactor config:set language_server.diagnostics_on_update false - $ phpactor config:set language_server_highlight.enabled false - -Indexing --------- - -The Phpactor indexer will include all files that satisfy the :ref:`include globs` and exclude any files in the :ref:`exclude globs`. - -Depending on your project you may want to customize this, for example, in a **Symfony** project you can avoid indexing the `var/cache` directory by excluding `/var/cache/**/*`. - -The following command (run in the project root) will update ``.phpactor.json`` to exclude cache and other common directories: - -.. code-block:: bash - - $ phpactor config:set indexer.exclude_patterns '["/vendor/**/Tests/**/*","/vendor/**/tests/**/*","/var/cache/**/*","/vendor/composer/**/*"]' - diff --git a/doc/usage.rst b/doc/usage.rst deleted file mode 100644 index ed607886fd..0000000000 --- a/doc/usage.rst +++ /dev/null @@ -1,11 +0,0 @@ -Usage -===== - -.. toctree:: - :maxdepth: 2 - - usage/getting-started - usage/standalone - usage/configuration - usage/language-server - usage/vim-plugin diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst deleted file mode 100644 index 9187fdf931..0000000000 --- a/doc/usage/configuration.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. _configuration: - -Configuration -============= - -**Trusted** configuration files are loaded from your current directory, and then -from the XDG standard user and system directories, for example: - -- ``/home/daniel/www/phpactor/phpactor/.phpactor.json`` -- ``/home/daniel/.config/phpactor/phpactor.json`` -- ``/etc/xdg/phpactor/phpactor.json`` - -Phpactor will merge configuration files, with more specific -configurations overriding the less specific ones. - -Initializing ------------- - -To create a new configuration file with a reference to the JSON schema use: - -.. code:: bash - - $ phpactor config:init - -Trusting Configuration ----------------------- - -By default Phpactor will not load configuration files from the project root or -current working directory as a maliciously placed configuration file in a project -would allow arbitrary code execution. Therefore directories must be **trusted**. - -When using the langauge server a dialog will show up asking if you trust the configuration -file if one is present. On the CLI you can use the `phpactor config:trust` command. - -Config Dump ------------ - -Use the ``config:dump`` command to show the currently loaded -configuration files and all of the current settings: - -.. code:: bash - - $ phpactor config:dump - Config files: - [✔] /home/daniel/workspace/myproject/.phpactor.json - [✔] /home/daniel/.config/phpactor/phpactor.json - [𐄂] /etc/xdg/phpactor/phpactor.yml - - code_transform.class_new.variants: - exception:exception - - # ... etc - -File Paths ----------- - -Configured file paths can make use of some special tokens, for example -``%cache%/foobar`` will expand to ``/home/user/.cache/phpactor/foobar``: - -- ``%cache%``: The absolute path to the phpactor cache dir (e.g. - ``/home/user/.cache/phpactor``). -- ``%project_root%``: Will expand to the project root (e.g. the current - working directory or the value provided by ``--working-dir``). -- ``%config%``: The path to Phpactor’s config dir - (e.g. ``/home/user/.config/phpactor``). -- ``%application_root%``: The path to Phpactor’s own root directory. - -Reference ---------- - -See: :doc:`../reference/configuration` diff --git a/doc/usage/getting-started.rst b/doc/usage/getting-started.rst deleted file mode 100644 index 4c09388f1b..0000000000 --- a/doc/usage/getting-started.rst +++ /dev/null @@ -1,29 +0,0 @@ -Getting Started -=============== - -How you start depends on your editor. - -.. tabs:: - - .. tab:: VIM or Neovim - - Configure the :doc:`Language Server<../lsp/vim>` and optionally the legacy :doc:`vim-plugin` for some additional functionality - - .. tab:: VS Code - - Install the `vscode-phpactor`_ extension - - .. tab:: Nova Code Editor - - Install the `Nova`_ extension - - .. tab:: Other Editor - - Phpactor is known to work as a language server for Emacs_, Kate_, Sublime_, Helix_ and many others, feel free to contribute setup guides for these editors to this documentation. - -.. _vscode-phpactor: https://github.com/phpactor/vscode-phpactor -.. _Nova: https://extensions.panic.com/extensions/emran-mr/emran-mr.phpactor/ -.. _Emacs: https://github.com/emacs-lsp/lsp-mode -.. _Helix: https://helix-editor.com/ -.. _Sublime: https://www.sublimetext.com/ -.. _Kate: https://kate-editor.org/en-gb/ diff --git a/doc/usage/language-server.rst b/doc/usage/language-server.rst deleted file mode 100644 index 5cac387907..0000000000 --- a/doc/usage/language-server.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _language_server: - -Language Server -=============== - -Phpactor implements the `Language Server Protocol`_ -which is supported by many text editors and IDEs. - -See :doc:`/lsp/support` for the list of supported features. - -.. toctree:: - :maxdepth: 2 - :glob: - - ../lsp/clients - -.. _Language Server Protocol: https://microsoft.github.io/language-server-protocol/specification diff --git a/doc/usage/standalone.rst b/doc/usage/standalone.rst deleted file mode 100644 index e4d0c68b38..0000000000 --- a/doc/usage/standalone.rst +++ /dev/null @@ -1,80 +0,0 @@ -.. _installation: - -Installation -============ - -Requirements ------------- - -Phpactor requires PHP 8.2. - -.. _installation_phar: - -PHAR Installation ------------------ - -You can download ``phpactor.phar`` as follows: - -.. code-block:: bash - - $ curl -Lo phpactor.phar https://github.com/phpactor/phpactor/releases/latest/download/phpactor.phar - -Then make it executable and symlink it somewhere in your PATH_: - -.. code:: bash - - $ chmod a+x phpactor.phar - $ mv phpactor.phar ~/.local/bin/phpactor - -.. _installation_global: - -Manual Installation -------------------- - -You can checkout the project and then create a symlink. - -.. code:: bash - - $ cd ~/home/you/somewhere - $ git clone https://github.com/phpactor/phpactor.git - $ cd phpactor - $ composer install - $ cd /usr/local/bin - $ sudo ln -s ~/your/projects/phpactor/bin/phpactor phpactor - -This is the best approach for bleeding edge and local development. - -Arch Linux (AUR) ----------------- - -Also available in the AUR: - -.. code:: bash - - $ yay -S phpactor - -Nix/OS ------- - -Phpactor is available in NixOS. - -.. code-block:: bash - - $ nix-shell -p phpactor - - -Health Check ------------- - -Phpactor works best when used with Composer, and is slightly better when -used with Git. - -Check support using the ``status`` command: - -:: - - $ phpactor status - ✔ Composer detected - faster class location and more features! - ✔ Git detected - enables faster refactorings in your repository scope! - -.. _PATH: https://en.wikipedia.org/wiki/PATH_(variable) diff --git a/doc/usage/vim-plugin.rst b/doc/usage/vim-plugin.rst deleted file mode 100644 index c578100e0b..0000000000 --- a/doc/usage/vim-plugin.rst +++ /dev/null @@ -1,108 +0,0 @@ -.. _vim_plugin: - -VIM RPC Plugin -============== - -This is the original VIM plugin, and is bundled with Phpactor by default. - -.. note:: - Using the RPC part of phpactor is deprecated. Most of it's features have been implemented as part of the LSP which should be used instead. - - -Installation ------------- - -**Prerequisites**: - -- `Composer `__ -- PHP 8.0 -- `VIM 8 `__ or - `Neovim `__ - -Using the `vim-plug `__ plugin -manager add the following in your VIM configuration (e.g. ``~/.vimrc`` -or ``~/.config/nvim/init.vim`` when using Neovim): - -:: - - Plug 'phpactor/phpactor', {'for': 'php', 'tag': '*', 'do': 'composer install --no-dev -o'} - -Reload VIM (or ``:source ~/.vimrc``) then update your plugins: - -:: - - :PlugInstall - -If you need to install the dependencies manually, then: - -:: - - $ cd ~/.vim/plugged/phpactor - $ composer install - -Now open a PHP file and issue the following command ``:PhpactorStatus``: - -:: - - Support - ------- - [✔] Composer detected - faster class location and more features! - [✔] Git detected - enables faster refactorings in your repository scope! - [✔] XDebug is disabled. XDebug has a negative effect on performance. - - Config files - ------------ - [✔] /home/daniel/www/phpactor/phpactor/.phpactor.yml - [✔] /home/daniel/.config/phpactor/phpactor.yml - [✘] /etc/xdg/phpactor/phpactor.yml - -To find out more about the plugin type ``:help phpactor`` - -Troubleshooting -~~~~~~~~~~~~~~~ - -``E492: Not an editor command: PhpactorStatus`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You need to open a PHP file before using Phpactor. - -``Phpactor requires at least PHP 8.0`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you run an older version of PHP by default, you will need to install -another version and set ``:phpactorPhpBin`` in your ``.vimrc`` (or equivalent): - -.. code:: vim - - let g:phpactorPhpBin = "/usr/bin/php7.3" - -``Composer not found** or **Git not detected`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The Git and Composer checks are referring to the current “workspace” -(i.e. where you started Vim from). If you've already setup Git and -Composer for your project, ensure you are starting Vim from the project -directory to enable detection. - -Usage and Configuration ------------------------ - -To find out how to use the plugin type ``:help phpactor`` or view the -:doc:`../vim-plugin/man`. - -Complementary Plugins ---------------------- - -The following plugins add more functionality to Phpactor - -- `ncm2-phpactor `__: - Integrates with the `ncm2 `__ - autocompletion manager (for Neovim). -- `deoplete-phpactor `__: - Integrates with - `deoplete `__ -- `coc-phpactor `__: - Integrates with - `CoC `__ -- `phpactor-mappings `__: - Provides sensible default key mappings for Phpactor. diff --git a/doc/vim-plugin/experimental.rst b/doc/vim-plugin/experimental.rst deleted file mode 100644 index 85036f3522..0000000000 --- a/doc/vim-plugin/experimental.rst +++ /dev/null @@ -1,53 +0,0 @@ -Experimental -============ - -FZF and BAT ------------ - -Experimental functionality with FZF and BAT depends on: - -- `fzf `__ -- `bat `__ - -In addition FZF support requires the FZF VIM plugin: - -`fzf.vim `__ - -FZF Choice Selection -~~~~~~~~~~~~~~~~~~~~ - -Some refactorings will allow you to select multiple entires (for example -`override -method `__. - -FZF provides a fuzzy search interface and the possiblity to select -multiple entries at once. - -Use ```` to toggle selection and CTRL-A/CTRL-D to select all/select -none. - -See the `Fzf `__ documentation for more -details. - -Enable this feature by configuring FZF as the ``inputlist`` strategy in -your \`.vimrc’: - -:: - - let g:phpactorInputListStrategy = 'phpactor#input#list#fzf' - -FZF Qucikfix with BAT preview -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The VIM quickfix list is used to navigate through a set of references -(where a reference is a file / character position). - -The FZF strategy provides a layer on top this to allow you to -efficiently filter, preview and select only those entries you want to -navigate to to the quickfix list. - -Enable it as follows: - -:: - - let g:phpactorQuickfixStrategy = 'phpactor#quickfix#fzf' diff --git a/doc/vim-plugin/man.rst b/doc/vim-plugin/man.rst deleted file mode 100644 index 1f732220bd..0000000000 --- a/doc/vim-plugin/man.rst +++ /dev/null @@ -1,7 +0,0 @@ -Help -==== - -You can view this manual in VIM by typing ``:help phpactor`` - -.. include:: ../phpactor.txt - :literal: diff --git a/doc/vim.rst b/doc/vim.rst deleted file mode 100644 index 756f81f86d..0000000000 --- a/doc/vim.rst +++ /dev/null @@ -1,10 +0,0 @@ -VIM Plugin -========== - -.. toctree:: - :maxdepth: 2 - :glob: - - vim-plugin/man - vim-plugin/experimental - diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 778e1d400c..0000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -services: - php: - build: - dockerfile: ./docker/Dockerfile - context: ./ - command: sleep infinity - environment: - XDG_CACHE_HOME: /phpactor/build/.cache - COLUMNS: 100 - volumes: - - ./:/phpactor:delegated diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index f01bc0efd3..0000000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM php:8.2-cli -RUN apt-get update && apt-get install -y python3-pip wget git inotify-tools libzip-dev -RUN echo "$(curl -sS https://composer.github.io/installer.sig) -" > composer-setup.php.sig \ - && curl -sS https://getcomposer.org/installer | tee composer-setup.php | sha384sum -c composer-setup.php.sig \ - && php composer-setup.php && rm composer-setup.php* \ - && chmod +x composer.phar && mv composer.phar /usr/bin/composer -RUN docker-php-ext-install pcntl zip -RUN wget https://github.com/google/vimdoc/releases/download/v0.7.1/vimdoc_0.7.1-1_all.deb && \ - dpkg -i vimdoc_0.7.1-1_all.deb -COPY requirements.txt ./ -COPY docker/php.ini /usr/local/etc/php/conf.d/ -RUN pip3 install -r requirements.txt --break-system-packages -RUN git config --global init.defaultBranch master -WORKDIR /phpactor diff --git a/docker/php.ini b/docker/php.ini deleted file mode 100644 index 7999e96156..0000000000 --- a/docker/php.ini +++ /dev/null @@ -1 +0,0 @@ -memory_limit = -1 diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 570692e78b..0000000000 --- a/flake.lock +++ /dev/null @@ -1,57 +0,0 @@ -{ - "nodes": { - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1736143030, - "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", - "type": "github" - }, - "original": { - "id": "flake-parts", - "type": "indirect" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1737879851, - "narHash": "sha256-H+FXIKj//kmFHTTW4DFeOjR7F1z2/3eb2iwN6Me4YZk=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5d3221fd57cc442a1a522a15eb5f58230f45a304", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1735774519, - "narHash": "sha256-CewEm1o2eVAnoqb6Ml+Qi9Gg/EfNAxbRx1lANGVyoLI=", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" - } - }, - "root": { - "inputs": { - "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index dd94f97029..0000000000 --- a/flake.nix +++ /dev/null @@ -1,73 +0,0 @@ -# this is a WIP flake for development and experimentation only -{ - description = "phpactor/phpactor"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - }; - - outputs = inputs @ { - self, - flake-parts, - ... - }: - flake-parts.lib.mkFlake {inherit inputs;} { - # This flake is for Linux (x86) and Apple (darwin) systems - # If you need more systems, inspect `nixpkgs.lib.systems.flakeExposed` and - # add them to this list. - # - # $ nix repl "" - # nix-repl> lib.systems.flakeExposed - systems = ["x86_64-linux" "aarch64-linux"]; - - perSystem = { - pkgs, - system, - ... - }: let - jaeger = pkgs.stdenv.mkDerivation { - pname = "jaeger"; - version = "1.49.0"; - src = pkgs.fetchurl { - url = "https://github.com/jaegertracing/jaeger/releases/download/v1.73.0/jaeger-2.10.0-linux-amd64.tar.gz"; - hash = "sha256-/hqgg1MNAqlBWOUJSjM0NikYLKye6GQAM3KinfHAUNM="; - }; - phases = [ "unpackPhase" "installPhase" "fixupPhase" ]; - installPhase = '' - mkdir -p $out/bin - install ./jaeger $out/bin - ''; - }; - phpWithXdebug = (pkgs.php84.buildEnv { - extensions = ({ enabled, all }: enabled ++ (with all; [ - xdebug - opentelemetry - ])); - extraConfig = '' - xdebug.mode=debug - ''; - }); - in { - # Run `nix fmt` to reformat the nix files - formatter = pkgs.alejandra; - - # Run `nix develop` to enter the development shell - devShells.default = pkgs.mkShellNoCC { - name = "php-devshell"; - - buildInputs = [ - jaeger - pkgs.python3 - phpWithXdebug - pkgs.php84.packages.composer - - ]; - shellHook = '' - if [ ! -d ".venv" ]; then - python3 -m venv .venv; - fi - source .venv/bin/activate;''; - }; - }; - }; -} diff --git a/ftplugin/php.vim b/ftplugin/php.vim deleted file mode 100644 index b6fb4ba2f0..0000000000 --- a/ftplugin/php.vim +++ /dev/null @@ -1,7 +0,0 @@ -augroup PhpactorInit - autocmd! * - autocmd CompleteDone call phpactor#_completeImportClass(v:completed_item) -augroup END - - -" vim: et ts=4 sw=4 fdm=marker diff --git a/ftplugin/php/commands.vim b/ftplugin/php/commands.vim deleted file mode 100644 index 304f9b758a..0000000000 --- a/ftplugin/php/commands.vim +++ /dev/null @@ -1,82 +0,0 @@ -"" -" Extract a new method from the current selection -command! -buffer -range=% PhpactorExtractMethod call phpactor#ExtractMethod() - -"" -" Extract the selected expression and assign it to a variable before (placing -" it before the current statement) -command! -buffer -range=% PhpactorExtractExpression call phpactor#ExtractExpression('v') - -"" -" Extract a constant from a literal -command! -buffer -nargs=0 PhpactorExtractConstant call phpactor#ExtractConstant() - -"" -" Import the name under the cursor. If multiple options are available, you -" are able to choose one. -command! -buffer -nargs=0 PhpactorImportClass call phpactor#ImportClass() - -"" -" Attempt to import all non-resolvable classes in the current class (based -" on offset position) -command! -buffer -nargs=0 PhpactorImportMissingClasses call phpactor#ImportMissingClasses() - -"" -" Show information about the symbol under the cursor. -command! -buffer -nargs=0 PhpactorHover call phpactor#Hover() - -"" -" Show the context menu for the current cursor position. -command! -buffer -nargs=0 PhpactorContextMenu call phpactor#ContextMenu() - -"" -" Copy the current file - updating the namespace and class name according to -" the new file location and name -command! -buffer -nargs=0 PhpactorCopyFile call phpactor#CopyFile() - -"" -" Copy the current class FQN (based on current filename) to the clipboard -command! -buffer -nargs=0 PhpactorCopyClassName call phpactor#CopyFullClassName() - -"" -" Move the current file - updating the namespace and class name according to -" the new file location and name -command! -buffer -nargs=0 PhpactorMoveFile call phpactor#MoveFile() - -"" -" Inflect a new class from the current class (e.g. generate an interface for -" the current class) -command! -buffer -nargs=0 PhpactorClassInflect call phpactor#ClassInflect() - -"" -" Attempt to find all references to the class name or method under the cursor. -" The results will be loaded into the quik-fix list -command! -buffer -nargs=0 PhpactorFindReferences call phpactor#FindReferences() - -"" -" Navigate - jump to the parent class, interface, or any of the relationships -" defined in `navigation.destinations` https://phpactor.github.io/phpactor/configuration.html#reference -command! -buffer -nargs=0 PhpactorNavigate call phpactor#Navigate() - -"" -" Rotate the visiblity of the method under the cursor -command! -buffer -nargs=0 PhpactorChangeVisibility call phpactor#ChangeVisibility() - -"" -" Generate accessors for the current class -command! -buffer -nargs=0 PhpactorGenerateAccessors call phpactor#GenerateAccessors() - -"" -" Generate mutators for the current class -command! -buffer -nargs=0 PhpactorGenerateMutators call phpactor#GenerateMutators() - -"" -" Automatically add any missing properties to a class -command! -buffer -nargs=0 PhpactorTransform call phpactor#Transform() - -"" -" Trust configuration in the current working directory -command! -buffer -nargs=0 PhpactorTrust call phpactor#Trust() - -" Revoke trust in the current working directory -command! -buffer -nargs=0 PhpactorUntrust call phpactor#Untrust() diff --git a/ftplugin/php/mappings.vim b/ftplugin/php/mappings.vim deleted file mode 100644 index f4c406707d..0000000000 --- a/ftplugin/php/mappings.vim +++ /dev/null @@ -1,34 +0,0 @@ -"" -" @section Mappings -" -" Phpactor does not assume any mappings automatically, the following mappings -" are available for you to copy: > -" -" augroup PhpactorMappings -" au! -" au FileType php nmap u :PhpactorImportClass -" au FileType php nmap e :PhpactorClassExpand -" au FileType php nmap ua :PhpactorImportMissingClasses -" au FileType php nmap mm :PhpactorContextMenu -" au FileType php nmap nn :PhpactorNavigate -" au FileType php,cucumber nmap o -" \ :PhpactorGotoDefinition edit -" au FileType php nmap K :PhpactorHover -" au FileType php nmap tt :PhpactorTransform -" au FileType php nmap cc :PhpactorClassNew -" au FileType php nmap ci :PhpactorClassInflect -" au FileType php nmap fr :PhpactorFindReferences -" au FileType php nmap mf :PhpactorMoveFile -" au FileType php nmap cf :PhpactorCopyFile -" au FileType php nmap ee -" \ :PhpactorExtractExpression -" au FileType php vmap ee -" \ :PhpactorExtractExpression -" au FileType php vmap em -" \ :PhpactorExtractMethod -" augroup END -" < -" -" Note: the cucumber mappings are for the Behat extension: -" -" https://github.com/phpactor/behat-extension diff --git a/lib/Amp/Mod.php b/lib/Amp/Mod.php deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Amp/Process/ProcessBuilder.php b/lib/Amp/Process/ProcessBuilder.php deleted file mode 100644 index a9b3f532d1..0000000000 --- a/lib/Amp/Process/ProcessBuilder.php +++ /dev/null @@ -1,70 +0,0 @@ - - */ - private array $env = []; - - private ?string $cwd = null; - - private bool $mergeParentEnv = false; - - /** - * @param list $args - */ - private function __construct(private array $args) - { - } - - /** - * @param list $args - */ - public function cmd(array $args): self - { - $this->args = $args; - return $this; - } - - public function cwd(string $cwd): self - { - $this->cwd = $cwd; - return $this; - } - /** - * @param array $env - */ - public function env(array $env): self - { - $this->env = $env; - return $this; - } - - public function mergeParentEnv(): self - { - $this->mergeParentEnv = true; - return $this; - } - - public function build(): Process - { - $env = $this->env; - if ($this->mergeParentEnv) { - $env = array_merge(getenv(), $env); - } - return new Process($this->args, $this->cwd, $env); - } - - /** - * @param array $args - */ - public static function create(array $args): self - { - return new self($args); - } -} diff --git a/lib/Amp/Process/ProcessUtil.php b/lib/Amp/Process/ProcessUtil.php deleted file mode 100644 index 0fa9186f73..0000000000 --- a/lib/Amp/Process/ProcessUtil.php +++ /dev/null @@ -1,37 +0,0 @@ -isRunning()) { - yield delay(500); - // phpstan doesn't expect that $process->isRunning() output can change - // @phpstan-ignore-next-line - if (time() >= $start + $timeout && $process->isRunning()) { - try { - $process->kill(); - $logger->warning(sprintf( - 'Killed process "%s" (%s) because it lived longer than %ds', - $process->getPid(), - $process->getCommand(), - $timeout - )); - } catch (StatusError $e) { - } - break; - } - } - }); - } -} diff --git a/lib/Amp/Tests/Process/ProcessBuilderTest.php b/lib/Amp/Tests/Process/ProcessBuilderTest.php deleted file mode 100644 index 8d906b5216..0000000000 --- a/lib/Amp/Tests/Process/ProcessBuilderTest.php +++ /dev/null @@ -1,56 +0,0 @@ -makeEnvDumpProcess()->build(); - $pid = wait($process->start()); - $exitCode = wait($process->join()); - self::assertEquals(0, $exitCode); - } - - public function testDoesNotMergeEnvByDefaultWhenEnvVarsPassed(): void - { - putenv('FOO='.self::PARENT_PROCESS_ENV_VAR); - $process = $this->makeEnvDumpProcess()->env(['ENV'=> 'myenvvar'])->build(); - $pid = wait($process->start()); - /** @var string $out @phpstan-ignore-next-line */ - $out = wait(buffer($process->getStdout())); - self::assertStringContainsString('myenvvar', $out); - self::assertStringNotContainsString(self::PARENT_PROCESS_ENV_VAR, $out); - $exitCode = wait($process->join()); - self::assertEquals(0, $exitCode); - putenv('FOO'); - } - - public function testInheritsWhenInstructedToWhenEnvVarsPassed(): void - { - putenv('FOO='.self::PARENT_PROCESS_ENV_VAR); - $process = $this->makeEnvDumpProcess()->env(['ENV'=> 'myenvvar'])->mergeParentEnv()->build(); - $pid = wait($process->start()); - /** @var string $out @phpstan-ignore-next-line */ - $out = wait(buffer($process->getStdout())); - self::assertStringContainsString('myenvvar', $out); - self::assertStringContainsString(self::PARENT_PROCESS_ENV_VAR, $out); - $exitCode = wait($process->join()); - self::assertEquals(0, $exitCode); - putenv('FOO'); - } - - private function makeEnvDumpProcess(): ProcessBuilder - { - // Short script that just prints its environment variables, - // so that the tests can verify what it received. - return ProcessBuilder::create([PHP_BINARY, '-r', 'var_dump(getenv());']); - } -} diff --git a/lib/Application.php b/lib/Application.php deleted file mode 100644 index bbaca81ebd..0000000000 --- a/lib/Application.php +++ /dev/null @@ -1,117 +0,0 @@ -initialize($input, $output); - $this->setCatchExceptions(false); - - if ($output->isVerbose()) { - $handler = new StreamHandler(STDERR); - $handler->setFormatter($this->container->get(PrettyFormatter::class)); - $this->container->get(LoggingExtension::SERVICE_LOGGER)->pushHandler($handler); - } - - $formatter = $output->getFormatter(); - $formatter->setStyle('highlight', new OutputFormatterStyle('red', null, [ 'bold' ])); - $formatter->setStyle('diff-add', new OutputFormatterStyle('green', null, [ ])); - $formatter->setStyle('diff-remove', new OutputFormatterStyle('red', null, [ ])); - - try { - return parent::doRun($input, $output); - } catch (Exception $e) { - if ( - $input->hasArgument('command') - && ($command = $input->getArgument('command')) - && $command !== 'list' - && $input->hasOption('format') - && $input->getOption('format') - ) { - /** @var string $format */ - $format = $input->getOption('format'); - - return $this->handleException($output, $format, $e); - } - - if ($output instanceof ConsoleOutputInterface) { - $this->renderThrowable($e, $output->getErrorOutput()); - } - - return 255; - } - } - - protected function getDefaultInputDefinition(): InputDefinition - { - $definition = parent::getDefaultInputDefinition(); - $definition->addOption(new InputOption('working-dir', 'd', InputOption::VALUE_REQUIRED, 'Working directory')); - $definition->addOption(new InputOption('config-extra', null, InputOption::VALUE_REQUIRED, 'Additional config to apply (JSON string)')); - - return $definition; - } - - private function handleException(OutputInterface $output, string $dumper, Exception $e): int - { - $errors = [ - 'error' => $this->serializeException($e), - 'previous' => [ - ], - ]; - - $this->container->get('logging.logger')->error($e->getMessage()); - - while ($e = $e->getPrevious()) { - $errors['previous'][] = $this->serializeException($e); - } - - $this->container->get('console.dumper_registry')->get($dumper)->dump($output, $errors); - - return 64; - } - - /** - * @return array - */ - private function serializeException(Throwable $e): array - { - return [ - 'class' => get_class($e), - 'code' => $e->getCode(), - 'message' => $e->getMessage(), - ]; - } - - private function initialize(InputInterface $input, OutputInterface $output): void - { - $this->container = Phpactor::boot($input, $output, $this->vendorDir, $this->phpactorBin); - - $this->setCommandLoader($this->container->get(ConsoleExtension::SERVICE_COMMAND_LOADER)); - } -} diff --git a/lib/Cast/Cast.php b/lib/Cast/Cast.php deleted file mode 100644 index e4b8ceed34..0000000000 --- a/lib/Cast/Cast.php +++ /dev/null @@ -1,77 +0,0 @@ - - */ - public static function toArray($value): array - { - return (array) $value; - } -} diff --git a/lib/ClassMover/Adapter/TolerantParser/TolerantClassFinder.php b/lib/ClassMover/Adapter/TolerantParser/TolerantClassFinder.php deleted file mode 100644 index 63cca6ba1f..0000000000 --- a/lib/ClassMover/Adapter/TolerantParser/TolerantClassFinder.php +++ /dev/null @@ -1,198 +0,0 @@ -parser->get($source); - - $namespaceRef = $this->getNamespaceRef($ast); - $sourceEnvironment = $this->getClassEnvironment($namespaceRef->namespace(), $ast); - - $classRefs = $this->resolveClassNames($source, $sourceEnvironment, $ast); - - return NamespacedClassReferences::fromNamespaceAndClassRefs($namespaceRef, $classRefs); - } - - /** @return array */ - private function resolveClassNames(TextDocument $source, NameImportTable $env, SourceFileNode $ast): array - { - $classRefs = []; - $nodes = $ast->getDescendantNodes(); - - foreach ($nodes as $node) { - if ( - $node instanceof ClassDeclaration || - $node instanceof EnumDeclaration || - $node instanceof InterfaceDeclaration || - $node instanceof TraitDeclaration - ) { - $name = (string) $node->name->getText($node->getFileContents()); - - if (!$name) { - continue; - } - - $classRefs[] = ClassReference::fromNameAndPosition( - QualifiedName::fromString($name), - FullyQualifiedName::fromString($node->getNamespacedName()->getFullyQualifiedNameText()), - Position::fromStartAndEnd($node->name->start, $node->name->start + $node->name->length - 1), - ImportedNameReference::none(), - true - ); - continue; - } - - // we want QualifiedNames - if (!$node instanceof ParserQualifiedName) { - continue; - } - - // (the) namepspace definition is not interesting - if ($node->getParent() instanceof NamespaceDefinition) { - continue; - } - - if ($node->getParent() instanceof CallExpression) { - continue; - } - - $qualifiedName = QualifiedName::fromString($node->getText()); - - // we want to replace all fully qualified use statements - $parentNode = $node->getParent(); - if ($parentNode instanceof NamespaceUseClause) { - $classRefs[] = ClassReference::fromNameAndPosition( - FullyQualifiedName::fromString($node->getText()), - FullyQualifiedName::fromString($node->getText()), - Position::fromStartAndEnd($node->getStartPosition(), $node->getEndPosition()), - ImportedNameReference::none(), - false, - // @phpstan-ignore-next-line It can be NULL - $parentNode->namespaceAliasingClause ? true : false, - true, - ); - continue; - } - - $resolvedClassName = $env->resolveClassName($qualifiedName); - - // if the name is aliased, then we can safely ignore it - if ($env->isAliased($qualifiedName)) { - continue; - } - - // this is a fully qualified class name - $importedNameReference = null; - if ($env->isNameImported($qualifiedName)) { - $importedNameReference = $env->getImportedNameRefFor($qualifiedName); - } - - $classRefs[] = ClassReference::fromNameAndPosition( - $qualifiedName, - $resolvedClassName, - Position::fromStartAndEnd($node->getStartPosition(), $node->getEndPosition()), - $importedNameReference ?? ImportedNameReference::none() - ); - } - - return $classRefs; - } - - private function getClassEnvironment(Namespace_ $namespace, SourceFileNode $node): NameImportTable - { - $useImportRefs = []; - foreach ($node->getChildNodes() as $childNode) { - if (false === $childNode instanceof NamespaceUseDeclaration) { - continue; - } - - $this->populateUseImportRefs($childNode, $useImportRefs); - } - - return NameImportTable::fromImportedNameRefs($namespace, $useImportRefs); - } - - /** - * @param array $useImportRefs - */ - private function populateUseImportRefs(NamespaceUseDeclaration $useDeclaration, array &$useImportRefs): void - { - if (null === $useDeclaration->useClauses) { - return; - } - - foreach ($useDeclaration->useClauses->getElements() as $useClause) { - /** @var NamespaceUseClause $useClause */ - $importedName = ImportedName::fromString((string) $useClause->namespaceName->getText()); - $alias = $importedName; - - /** @var NamespaceAliasingClause|null $aliasClause */ - $aliasClause = $useClause->namespaceAliasingClause; - if ($useClause->namespaceAliasingClause !== null) { - $alias = $useClause->namespaceAliasingClause->name->getText($useDeclaration->getFileContents()); - $importedName = $importedName->withAlias((string) $alias); - } - - $useImportRefs[] = ImportedNameReference::fromImportedNameAndPosition($importedName, Position::fromStartAndEnd( - $useDeclaration->getStartPosition(), - $useDeclaration->getEndPosition() - )); - } - } - - private function getNamespaceRef(SourceFileNode $ast): NamespaceReference - { - /** @var NamespaceDefinition|null $namespace */ - $namespace = $ast->getFirstDescendantNode(NamespaceDefinition::class); - - if (null === $namespace) { - return NamespaceReference::forRoot(); - } - - if (null === $namespace->name || $namespace->name instanceof MissingToken) { - return NamespaceReference::forRoot(); - } - - return NamespaceReference::fromNameAndPosition( - Namespace_::fromString($namespace->name->getText()), - Position::fromStartAndEnd( - $namespace->name->getStartPosition(), - $namespace->name->getEndPosition() - ) - ); - } -} diff --git a/lib/ClassMover/Adapter/TolerantParser/TolerantClassReplacer.php b/lib/ClassMover/Adapter/TolerantParser/TolerantClassReplacer.php deleted file mode 100644 index 5ee4022aa6..0000000000 --- a/lib/ClassMover/Adapter/TolerantParser/TolerantClassReplacer.php +++ /dev/null @@ -1,125 +0,0 @@ -name()->wasFullyQualified()) { - $edits[] = TextEdit::create( - $classRef->position()->start(), - $classRef->position()->length(), - '\\'.$newName->__toString() - ); - continue; - } - - if (false === $importClass) { - $importClass = $this->shouldImportClass($classRef, $originalName); - } - - if ($this->classIsTheOriginalInstance($classRef, $originalName)) { - $addNamespace = $classRefList->namespaceRef()->namespace()->isRoot(); - - if (false === $addNamespace) { - $edits[] = $this->replaceOriginalInstanceNamespace($classRefList, $newName); - } - } - - $edits[] = TextEdit::create( - $classRef->position()->start(), - $classRef->position()->length(), - $classRef->name()->transpose($newName)->__toString() - ); - } - - // make sure the edits are ordered - usort($edits, function (TextEdit $a, TextEdit $b) { - return $a->start()->toInt() <=> $b->start()->toInt(); - }); - - $edits = TextEdits::fromTextEdits($edits); - if (true === $importClass) { - $edits = $edits->merge($this->addUseStatement($source, $newName)); - } - - if (true === $addNamespace) { - $edits = $edits->merge($this->addNamespace($source, $newName->parentNamespace())); - } - - return $edits; - } - - private function replaceOriginalInstanceNamespace(NamespacedClassReferences $classRefList, FullyQualifiedName $newName): TextEdit - { - return TextEdit::create( - $classRefList->namespaceRef()->position()->start(), - $classRefList->namespaceRef()->position()->length(), - $newName->parentNamespace()->__toString() - ); - } - - private function shouldImportClass(ClassReference $classRef, FullyQualifiedName $originalName): bool - { - if ($classRef->isImport()) { - return false; - } - if ($classRef->hasAlias()) { - return false; - } - if (ImportedNameReference::none() != $classRef->importedNameRef()) { - return false; - } - - if ($classRef->isClassDeclaration()) { - return false; - } - - return $classRef->fullName()->equals($originalName); - } - - private function classIsTheOriginalInstance(ClassReference $classRef, FullyQualifiedName $originalName): bool - { - return $classRef->isClassDeclaration() && $classRef->fullName()->equals($originalName); - } - - private function addUseStatement(TextDocument $source, FullyQualifiedName $newName): TextEdits - { - return $this->updater->textEditsFor(SourceCodeBuilder::create()->use($newName->__toString())->build(), $source); - } - - private function addNamespace(TextDocument $source, QualifiedName $qualifiedName): TextEdits - { - return $this->updater->textEditsFor( - SourceCodeBuilder::create()->namespace($qualifiedName->__toString())->build(), - $source - ); - } -} diff --git a/lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberFinder.php b/lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberFinder.php deleted file mode 100644 index 61b2877977..0000000000 --- a/lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberFinder.php +++ /dev/null @@ -1,427 +0,0 @@ -reflector = $reflector ?: ReflectorBuilder::create()->addSource(TextDocumentBuilder::empty()); - } - - public function findMembers(SourceCode $source, ClassMemberQuery $query): MemberReferences - { - $rootNode = $this->parser->get($source); - $memberNodes = $this->collectMemberReferences($rootNode, $query); - - $queryClassReflection = null; - // TODO: Factor this to a method - if ($query->hasClass()) { - $queryClassReflection = $this->resolveBaseReflectionClass($query); - } - - $references = []; - foreach ($memberNodes as $memberNode) { - if ($memberNode instanceof ScopedPropertyAccessExpression && $reference = $this->getScopedPropertyAccessReference($query, $memberNode)) { - $references[] = $reference; - continue; - } - - if ($memberNode instanceof MemberAccessExpression && $reference = $this->getMemberAccessReference($query, $memberNode)) { - $references[] = $reference; - continue; - } - - if ($memberNode instanceof MethodDeclaration && $reference = $this->getMemberDeclarationReference($queryClassReflection, $memberNode)) { - $references[] = $reference; - continue; - } - - // properties ... - if ($memberNode instanceof Variable && $reference = $this->getMemberDeclarationReference($queryClassReflection, $memberNode)) { - $references[] = $reference; - continue; - } - - if ($memberNode instanceof ConstElement && $reference = $this->getMemberDeclarationReference($queryClassReflection, $memberNode)) { - $references[] = $reference; - continue; - } - } - - return MemberReferences::fromMemberReferences($references)->unique(); - } - - /** - * Collect all nodes which reference the method NAME. - * We will check if they belong to the requested class later. - * - * @return array - */ - private function collectMemberReferences(Node $node, ClassMemberQuery $query): array - { - $memberNodes = []; - $memberName = null; - - if (false === $query->hasType() || $query->type() === ClassMemberQuery::TYPE_METHOD) { - $this->collectMethods($node, $query, $memberNodes); - } - - if (false === $query->hasType() || $query->type() === ClassMemberQuery::TYPE_PROPERTY) { - $this->collectProperties($node, $query, $memberNodes); - } - - if (false === $query->hasType() || $query->type() === ClassMemberQuery::TYPE_CONSTANT) { - $this->collectConstants($node, $query, $memberNodes); - } - - foreach ($node->getChildNodes() as $childNode) { - $memberNodes = array_merge($memberNodes, $this->collectMemberReferences($childNode, $query)); - } - - return $memberNodes; - } - - /** @param array $memberNodes */ - private function collectMethods(Node $node, ClassMemberQuery $query, array &$memberNodes): void - { - if ($node instanceof MethodDeclaration) { - $memberName = (string) $node->name?->getText($node->getFileContents()); - - if ($query->matchesMemberName($memberName)) { - $memberNodes[] = $node; - } - } - - if ($this->isMethodAccess($node)) { - assert($node instanceof CallExpression); - $callableExpression = $node->callableExpression; - assert($callableExpression instanceof ScopedPropertyAccessExpression || $callableExpression instanceof MemberAccessExpression); - $memberName = $callableExpression->memberName->getText($node->getFileContents()); - - if ($query->matchesMemberName($memberName)) { - $memberNodes[] = $node->callableExpression; - } - } - } - - /** @param array $memberNodes */ - private function collectConstants(Node $node, ClassMemberQuery $query, array &$memberNodes): void - { - if ($node instanceof ClassConstDeclaration) { - if ($node->constElements->children) { - foreach ($node->constElements->getChildNodes() as $constElement) { - assert($constElement instanceof ConstElement); - $memberName = (string) $constElement->name->getText($constElement->getFileContents()); - if ($query->matchesMemberName($memberName)) { - $memberNodes[] = $constElement; - } - } - } - } - - if ($node instanceof ScopedPropertyAccessExpression && false === $node->parent instanceof CallExpression) { - $memberName = (string) $node->memberName->getText($node->getFileContents()); - if ($query->matchesMemberName($memberName)) { - $memberNodes[] = $node; - } - } - } - - /** @param array $memberNodes */ - private function collectProperties(Node $node, ClassMemberQuery $query, array &$memberNodes): void - { - if ($node instanceof PropertyDeclaration) { - if ($node->propertyElements->children) { - foreach ($node->propertyElements->getChildNodes() as $propertyElement) { - if ($propertyElement instanceof PropertyElement) { - $variable = $propertyElement->variable; - if (null === $variable) { - continue; - } - - $memberName = (string) $variable->name->getText($propertyElement->getFileContents()); - if ($query->matchesMemberName($memberName)) { - $memberNodes[] = $variable; - } - } - } - } - } - - // property access - only if it is not part of a call() expression - if ($node instanceof MemberAccessExpression && false === $node->parent instanceof CallExpression) { - $memberName = $node->memberName->getText($node->getFileContents()); - if (is_string($memberName) && $query->matchesMemberName($memberName)) { - $memberNodes[] = $node; - } - } - - if ($node instanceof ScopedPropertyAccessExpression && false === $node->parent instanceof CallExpression) { - $memberName = (string) $node->memberName->getText($node->getFileContents()); - - // TODO: Some better way to determine if member names are properties - if (str_starts_with($memberName, '$') && $query->matchesMemberName($memberName)) { - $memberNodes[] = $node; - } - } - } - - private function isMethodAccess(Node $node): bool - { - if (false === $node instanceof CallExpression) { - return false; - } - - if (null === $node->callableExpression) { - return false; - } - - return - $node->callableExpression instanceof MemberAccessExpression || - $node->callableExpression instanceof ScopedPropertyAccessExpression; - } - - private function getMemberDeclarationReference(?ReflectionClassLike $queryClass, Node $memberNode): ?MemberReference - { - assert($memberNode instanceof MethodDeclaration || $memberNode instanceof ConstElement || $memberNode instanceof Variable); - // we don't handle Variable calls yet. - if (false === $memberNode->name instanceof Token) { - $this->logger->warning('Do not know how to infer method name from variable'); - return null; - } - - $memberName = MemberName::fromString((string) $memberNode->name->getText($memberNode->getFileContents())); - $reference = MemberReference::fromMemberNameAndPosition( - $memberName, - Position::fromStartAndEnd( - $this->memberStartPosition($memberNode), - $memberNode->name->start + $memberNode->name->length - 1 - ) - ); - - /** @var ClassDeclaration|InterfaceDeclaration|TraitDeclaration|null $classNode */ - $classNode = $memberNode->getFirstAncestor(ClassDeclaration::class, InterfaceDeclaration::class, TraitDeclaration::class); - - // if no class node found, then this is not valid, don't know how to reproduce this, probably - // not a possible scenario with the parser. - if (null === $classNode) { - return null; - } - - $className = ClassName::fromString($classNode->getNamespacedName()); - $reference = $reference->withClass(Class_::fromString($className)); - - if (null === $queryClass) { - return $reference; - } - - if (null === $reflectionClass = $this->reflectClassLike($className)) { - $this->logger->warning(sprintf('Could not find class "%s" for method declaration, ignoring it', (string) $className)); - return null; - } - - // if the references class is not an instance of the requested class, or the requested class is not - // an instance of the referenced class then ignore it. - if ((!$reflectionClass instanceof ReflectionTrait) && false === $reflectionClass->isInstanceOf($queryClass->name())) { - return null; - } - - return $reference; - } - - /** - * Get static method call. - * TODO: This does not support overridden static methods. - */ - private function getScopedPropertyAccessReference(ClassMemberQuery $query, ScopedPropertyAccessExpression $memberNode): ?MemberReference - { - if ($memberNode->scopeResolutionQualifier instanceof Variable) { - return null; - } - - $memberNameToken = $memberNode->memberName; - $startOffset = 0; - if ($memberNameToken instanceof Variable) { - $memberNameToken = $memberNameToken->name; - $startOffset++; // do not include the $ - } - - if (false === $memberNameToken instanceof Token) { - return null; - } - - $memberName = (string) $memberNameToken->getText($memberNode->getFileContents()); - - $reference = MemberReference::fromMemberNameAndPosition( - MemberName::fromString($memberName), - Position::fromStartAndEnd( - $memberNameToken->start + $startOffset, - $memberNameToken->start + $memberNameToken->length - ) - ); - - $offset = $this->reflector->reflectOffset( - NodeToTextDocumentConverter::convert($memberNode), - ByteOffset::fromInt($memberNode->scopeResolutionQualifier->getEndPosition()) - ); - - return $this->attachClassInfoToReference($reference, $query, $offset); - } - - private function getMemberAccessReference(ClassMemberQuery $query, MemberAccessExpression $memberNode): ?MemberReference - { - /** @var Token|null */ - $memberName = $memberNode->memberName; - if (false === $memberName instanceof Token) { - $this->logger->warning('Do not know how to infer method name from variable'); - return null; - } - - $reference = MemberReference::fromMemberNameAndPosition( - MemberName::fromString((string) $memberNode->memberName->getText($memberNode->getFileContents())), - Position::fromStartAndEnd( - $memberNode->memberName->start, - $memberNode->memberName->start + $memberNode->memberName->length - ) - ); - - $offset = $this->reflector->reflectOffset( - NodeToTextDocumentConverter::convert($memberNode), - ByteOffset::fromInt($memberNode->dereferencableExpression->getEndPosition()) - ); - - return $this->attachClassInfoToReference($reference, $query, $offset); - } - - private function reflectClassLike(ClassName $className): ?ReflectionClassLike - { - try { - return $this->reflector->reflectClassLike($className); - } catch (NotFound) { - return null; - } - } - - private function resolveBaseReflectionClass(ClassMemberQuery $query): ?ReflectionClassLike - { - $queryClassReflection = $this->reflectClassLike(ClassName::fromString((string) $query->class())); - - if (null === $queryClassReflection) { - return $queryClassReflection; - } - - $methods = $queryClassReflection->methods(); - - if (false === $query->hasMember()) { - return $queryClassReflection; - } - - if (false === $methods->has($query->memberName())) { - return $queryClassReflection; - } - - if (!$queryClassReflection instanceof ReflectionClass) { - return $queryClassReflection; - } - - // TODO: Support the case where interfaces both implement the same method - foreach ($queryClassReflection->interfaces() as $interfaceReflection) { - if ($interfaceReflection->methods()->has($query->memberName())) { - $queryClassReflection = $interfaceReflection; - break; - } - } - - return $queryClassReflection; - } - - private function attachClassInfoToReference(MemberReference $reference, ClassMemberQuery $query, ReflectionOffset $offset): ?MemberReference - { - $type = $offset->nodeContext()->type()->expandTypes()->classLike()->firstOrNull(); - - if ($query->hasMember() && !$type) { - return $reference; - } - if (!$type instanceof ReflectedClassType) { - return null; - } - - if (false === $query->hasClass()) { - $reference = $reference->withClass(Class_::fromString((string) $type->name()->full())); - return $reference; - } - - - $accepts = $type->instanceof(TypeFactory::reflectedClass($this->reflector, (string) $query->class())); - - if ($accepts->isMaybe()) { - return $reference; - } - if ($accepts->isFalse()) { - return null; - } - - return $reference->withClass(Class_::fromString((string) $type->name()->full())); - } - - private function memberStartPosition(Node $memberNode): int - { - assert($memberNode instanceof MethodDeclaration || $memberNode instanceof ConstElement || $memberNode instanceof Variable); - $name = $memberNode->name; - assert($name !== null); - $start = $name->start; - - if ($memberNode->getFirstAncestor(PropertyDeclaration::class)) { - return $start + 1; // ignore the dollar sign - } - - return $start; - } -} diff --git a/lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberReplacer.php b/lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberReplacer.php deleted file mode 100644 index 9c370a3530..0000000000 --- a/lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberReplacer.php +++ /dev/null @@ -1,26 +0,0 @@ -position()->start(), $reference->position()->length(), $newName); - } - - $source = $source->replaceSource(TextEdit::applyEdits($edits, $source->__toString())); - - return $source; - } -} diff --git a/lib/ClassMover/ClassMover.php b/lib/ClassMover/ClassMover.php deleted file mode 100644 index 9b430756c9..0000000000 --- a/lib/ClassMover/ClassMover.php +++ /dev/null @@ -1,42 +0,0 @@ -build(); - $name = FullyQualifiedName::fromString($fullyQualifiedName); - $references = $this->finder->findIn($source)->filterForName($name); - - return new FoundReferences($source, $name, $references); - } - - public function replaceReferences(FoundReferences $foundReferences, string $newFullyQualifiedName): TextEdits - { - $newName = FullyQualifiedName::fromString($newFullyQualifiedName); - return $this->replacer->replaceReferences( - $foundReferences->source(), - $foundReferences->references(), - $foundReferences->targetName(), - $newName - ); - } -} diff --git a/lib/ClassMover/Domain/ClassFinder.php b/lib/ClassMover/Domain/ClassFinder.php deleted file mode 100644 index 6cf7e6b44d..0000000000 --- a/lib/ClassMover/Domain/ClassFinder.php +++ /dev/null @@ -1,11 +0,0 @@ - */ - private array $validTypes = [ - self::TYPE_CONSTANT, - self::TYPE_METHOD, - self::TYPE_PROPERTY - ]; - - private ?string $type; - - private function __construct( - private ?Class_ $class = null, - private ?MemberName $memberName = null, - ?string $type = null - ) { - if (null !== $type && false === in_array($type, $this->validTypes)) { - throw new InvalidArgumentException(sprintf( - 'Invalid member type "%s", valid types: "%s"', - $type, - implode('", "', $this->validTypes) - )); - } - - $this->type = $type; - } - - public function __toString(): string - { - return (string) $this->class; - } - - public static function create(): ClassMemberQuery - { - return new self(); - } - - public function onlyConstants(): self - { - return new self( - $this->class, - $this->memberName, - self::TYPE_CONSTANT - ); - } - - public function onlyMethods(): self - { - return new self( - $this->class, - $this->memberName, - self::TYPE_METHOD - ); - } - - public function onlyProperties(): self - { - return new self( - $this->class, - $this->memberName, - self::TYPE_PROPERTY - ); - } - - /** - * If the argument is anything other than a Class_ or string then it will throw an error. - * - * @param Class_|string|mixed $className - */ - public function withClass($className): ClassMemberQuery - { - if (false === is_string($className) && false === $className instanceof Class_) { - throw new InvalidArgumentException(sprintf( - 'Class must be either a string or an instanceof Class_, got: "%s"', - gettype($className) - )); - } - - return new self( - is_string($className) ? Class_::fromString($className) : $className, - $this->memberName, - $this->type - ); - } - - /** - * If the argument is anything but a MemberName or a string this class will throw an error. - * - * @param MemberName|string|mixed $memberName - */ - public function withMember($memberName): ClassMemberQuery - { - if (false === is_string($memberName) && false === $memberName instanceof MemberName) { - throw new InvalidArgumentException(sprintf( - 'Member must be either a string or an instanceof MemberName, got: "%s"', - gettype($memberName) - )); - } - - return new self( - $this->class, - is_string($memberName) ? MemberName::fromString($memberName) : $memberName, - $this->type - ); - } - - public function withType(string $memberType): ClassMemberQuery - { - return new self( - $this->class, - $this->memberName, - $memberType - ); - } - - public function memberName(): ?MemberName - { - return $this->memberName; - } - - public function matchesMemberName(string $memberName): bool - { - if (null === $this->memberName) { - return true; - } - - return $this->memberName->matches($memberName); - } - - public function matchesClass(string $className): bool - { - if (null === $this->class) { - return true; - } - - return $className == (string) $this->class; - } - - public function class(): ?Class_ - { - return $this->class; - } - - public function type(): ?string - { - return $this->type; - } - - public function hasType(): bool - { - return null !== $this->type; - } - - public function hasClass(): bool - { - return null !== $this->class; - } - - public function hasMember(): bool - { - return null !== $this->memberName; - } -} diff --git a/lib/ClassMover/Domain/Model/Class_.php b/lib/ClassMover/Domain/Model/Class_.php deleted file mode 100644 index fd1f93b536..0000000000 --- a/lib/ClassMover/Domain/Model/Class_.php +++ /dev/null @@ -1,27 +0,0 @@ -name; - } - - public static function fromFullyQualifiedName(FullyQualifiedName $name): self - { - return new self($name); - } - - public static function fromString(string $name): self - { - return self::fromFullyQualifiedName(FullyQualifiedName::fromString($name)); - } -} diff --git a/lib/ClassMover/Domain/Name/FullyQualifiedName.php b/lib/ClassMover/Domain/Name/FullyQualifiedName.php deleted file mode 100644 index a926e0ca5f..0000000000 --- a/lib/ClassMover/Domain/Name/FullyQualifiedName.php +++ /dev/null @@ -1,11 +0,0 @@ -parts); - } - - public function getShortName(): string - { - /** @var string $lastPart */ - $lastPart = end($this->parts); - - return $lastPart; - } - - public function qualifies(QualifiedName $name): bool - { - $head = $this->alias ?: $this->head(); - $qualifies = $head === $name->base(); - - return $qualifies; - } - - public function qualify(QualifiedName $name): FullyQualifiedName - { - return FullyQualifiedName::fromString($this->parentNamespace()->__toString().'\\'.$name->__toString()); - } - - public function withAlias(string $alias): self - { - $new = new self($this->parts); - $new->alias = $alias; - - return $new; - } - - public function isAlias(): bool - { - return null !== $this->alias; - } - - public static function fromStringAsAlias(string $string): self - { - return parent::fromString($string); - } -} diff --git a/lib/ClassMover/Domain/Name/Label.php b/lib/ClassMover/Domain/Name/Label.php deleted file mode 100644 index 7947d74f75..0000000000 --- a/lib/ClassMover/Domain/Name/Label.php +++ /dev/null @@ -1,20 +0,0 @@ -label; - } - - public static function fromString(string $label): static - { - return new static($label); - } -} diff --git a/lib/ClassMover/Domain/Name/MemberName.php b/lib/ClassMover/Domain/Name/MemberName.php deleted file mode 100644 index 93d97c9e79..0000000000 --- a/lib/ClassMover/Domain/Name/MemberName.php +++ /dev/null @@ -1,18 +0,0 @@ -addImportedName($importedNamespaceName); - } - } - - /** @param ImportedNameReference[] $importedNameRefs */ - public static function fromImportedNameRefs(Namespace_ $namespace, array $importedNameRefs): NameImportTable - { - return new self($namespace, $importedNameRefs); - } - - public function isNameImported(QualifiedName $name): bool - { - foreach ($this->importedNameRefs as $importedNameRef) { - if ($importedNameRef->importedName()?->qualifies($name)) { - return true; - } - } - - return false; - } - - public function getImportedNameRefFor(QualifiedName $name): ?ImportedNameReference - { - foreach ($this->importedNameRefs as $importedNameRef) { - if ($importedNameRef->importedName()?->qualifies($name)) { - return $importedNameRef; - } - } - - throw new RuntimeException(sprintf( - 'Could not find name in import table "%s"', - (string)$name - )); - } - - public function resolveClassName(QualifiedName $name): FullyQualifiedName - { - foreach ($this->importedNameRefs as $importedNameRef) { - if ($importedNameRef->importedName()?->qualifies($name)) { - return $importedNameRef->importedName()->qualify($name); - } - } - - if (str_starts_with($name->__toString(), '\\')) { - return FullyQualifiedName::fromString($name->__toString()); - } - - return $this->namespace->qualify($name); - } - - public function namespace(): Namespace_ - { - return $this->namespace; - } - - public function isAliased(QualifiedName $name): bool - { - foreach ($this->importedNameRefs as $importedNameRef) { - $importedName = $importedNameRef->importedName(); - if ($importedName === null) { - continue; - } - - if ($importedName->qualifies($name)) { - return $importedName->isAlias(); - } - } - - return false; - } - - private function addImportedName(ImportedNameReference $importedNameRef): void - { - $this->importedNameRefs[] = $importedNameRef; - } -} diff --git a/lib/ClassMover/Domain/Name/Namespace_.php b/lib/ClassMover/Domain/Name/Namespace_.php deleted file mode 100644 index d498c90c04..0000000000 --- a/lib/ClassMover/Domain/Name/Namespace_.php +++ /dev/null @@ -1,16 +0,0 @@ -__toString().'\\'.$name->__toString()); - } - - public function isRoot(): bool - { - return count($this->parts) === 0; - } -} diff --git a/lib/ClassMover/Domain/Name/QualifiedName.php b/lib/ClassMover/Domain/Name/QualifiedName.php deleted file mode 100644 index a9abf4e5a5..0000000000 --- a/lib/ClassMover/Domain/Name/QualifiedName.php +++ /dev/null @@ -1,108 +0,0 @@ - $parts - */ - protected function __construct(protected array $parts) - { - if (count($this->parts) > 1) { - $this->fullyQualified = $this->parts[0] === ''; - } - } - - public function __toString(): string - { - return implode('\\', $this->parts); - } - - public function wasFullyQualified(): bool - { - return $this->fullyQualified; - } - - public static function root(): QualifiedName - { - return new static([]); - } - - public function isEqualTo(QualifiedName $name): bool - { - return $name->__toString() == $this->__toString(); - } - - public static function fromString(string $string): static - { - if ($string === '') { - throw new InvalidArgumentException( - 'Name cannot be empty' - ); - } - - /** @var non-empty-array $parts */ - $parts = explode('\\', trim($string)); - - return new static($parts); - } - - public function base(): string - { - return reset($this->parts); - } - - public function parentNamespace(): static - { - $parts = $this->parts; - array_pop($parts); - - return new static($parts); - } - - public function equals(QualifiedName $qualifiedName): bool - { - return $qualifiedName->__toString() == $this->__toString(); - } - - public function head(): string - { - return end($this->parts); - } - - public function transpose(QualifiedName $name): self - { - // both fully qualified names? great, nothing to see here. - if ($this instanceof FullyQualifiedName && $name instanceof FullyQualifiedName) { - return $name; - } - - // pretty sure there are some holes in this logic.. - $newParts = []; - $replaceParts = $name->parts(); - - for ($index = 0; $index < count($this->parts); ++$index) { - $newParts[] = array_pop($replaceParts); - } - - return new self(array_reverse(array_filter($newParts))); - } - - /** - * @return string[] - */ - public function parts(): array - { - return $this->parts; - } - - public function isAlone(): bool - { - return count($this->parts) === 1; - } -} diff --git a/lib/ClassMover/Domain/Reference/ClassReference.php b/lib/ClassMover/Domain/Reference/ClassReference.php deleted file mode 100644 index 78d70e70bc..0000000000 --- a/lib/ClassMover/Domain/Reference/ClassReference.php +++ /dev/null @@ -1,84 +0,0 @@ -fullName; - } - - public static function fromNameAndPosition( - QualifiedName $referencedName, - FullyQualifiedName $fullName, - Position $position, - ImportedNameReference $importedNameRef, - bool $isClassDeclaration = false, - bool $hasAlias = false, - bool $isImport = false - ): self { - $new = new self(); - $new->position = $position; - $new->name = $referencedName; - $new->fullName = $fullName; - $new->importedNameRef = $importedNameRef; - $new->isClassDeclaration = $isClassDeclaration; - $new->hasAlias = $hasAlias; - $new->isImport = $isImport; - - return $new; - } - - public function position(): Position - { - return $this->position; - } - - public function name(): QualifiedName - { - return $this->name; - } - - public function fullName(): FullyQualifiedName - { - return $this->fullName; - } - - public function importedNameRef(): ImportedNameReference - { - return $this->importedNameRef; - } - - public function isClassDeclaration(): bool - { - return $this->isClassDeclaration; - } - - public function hasAlias(): bool - { - return $this->hasAlias; - } - - public function isImport(): bool - { - return $this->isImport; - } -} diff --git a/lib/ClassMover/Domain/Reference/ImportedNameReference.php b/lib/ClassMover/Domain/Reference/ImportedNameReference.php deleted file mode 100644 index ba87603888..0000000000 --- a/lib/ClassMover/Domain/Reference/ImportedNameReference.php +++ /dev/null @@ -1,49 +0,0 @@ -importedName; - } - - public static function none(): self - { - $new = new self(); - $new->exists = false; - - return $new; - } - - public static function fromImportedNameAndPosition(ImportedName $importedName, Position $position): ImportedNameReference - { - return new self($position, $importedName); - } - - public function exists(): bool - { - return $this->exists; - } - - public function position(): ?Position - { - return $this->position; - } - - public function importedName(): ?ImportedName - { - return $this->importedName; - } -} diff --git a/lib/ClassMover/Domain/Reference/MemberReference.php b/lib/ClassMover/Domain/Reference/MemberReference.php deleted file mode 100644 index a981527ead..0000000000 --- a/lib/ClassMover/Domain/Reference/MemberReference.php +++ /dev/null @@ -1,61 +0,0 @@ -position->start(), - $this->position->end(), - (string) $this->method - ); - } - - public static function fromMemberNameAndPosition(MemberName $method, Position $position): MemberReference - { - return new self($method, $position); - } - - public static function fromMemberNamePositionAndClass(MemberName $method, Position $position, Class_ $class): MemberReference - { - return new self($method, $position, $class); - } - - public function methodName(): MemberName - { - return $this->method; - } - - public function position(): Position - { - return $this->position; - } - - public function hasClass(): bool - { - return null !== $this->class; - } - - public function withClass(Class_ $class): self - { - return new self($this->method, $this->position, $class); - } - - public function class(): ?Class_ - { - return $this->class; - } -} diff --git a/lib/ClassMover/Domain/Reference/MemberReferences.php b/lib/ClassMover/Domain/Reference/MemberReferences.php deleted file mode 100644 index 80742b5dc3..0000000000 --- a/lib/ClassMover/Domain/Reference/MemberReferences.php +++ /dev/null @@ -1,72 +0,0 @@ - - */ -final class MemberReferences implements IteratorAggregate, Countable -{ - /** @var array */ - private array $methodReferences = []; - - /** @param array $methodReferences */ - private function __construct(array $methodReferences) - { - foreach ($methodReferences as $item) { - $this->add($item); - } - } - - /** @param array $methodReferences */ - public static function fromMemberReferences(array $methodReferences): MemberReferences - { - return new self($methodReferences); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->methodReferences); - } - - public function withClasses(): MemberReferences - { - return self::fromMemberReferences(array_filter($this->methodReferences, function (MemberReference $reference) { - return $reference->hasClass(); - })); - } - - public function withoutClasses(): MemberReferences - { - return self::fromMemberReferences(array_filter($this->methodReferences, function (MemberReference $reference) { - return false === $reference->hasClass(); - })); - } - - - public function count(): int - { - return count($this->methodReferences); - } - - public function unique(): self - { - $members = []; - return self::fromMemberReferences(array_filter($this->methodReferences, function (MemberReference $reference) use (&$members) { - $hash = sprintf('%s.%s.%s', $reference->methodName(), $reference->position()->start(), $reference->position()->end()); - $inArray = false === in_array($hash, $members); - $members[] = $hash; - return $inArray; - })); - } - - private function add(MemberReference $item): void - { - $this->methodReferences[] = $item; - } -} diff --git a/lib/ClassMover/Domain/Reference/NamespaceReference.php b/lib/ClassMover/Domain/Reference/NamespaceReference.php deleted file mode 100644 index 42340e7301..0000000000 --- a/lib/ClassMover/Domain/Reference/NamespaceReference.php +++ /dev/null @@ -1,41 +0,0 @@ -namespace; - } - - public static function fromNameAndPosition(Namespace_ $namespace, Position $position): self - { - return new self($namespace, $position); - } - - public static function forRoot(): self - { - /** @var Namespace_ $rootNamespace */ - $rootNamespace = Namespace_::root(); - return new self($rootNamespace, Position::fromStartAndEnd(0, 0)); - } - - public function position(): Position - { - return $this->position; - } - - public function namespace(): Namespace_ - { - return $this->namespace; - } -} diff --git a/lib/ClassMover/Domain/Reference/NamespacedClassReferences.php b/lib/ClassMover/Domain/Reference/NamespacedClassReferences.php deleted file mode 100644 index 0bdc88b093..0000000000 --- a/lib/ClassMover/Domain/Reference/NamespacedClassReferences.php +++ /dev/null @@ -1,71 +0,0 @@ - - */ -final class NamespacedClassReferences implements IteratorAggregate -{ - /** - * @var ClassReference[] - */ - private array $classRefs = []; - - /** - * @param ClassReference[] $classRefs - */ - private function __construct( - private NamespaceReference $namespaceRef, - array $classRefs - ) { - foreach ($classRefs as $classRef) { - $this->add($classRef); - } - } - - /** - * @param ClassReference[] $classRefs - */ - public static function fromNamespaceAndClassRefs(NamespaceReference $namespace, array $classRefs): NamespacedClassReferences - { - return new self($namespace, $classRefs); - } - - public static function empty(): self - { - return new self(NamespaceReference::forRoot(), []); - } - - public function filterForName(FullyQualifiedName $name): NamespacedClassReferences - { - return new self($this->namespaceRef, array_filter($this->classRefs, function (ClassReference $classRef) use ($name) { - return $classRef->fullName()->isEqualTo($name); - })); - } - - public function isEmpty(): bool - { - return $this->classRefs === []; - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->classRefs); - } - - public function namespaceRef(): NamespaceReference - { - return $this->namespaceRef; - } - - private function add(ClassReference $classRef): void - { - $this->classRefs[] = $classRef; - } -} diff --git a/lib/ClassMover/Domain/Reference/Position.php b/lib/ClassMover/Domain/Reference/Position.php deleted file mode 100644 index e1a02ffdeb..0000000000 --- a/lib/ClassMover/Domain/Reference/Position.php +++ /dev/null @@ -1,32 +0,0 @@ -start; - } - - public function end(): int - { - return $this->end; - } - - public function length(): int - { - return $this->end - $this->start; - } -} diff --git a/lib/ClassMover/Domain/SourceCode.php b/lib/ClassMover/Domain/SourceCode.php deleted file mode 100644 index d935bdd375..0000000000 --- a/lib/ClassMover/Domain/SourceCode.php +++ /dev/null @@ -1,133 +0,0 @@ -source; - } - - public static function fromString(string $source): SourceCode - { - return new self($source); - } - - public function addNamespace(FullyQualifiedName $namespace): SourceCode - { - [$phpDeclarationLineNb, $namespaceLineNb] = $this->significantLineNumbers(); - - if (null !== $namespaceLineNb) { - return $this; - } - - if (null !== $phpDeclarationLineNb) { - return $this->insertAfter( - $phpDeclarationLineNb, - "\n" . sprintf('namespace %s;', (string) $namespace) - ); - } - - return new self($this->source); - } - - public function addUseStatement(FullyQualifiedName $classToUse): SourceCode - { - $useStmt = 'use '.$classToUse->__toString().';'; - - $namespaceLineNb = null; - $lastUseLineNb = null; - $phpDeclarationLineNb = null; - - [$phpDeclarationLineNb, $namespaceLineNb, $lastUseLineNb] = $this->significantLineNumbers(); - - if ($lastUseLineNb) { - return $this->insertAfter($lastUseLineNb, $useStmt); - } - - if ($namespaceLineNb) { - return $this->insertAfter($namespaceLineNb, "\n".$useStmt); - } - - if (null !== $phpDeclarationLineNb) { - return $this->insertAfter($phpDeclarationLineNb, "\n".$useStmt); - } - - throw new InvalidArgumentException( - 'Could not find source); - $newLines = []; - foreach ($lines as $index => $line) { - if ($line === $text) { - return $this; - } - - $newLines[] = $line; - if ($index === $lineNb) { - $newLines[] = $text; - } - } - - return $this->replaceSource(implode("\n", $newLines)); - } - - /** @return array{int|null, int|null, int|null} */ - private function significantLineNumbers(): array - { - $lines = explode("\n", $this->source); - $phpDeclarationLineNb = $namespaceLineNb = $lastUseLineNb = null; - - foreach ($lines as $index => $line) { - if (preg_match('{^<\?php}', $line)) { - $phpDeclarationLineNb = $index; - } - - if (preg_match('{^namespace}', $line)) { - $namespaceLineNb = $index; - } - - if (preg_match('{^use}', $line)) { - $lastUseLineNb = $index; - } - } - - return [ $phpDeclarationLineNb, $namespaceLineNb, $lastUseLineNb ]; - } -} diff --git a/lib/ClassMover/Extension/ClassMoverExtension.php b/lib/ClassMover/Extension/ClassMoverExtension.php deleted file mode 100644 index 818e9286fb..0000000000 --- a/lib/ClassMover/Extension/ClassMoverExtension.php +++ /dev/null @@ -1,43 +0,0 @@ -registerClassMover($container); - } - - private function registerClassMover(ContainerBuilder $container): void - { - $container->register(ClassMover::class, function (Container $container) { - return new ClassMover( - $container->expect('class_mover.class_finder', TolerantClassFinder::class), - $container->expect('class_mover.ref_replacer', TolerantClassReplacer::class) - ); - }); - - $container->register('class_mover.class_finder', function (Container $container) { - return new TolerantClassFinder(); - }); - - $container->register('class_mover.ref_replacer', function (Container $container) { - return new TolerantClassReplacer($container->get(Updater::class)); - }); - } -} diff --git a/lib/ClassMover/FoundReferences.php b/lib/ClassMover/FoundReferences.php deleted file mode 100644 index 521e8ef610..0000000000 --- a/lib/ClassMover/FoundReferences.php +++ /dev/null @@ -1,32 +0,0 @@ -source; - } - - public function targetName(): FullyQualifiedName - { - return $this->name; - } - - public function references(): NamespacedClassReferences - { - return $this->references; - } -} diff --git a/lib/ClassMover/Tests/Adapter/AdapterTestCase.php b/lib/ClassMover/Tests/Adapter/AdapterTestCase.php deleted file mode 100644 index 32518c2e60..0000000000 --- a/lib/ClassMover/Tests/Adapter/AdapterTestCase.php +++ /dev/null @@ -1,38 +0,0 @@ -exists($this->workspacePath())) { - $filesystem->remove($this->workspacePath()); - } - - $filesystem->mkdir($this->workspacePath()); - } - - protected function workspacePath(): string - { - return __DIR__ . '/../Assets/workspace'; - } - - protected function loadProject(): void - { - $projectPath = __DIR__ . '/../Assets/project'; - $filesystem = new Filesystem(); - $filesystem->mirror($projectPath, $this->workspacePath()); - chdir($this->workspacePath()); - exec('composer dumpautoload --quiet'); - } - - protected function getProjectAutoloader(): mixed - { - return require(__DIR__ . '/project/vendor/autoload.php'); - } -} diff --git a/lib/ClassMover/Tests/Adapter/TolerantParser/TolerantClassFinderTest.php b/lib/ClassMover/Tests/Adapter/TolerantParser/TolerantClassFinderTest.php deleted file mode 100644 index 2525a11413..0000000000 --- a/lib/ClassMover/Tests/Adapter/TolerantParser/TolerantClassFinderTest.php +++ /dev/null @@ -1,31 +0,0 @@ -build(); - $names = iterator_to_array($tolerantRefFinder->findIn($source)); - - - $this->assertCount(8, $names); - - $this->assertEquals('Acme\\Foobar\\Warble', $names[0]->__toString()); - $this->assertEquals('Acme\\Foobar\\Barfoo', $names[1]->__toString()); - $this->assertEquals('Acme\\Barfoo', $names[2]->__toString()); - $this->assertEquals('Acme\\Hello', $names[3]->__toString()); - $this->assertEquals('Acme\\Foobar\\Warble', $names[4]->__toString()); - $this->assertEquals('Acme\\Demo', $names[5]->__toString()); - $this->assertEquals('Acme\\Foobar\\Barfoo', $names[6]->__toString()); - $this->assertEquals('Acme\\Foobar\\Barfoo', $names[7]->__toString()); - } -} diff --git a/lib/ClassMover/Tests/Adapter/TolerantParser/TolerantClassReplacerTest.php b/lib/ClassMover/Tests/Adapter/TolerantParser/TolerantClassReplacerTest.php deleted file mode 100644 index a8a87a26cd..0000000000 --- a/lib/ClassMover/Tests/Adapter/TolerantParser/TolerantClassReplacerTest.php +++ /dev/null @@ -1,248 +0,0 @@ -build(); - $originalName = FullyQualifiedName::fromString($classFqn); - - $names = $tolerantRefFinder->findIn($source)->filterForName($originalName); - - $updater = new TolerantUpdater(new TwigRenderer()); - - $replacer = new TolerantClassReplacer($updater); - $edits = $replacer->replaceReferences($source, $names, $originalName, FullyQualifiedName::fromString($replaceWithFqn)); - $stripEmptyLines = function (string $source) { - return implode("\n", array_filter(explode("\n", $source), function (string $line) { - return $line !== ''; - })); - }; - self::assertStringContainsString($stripEmptyLines($expectedSource), $stripEmptyLines($edits->apply($source->__toString()))); - } - - /** - * @return Generator> - */ - public static function provideTestFind(): Generator - { - yield 'Change references of moved class' => [ - 'Example1.php', - 'Acme\\Foobar\\Warble', - 'BarBar\\Hello', - <<<'EOT' - [ - 'Example1.php', - 'Acme\\Hello', - 'Acme\\Definee', - <<<'EOT' - [ - 'Example1.php', - 'Acme\\Hello', - 'Acme\\Definee\\Foobar', - <<<'EOT' - namespace Acme\Definee; - - use Acme\Foobar\Warble; - use Acme\Foobar\Barfoo; - use Acme\Barfoo as ZedZed; - - class Foobar - EOT - ]; - yield 'Change namespace of class which has same namespace as current file' => [ - 'Example2.php', - 'Acme\\Barfoo', - 'Acme\\Definee\\Barfoo', - <<<'EOT' - [ - 'Example3.php', - 'Acme\\ClassMover\\RefFinder\\RefFinder\\TolerantRefFinder', - 'Acme\\ClassMover\\Bridge\\Microsoft\\TolerantParser\\TolerantRefFinder', - <<<'EOT' - use Acme\ClassMover\Bridge\Microsoft\TolerantParser\TolerantRefFinder; - EOT - ]; - yield'Change namespace of interface' => [ - 'Example5.php', - 'Acme\ClassMover\Tests\Adapter\TolerantParser\Example5Interface', - 'Acme\ClassMover\Tests\Adapter\TolerantParser\BarBar\FoobarInterface', - <<<'EOT' - [ - 'Example6.php', - 'Acme\ClassMover\Tests\Adapter\TolerantParser\ExampleTrait', - 'Acme\ClassMover\Tests\Adapter\TolerantParser\BarBar\FoobarTrait', - <<<'EOT' - namespace Acme\ClassMover\Tests\Adapter\TolerantParser\BarBar; - EOT - ]; - yield'Change name of class expansion' => [ - 'Example4.php', - 'Acme\\ClassMover\\RefFinder\\RefFinder\\TolerantRefFinder', - 'Acme\\ClassMover\\RefFinder\\RefFinder\\Foobar', - <<<'EOT' - [ - 'Example7.php', - 'Acme\ClassMover\Tests\Adapter\TolerantParser\Example7', - 'Acme\ClassMover\Tests\Adapter\TolerantParser\Example8', - <<<'EOT' - class Example8 - EOT - ]; - yield'Self class with no namespace to a namespace' => [ - 'Example8.php', - 'ClassOne', - 'Phpactor\ClassMover\Example8', - <<<'EOT' - [ - 'Example9.php', - 'Example', - 'Phpactor\ClassMover\Example', - <<<'EOT' - [ - 'Example10.php', - 'Foobar\Example', - 'Phpactor\ClassMover\Example', - <<<'EOT' - [ - 'Example11.php', - 'Foobar\Example', - 'Phpactor\ClassMover\Example', - <<<'EOT' - [ - 'Example12.php', - 'FQN\Class', - 'OtherFQN\ClassTwo', - << [ - 'Enum1.php', - 'Acme\Hello', - 'Acme\Goodbye', - <<createFinder($source); - $members = $finder->findMembers(SourceCode::fromString($source), $classMember); - $this->assertCount($expectedCount, $members->withClasses()); - $this->assertCount($expectedRiskyCount, $members->withoutClasses()); - } - - /** - * @return Generator - */ - public static function provideFindMember(): Generator - { - yield 'It returns zero references when there are no methods at all' => [ - <<<'EOT' - onlyMethods()->withClass('Foobar')->withMember('foobar'), - 0, - ]; - yield'It returns zero references when there are no matching methods' => [ - <<<'EOT' - barfoo(); - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('foobar'), - 0, - ]; - yield'Reference for static call' => [ - <<<'EOT' - onlyMethods()->withClass('Foobar')->withMember('foobar'), - 2 - ]; - yield'Reference for instantiated instance' => [ - <<<'EOT' - foobar(); - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('foobar'), - 1 - ]; - yield'Reference for instantiated instance of wrong class' => [ - <<<'EOT' - foobar(); - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('foobar'), - 0 - ]; - - yield'Instance in method call in class' => [ - <<<'EOT' - giveMe(); - } - } - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Beer')->withMember('giveMe'), - 1 - ]; - yield 'Includes method declarations' => [ - <<<'EOT' - hello($beer); - } - } - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('hello'), - 2 - ]; - yield 'Multiple references with false positives' => [ - <<<'EOT' - foobar(); - $foobar = new Foobar(); - $foobar->foobar(); - - ($foobar->foobar())->foobar(); - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('foobar'), - 2, - 1 - ]; - - yield'From return types' => [ - <<<'EOT' - goobee()->catma(); - - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Goobee')->withMember('catma'), - 1 - ]; - - yield'Reference from parent class' => [ - <<<'EOT' - foobar(); - - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('foobar'), - 2 - ]; - yield 'Reference to overridden method' => [ - <<<'EOT' - onlyMethods()->withClass('Foobar')->withMember('foobar'), - 2 - ]; - yield 'Reference to interface' => [ - <<<'EOT' - foobar(); - - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('foobar'), - 3 - ]; - - yield'Returns all methods if no method specified' => [ - <<<'EOT' - foobar(); - $foobar->bar(); - - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Barfoo'), - 2 - ]; - - yield'Returns all methods if no method specified, ignores unknown or other classes' => [ - <<<'EOT' - barbar(); - $undefined->gatgat(); - $foobar = new Barfoo(); - $foobar->foobar(); - $foobar->bar(); - - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Barfoo'), - 2 - ]; - - yield'Returns all methods for all classes' => [ - <<<'EOT' - foobar(); - $foobar->bar(); - $stdClass = new \stdClass; - $stdClass->foobar(); - - EOT - , - ClassMemberQuery::create(), - 3, - 0 - ]; - - yield'Ignores dynamic calls' => [ - <<<'EOT' - $foobarName(); - - EOT - , - ClassMemberQuery::create(), - 0 - ]; - - yield 'Ignores calls made on non-class types' => [ - <<<'EOT' - foobar(); - - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar'), - 0 - ]; - yield'Ignore non-existing classes' => [ - <<<'EOT' - foobar(); - - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar'), - 0, - 1 - ]; - yield'Collects unknown methods' => [ - <<<'EOT' - foobar(); - - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('foobar'), - 0, - 1 - ]; - yield 'Finds interface methods for implementation' => [ - <<<'EOT' - onlyMethods()->withClass('CCC')->withMember('bbb'), - 2, - 0 - ]; - yield'Checks from perspective of declaring interface' => [ - <<<'EOT' - onlyMethods()->withClass('CCC')->withMember('bbb'), - 3, - 0 - ]; - yield 'Handles traits' => [ - <<<'EOT' - onlyMethods()->withClass('CCC')->withMember('bbb'), - 2, - 0 - ]; - yield'Properties' => [ - <<<'EOT' - foobar; - - - - EOT - , - ClassMemberQuery::create()->onlyProperties()->withClass('AAA')->withMember('foobar'), - 2, - 0 - ]; - yield'Properties with assignments' => [ - <<<'EOT' - foobar; - - - - EOT - , - ClassMemberQuery::create()->onlyProperties()->withClass('AAA')->withMember('foobar'), - 2, - 0 - ]; - yield 'Scoped property access with variable' => [ - <<<'EOT' - onlyProperties()->withClass('AAA')->withMember('foobar'), - 2, - 0 - ]; - yield'Constants' => [ - <<<'EOT' - onlyConstants()->withClass('AAA')->withMember('BBB'), - 2, - 0 - ]; - yield'Constants from self' => [ - <<<'EOT' - onlyConstants()->withClass('AAA')->withMember('BBB'), - 2, - 0 - ]; - yield'Static method with no restrictions' => [ - <<<'EOT' - withClass('AAA')->withMember('BBB'), - 2, - 0 - ]; - yield'All members for all classes' => [ - <<<'EOT' - methodA(); - $foobar->pubA; - Barfoo::A; - - EOT - , - ClassMemberQuery::create(), - 6, - 0 - ]; - } - - #[DataProvider('provideOffset')] - public function testOffset(string $source, ClassMemberQuery $classMember, Closure $assertion): void - { - $finder = $this->createFinder($source); - $methods = $finder->findMembers(SourceCode::fromString($source), $classMember); - $assertion(iterator_to_array($methods)); - } - - /** - * @return Generator - */ - public function provideOffset(): Generator - { - yield 'Start and end from static call' => [ - <<<'EOT' - onlyMethods()->withClass('Foobar')->withMember('foobar'), - function (array $members): void { - $first = reset($members); - $this->assertEquals(15, $first->position()->start()); - $this->assertEquals(21, $first->position()->end()); - } - ]; - yield 'Start and end from instance call' => [ - <<<'EOT' - foobar(); - EOT - , - ClassMemberQuery::create()->onlyMethods()->withClass('Foobar')->withMember('foobar'), - function (array $members): void { - $first = reset($members); - $this->assertEquals(89, $first->position()->start()); - $this->assertEquals(95, $first->position()->end()); - } - ]; - yield 'Start and end from member declaration' => [ - <<<'EOT' - onlyMethods()->withClass('Foobar')->withMember('foobar'), - function (array $members): void { - $first = reset($members); - $this->assertEquals(38, $first->position()->start()); - $this->assertEquals(44, $first->position()->end()); - } - ]; - } -} diff --git a/lib/ClassMover/Tests/Adapter/WorseTolerant/WorseTolerantMemberReplacerTest.php b/lib/ClassMover/Tests/Adapter/WorseTolerant/WorseTolerantMemberReplacerTest.php deleted file mode 100644 index d3038f0e1c..0000000000 --- a/lib/ClassMover/Tests/Adapter/WorseTolerant/WorseTolerantMemberReplacerTest.php +++ /dev/null @@ -1,113 +0,0 @@ -createFinder($source); - $source = SourceCode::fromString($source); - - $references = $finder->findMembers($source, ClassMemberQuery::create()->withClass($classFqn)->withMember($memberName)); - - $replacer = new WorseTolerantMemberReplacer(); - $source = $replacer->replaceMembers($source, $references, $newMemberName); - $this->assertStringContainsString($expectedSource, $source->__toString()); - } - - /** @return Generator> */ - public static function provideTestReplace(): Generator - { - yield 'It returns unmodified if no references' => [ - 'Foobar', 'zzzzz', 'barfoo', - <<<'EOT' - foobar(); - EOT - , <<<'EOT' - foobar(); - EOT - ]; - yield 'It replaces references' => [ - 'Foobar', 'foobar', 'barfoo', - <<<'EOT' - foobar(); - EOT - , <<<'EOT' - $foobar->barfoo(); - EOT - ]; - yield 'It replaces member declarations' => [ - 'Foobar', 'foobar', 'barfoo', - <<<'EOT' - foobar(); - EOT - , <<<'EOT' - class Foobar { function barfoo() {} } - EOT - ]; - yield 'It replaces property declarations' => [ - 'Foobar', 'foobar', 'barfoo', - <<<'EOT' - foobar; - EOT - , <<<'EOT' - class Foobar { protected $barfoo; {} } - EOT - ]; - yield 'It replaces static property declarations' => [ - 'Foobar', 'foobar', 'barfoo', - <<<'EOT' - [ - 'Foobar', 'BARFOO', 'FOO', - <<<'EOT' - addSource($source)->build() - ); - } -} diff --git a/lib/ClassMover/Tests/Assets/project/composer.json b/lib/ClassMover/Tests/Assets/project/composer.json deleted file mode 100644 index f95f13efe4..0000000000 --- a/lib/ClassMover/Tests/Assets/project/composer.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "acme/test", - "require": { - }, - "require-dev": { - }, - "autoload": { - "psr-4": { - "Acme\\": "src/" - } - }, - "autoload-dev": { - } -} diff --git a/lib/ClassMover/Tests/Assets/project/src/Foobar.php b/lib/ClassMover/Tests/Assets/project/src/Foobar.php deleted file mode 100644 index 8ee55460a3..0000000000 --- a/lib/ClassMover/Tests/Assets/project/src/Foobar.php +++ /dev/null @@ -1,7 +0,0 @@ -undefined = true; - - return $new; - } - - public function isDefined(): bool - { - return !$this->undefined; - } - - public function value(): mixed - { - return $this->value; - } -} diff --git a/lib/ClassMover/Tests/Unit/ClassMoverTest.php b/lib/ClassMover/Tests/Unit/ClassMoverTest.php deleted file mode 100644 index a25308eba8..0000000000 --- a/lib/ClassMover/Tests/Unit/ClassMoverTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - */ - private ObjectProphecy $finder; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $replacer; - - private ClassMover $mover; - - public function setUp(): void - { - $this->finder = $this->prophesize(ClassFinder::class); - $this->replacer = $this->prophesize(ClassReplacer::class); - - $this->mover = new ClassMover( - $this->finder->reveal(), - $this->replacer->reveal() - ); - } - - /** - * It should delgate to the finder to find references. - */ - public function testFindReferences(): FoundReferences - { - $source = TextDocumentBuilder::create('build(); - $fullName = 'Something'; - $refList = NamespacedClassReferences::empty(); - - $this->finder->findIn($source)->willReturn($refList); - - - $references = $this->mover->findReferences($source, $fullName); - - $this->assertInstanceOf(FoundReferences::class, $references); - - $this->assertEquals($source, (string) $references->source()); - $this->assertEquals($fullName, (string) $references->targetName()); - $this->assertEquals([], iterator_to_array($references->references())); - - return $references; - } - - /** - * It should replace references. - */ - #[Depends('testFindReferences')] - public function testReplaceReferences(FoundReferences $references): void - { - $newFqn = 'SomethingElse'; - - $this->replacer->replaceReferences( - $references->source(), - $references->references(), - $references->targetName(), - FullyQualifiedName::fromString($newFqn) - )->shouldBeCalled(); - - $this->mover->replaceReferences($references, $newFqn); - } -} diff --git a/lib/ClassMover/Tests/Unit/Domain/Model/ClassMemberQueryTest.php b/lib/ClassMover/Tests/Unit/Domain/Model/ClassMemberQueryTest.php deleted file mode 100644 index 24904728ee..0000000000 --- a/lib/ClassMover/Tests/Unit/Domain/Model/ClassMemberQueryTest.php +++ /dev/null @@ -1,36 +0,0 @@ -onlyConstants(); - $this->assertEquals(ClassMemberQuery::TYPE_CONSTANT, $query->type()); - } - - public function testOnlyMethods(): void - { - $query = ClassMemberQuery::create()->onlyMethods(); - $this->assertEquals(ClassMemberQuery::TYPE_METHOD, $query->type()); - } - - public function testOnlyProperties(): void - { - $query = ClassMemberQuery::create()->onlyProperties(); - $this->assertEquals(ClassMemberQuery::TYPE_PROPERTY, $query->type()); - } - - public function testHasType(): void - { - $query = ClassMemberQuery::create(); - $this->assertFalse($query->hasType()); - - $query = $query->onlyConstants(); - $this->assertTrue($query->hasType()); - } -} diff --git a/lib/ClassMover/Tests/Unit/Domain/Name/ImportedNamespaceNameTest.php b/lib/ClassMover/Tests/Unit/Domain/Name/ImportedNamespaceNameTest.php deleted file mode 100644 index cf8c0b36be..0000000000 --- a/lib/ClassMover/Tests/Unit/Domain/Name/ImportedNamespaceNameTest.php +++ /dev/null @@ -1,34 +0,0 @@ -withAlias('BarBar'); - $this->assertEquals('Foobar\\Barfoo\\FooFoo', $imported->__toString()); - } - - #[TestDox('It allows single part namespace.')] - public function testSinglePart(): void - { - $imported = ImportedName::fromString('Foobar'); - $this->assertEquals('Foobar', $imported->__toString()); - } - - #[TestDox('It does not allow empty namespace.')] - public function testEmpty(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Name cannot be empty'); - ImportedName::fromString(''); - } -} diff --git a/lib/ClassMover/Tests/Unit/Domain/Name/MemberNameTest.php b/lib/ClassMover/Tests/Unit/Domain/Name/MemberNameTest.php deleted file mode 100644 index 4ed00106ef..0000000000 --- a/lib/ClassMover/Tests/Unit/Domain/Name/MemberNameTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertEquals('foobar', (string) $name); - } - - public function testCompareDollars(): void - { - $name = MemberName::fromString('$foobar'); - $this->assertTrue($name->matches('foobar')); - $this->assertTrue($name->matches('$foobar')); - - $name = MemberName::fromString('foobar'); - $this->assertTrue($name->matches('$foobar')); - - $name = MemberName::fromString('foobar'); - $this->assertTrue($name->matches('foobar')); - } -} diff --git a/lib/ClassMover/Tests/Unit/Domain/Name/NamespacedClassRefListTest.php b/lib/ClassMover/Tests/Unit/Domain/Name/NamespacedClassRefListTest.php deleted file mode 100644 index 4208f70878..0000000000 --- a/lib/ClassMover/Tests/Unit/Domain/Name/NamespacedClassRefListTest.php +++ /dev/null @@ -1,50 +0,0 @@ -assertCount(2, $refList->filterForName( - FullyQualifiedName::fromString('Foo\\Bar') - )); - } -} diff --git a/lib/ClassMover/Tests/Unit/Domain/Name/QualifiedNameTest.php b/lib/ClassMover/Tests/Unit/Domain/Name/QualifiedNameTest.php deleted file mode 100644 index eb8c53805a..0000000000 --- a/lib/ClassMover/Tests/Unit/Domain/Name/QualifiedNameTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertFalse($name->isEqualTo($notMatching)); - $this->assertTrue($name->isEqualTo($matching)); - } -} diff --git a/lib/ClassMover/Tests/Unit/Domain/SourceCodeTest.php b/lib/ClassMover/Tests/Unit/Domain/SourceCodeTest.php deleted file mode 100644 index 42fcb1a03e..0000000000 --- a/lib/ClassMover/Tests/Unit/Domain/SourceCodeTest.php +++ /dev/null @@ -1,140 +0,0 @@ -addUseStatement(FullyQualifiedName::fromString('Foobar')); - $this->assertEquals($expected, $source->__toString()); - } - - /** @return Generator */ - public static function provideAddUse(): Generator - { - yield 'No namespace' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - addNamespace(FullyQualifiedName::fromString('NS1')); - $this->assertEquals($expected, $source->__toString()); - } - - /** - * @return Generator - */ - public static function provideNamespaceAdd(): Generator - { - yield 'Add namespace' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - class - EOT - , - <<<'EOT' - class - EOT - ]; - } -} diff --git a/lib/ClassMover/Tests/Unit/Extension/ClassMoverExtensionTest.php b/lib/ClassMover/Tests/Unit/Extension/ClassMoverExtensionTest.php deleted file mode 100644 index 8f195a7287..0000000000 --- a/lib/ClassMover/Tests/Unit/Extension/ClassMoverExtensionTest.php +++ /dev/null @@ -1,33 +0,0 @@ - realpath(__DIR__ .'/..'), - CodeTransformExtension::PARAM_TEMPLATE_PATHS => [], - ]); - $var = $container->get(ClassMover::class); - self::assertInstanceOf(ClassMover::class, $var); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Edits.php b/lib/CodeBuilder/Adapter/TolerantParser/Edits.php deleted file mode 100644 index c4ed63acb6..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Edits.php +++ /dev/null @@ -1,81 +0,0 @@ - - */ - private array $edits = []; - - public function __construct(private TextFormat $format = new TextFormat()) - { - } - - /** - * @param Node|Token $node - */ - public function remove($node): void - { - $this->edits[] = TextEdit::create($node->getFullStartPosition(), $node->getFullWidth(), ''); - } - - /** - * @param Node|Token $node - */ - public function before($node, string $text): void - { - $this->edits[] = TextEdit::create($node->getStartPosition(), 0, $text); - } - - /** - * @param Node|Token $node - */ - public function after($node, string $text): void - { - $this->edits[] = TextEdit::create($node->getEndPosition(), 0, $text); - } - - /** - * @param Node|Token|QualifiedName $node - */ - public function replace($node, string $text): void - { - $this->edits[] = TextEdit::create($node->getFullStartPosition(), $node->getFullWidth(), $text); - } - - public function replaceMultiple( - Node|Token|QualifiedName $firstNodeToReplace, - Node|Token|QualifiedName $lastToReplace, - string $text, - ): void { - $this->edits[] = TextEdit::create( - $firstNodeToReplace->getStartPosition(), - $lastToReplace->getEndPosition() - $firstNodeToReplace->getStartPosition(), - $text - ); - } - - public function textEdits(): TextEdits - { - return TextEdits::fromTextEdits($this->edits); - } - - public function add(TextEdit $textEdit): void - { - $this->edits[] = $textEdit; - } - - public function indent(string $string, int $level): string - { - return $this->format->indent($string, $level); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php deleted file mode 100644 index 22c2a5f614..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php +++ /dev/null @@ -1,156 +0,0 @@ -classUpdater = new ClassUpdater($renderer); - $this->interfaceUpdater = new InterfaceUpdater($renderer); - $this->traitUpdater = new TraitUpdater($renderer); - $this->enumUpdater = new EnumUpdater($renderer); - $this->useStatementUpdater = new UseStatementUpdater(); - } - - public function textEditsFor(Prototype $prototype, TextDocument $code): TextEdits - { - $edits = new Edits($this->textFormat); - $node = $this->parser->get($code); - - $this->updateNamespace($edits, $prototype, $node); - $this->useStatementUpdater->updateUseStatements($edits, $prototype, $node); - $this->updateClasses($edits, $prototype, $node); - return $edits->textEdits(); - } - - private function updateNamespace(Edits $edits, SourceCode $prototype, SourceFileNode $node): void - { - $namespaceNode = $node->getFirstChildNode(NamespaceDefinition::class); - - if (null !== $namespaceNode && NamespaceName::root() == $prototype->namespace()) { - return; - } - - /** @var $namespaceNode NamespaceDefinition */ - if ($namespaceNode && $namespaceNode->name->getText() == (string) $prototype->namespace()) { - return; - } - - if (((string) $prototype->namespace()) === '') { - return; - } - - if ($namespaceNode) { - $edits->replace($namespaceNode, 'namespace ' . (string) $prototype->namespace() . ';'); - return; - } - - $startTag = $node->getFirstChildNode(InlineHtml::class); - $edits->after($startTag, 'namespace ' . (string) $prototype->namespace() . ';' . "\n"."\n"); - } - - private function updateClasses(Edits $edits, SourceCode $prototype, SourceFileNode $node): void - { - $classNodes = []; - $traitNodes = []; - $interfaceNodes = []; - $enumNodes = []; - $lastStatement = null; - - foreach ($node->statementList as $classNode) { - $lastStatement = $classNode; - - if ($classNode instanceof ClassDeclaration) { - $name = $classNode->name->getText($node->getFileContents()); - $classNodes[$name] = $classNode; - } - - if ($classNode instanceof InterfaceDeclaration) { - $name = $classNode->name->getText($node->getFileContents()); - $interfaceNodes[$name] = $classNode; - } - - if ($classNode instanceof TraitDeclaration) { - $name = $classNode->name->getText($node->getFileContents()); - $traitNodes[$name] = $classNode; - } - - if ($classNode instanceof EnumDeclaration) { - $name = $classNode->name->getText($node->getFileContents()); - $enumNodes[$name] = $classNode; - } - } - - foreach ($prototype->classes()->in(array_keys($classNodes)) as $classPrototype) { - $this->classUpdater->updateClass($edits, $classPrototype, $classNodes[$classPrototype->name()]); - } - - foreach ($prototype->interfaces()->in(array_keys($interfaceNodes)) as $classPrototype) { - $this->interfaceUpdater->updateInterface($edits, $classPrototype, $interfaceNodes[$classPrototype->name()]); - } - - foreach ($prototype->traits()->in(array_keys($traitNodes)) as $traitPrototype) { - $this->traitUpdater->updateTrait($edits, $traitPrototype, $traitNodes[$traitPrototype->name()]); - } - - foreach ($prototype->enums()->in(array_keys($enumNodes)) as $enumPrototype) { - $this->enumUpdater->updateEnum($edits, $enumPrototype, $enumNodes[$enumPrototype->name()]); - } - - $classes = array_merge( - iterator_to_array($prototype->classes()->notIn(array_keys($classNodes))), - iterator_to_array($prototype->interfaces()->notIn(array_keys($interfaceNodes))), - iterator_to_array($prototype->traits()->notIn(array_keys($traitNodes))), - iterator_to_array($prototype->enums()->notIn(array_keys($enumNodes))) - ); - - $index = 0; - foreach ($classes as $classPrototype) { - if (substr($lastStatement->getText(), -1) !== "\n") { - $edits->after($lastStatement, "\n"); - } - - if ($index > 0 && $index + 1 == count($classes)) { - $edits->after($lastStatement, "\n"); - } - $edits->after($lastStatement, "\n" . $this->renderer->render($classPrototype)); - $index++; - } - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php deleted file mode 100644 index bdd177a400..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php +++ /dev/null @@ -1,319 +0,0 @@ -methods()) === 0) { - return; - } - - $lastMember = $this->memberDeclarationsNode($classNode)->openBrace; - $lastNonMethodMember = null; - $methodHasBeenEncountered = false; - $newLine = false; - $existingMethodNames = []; - $existingMethods = []; - foreach ($this->memberDeclarations($classNode) as $memberNode) { - if ($memberNode instanceof PropertyDeclaration || $memberNode instanceof EnumCaseDeclaration) { - $lastMember = $memberNode; - $newLine = true; - if (!$methodHasBeenEncountered) { - $lastNonMethodMember = $memberNode; - } - } - - if ($memberNode instanceof MethodDeclaration) { - $lastMember = $memberNode; - $existingMethodNames[] = $memberNode->getName(); - $existingMethods[$memberNode->getName()] = $memberNode; - $newLine = true; - $methodHasBeenEncountered = true; - } - } - - // Update methods - $methodPrototypes = $classPrototype->methods()->in($existingMethodNames); - - $ignoreMethods = []; - foreach ($methodPrototypes as $methodPrototype) { - /** @var MethodDeclaration $methodDeclaration */ - $methodDeclaration = $existingMethods[$methodPrototype->name()]; - - if ($methodPrototype->docblock()->notNone()) { - $this->updateDocblock($edits, $methodPrototype, $methodDeclaration); - } - - $lines = $methodPrototype->body()->lines(); - if ($lines->count() > 0) { - $bodyNode = $methodDeclaration->compoundStatementOrSemicolon; - $this->appendLinesToMethod($edits, $methodPrototype, $bodyNode); - } - - if (false === $methodPrototype->applyUpdate() || $this->prototypeSameAsDeclaration($methodPrototype, $methodDeclaration)) { - $ignoreMethods[] = $methodPrototype->name(); - continue; - } - - /** @phpstan-ignore-next-line */ - if ($methodPrototype->applyUpdate()) { - $this->updateOrAddParameters($edits, $methodPrototype->parameters(), $methodDeclaration); - $this->updateOrAddReturnType($edits, $methodPrototype->returnType(), $methodDeclaration); - } - } - - // Add methods - $methodPrototypes = $classPrototype->methods()->notIn($existingMethodNames)->notIn($ignoreMethods); - - if (0 === count($methodPrototypes)) { - return; - } - - // Don't add new line if it's only inserting the constructor - if (1 === count($methodPrototypes) && $methodPrototypes->has('__construct')) { - $newLine = false; - } - - if ($newLine) { - $edits->after($lastMember, "\n"); - } - - foreach ($methodPrototypes as $methodPrototype) { - // If class has methods add the constructor before it. - if ($methodPrototype->name() === '__construct') { - if ($lastNonMethodMember === null) { - $edits->after( - $this->memberDeclarationsNode($classNode)->openBrace, - "\n".$edits->indent($this->renderMethod($this->renderer, $methodPrototype), 1)."\n" - ); - } else { - $edits->after( - $lastNonMethodMember, - "\n"."\n".$edits->indent($this->renderMethod($this->renderer, $methodPrototype), 1) - ); - } - continue; - } - - $edits->after( - $lastMember, - "\n" . $edits->indent($this->renderMethod($this->renderer, $methodPrototype), 1) - ); - - if (false === $classPrototype->methods()->isLast($methodPrototype)) { - $edits->after($lastMember, "\n"); - } - } - } - - /** - * @return array - */ - abstract protected function memberDeclarations(ClassLike $classNode): array; - - /** @return TMembersNodeType */ - abstract protected function memberDeclarationsNode(ClassLike $classNode); - - abstract protected function renderMethod(Renderer $renderer, Method $method): string; - - private function appendLinesToMethod(Edits $edits, Method $method, Node $bodyNode): void - { - if (false === $bodyNode instanceof CompoundStatementNode) { - return; - } - - $lastStatement = end($bodyNode->statements) ?: $bodyNode->openBrace; - - foreach ($method->body()->lines() as $line) { - // do not add duplicate lines - $bodyNodeLines = explode("\n", $bodyNode->getText()); - - foreach ($bodyNodeLines as $bodyNodeLine) { - if (trim($bodyNodeLine) == trim((string) $line)) { - continue 2; - } - } - - $edits->after( - $lastStatement, - "\n" . $edits->indent((string) $line, 2) - ); - } - } - - private function updateOrAddParameters(Edits $edits, Parameters $parameters, MethodDeclaration $methodDeclaration): void - { - if (0 === $parameters->count()) { - return; - } - - $renderedParameters = []; - - /** @var ParameterDeclarationList|null $existingParameterDeclaration */ - $existingParameterDeclaration = $methodDeclaration->parameters; - - // Copying over existing parameters - if ($existingParameterDeclaration) { - /** @var array $existingParameters */ - $existingParameters = iterator_to_array($existingParameterDeclaration->getElements()); - - // This is an array [variableName => 'rendered parameter node as string'] - $renderedParameters = (array)array_combine( - array_map(function (Parameter $parameter) { - $variableName = $parameter->variableName ? - $parameter->variableName->getText($parameter->getFileContents()): - false; - return substr((string) $variableName, 1); - }, $existingParameters), - array_map(fn (Parameter $parameter) => $parameter->getText(), $existingParameters) - ); - } - - // Adding new parameters to the mix - foreach ($parameters as $parameter) { - assert($parameter instanceof PhpactorParameter); - if (!isset($renderedParameters[$parameter->name()])) { - $renderedParameters[$parameter->name()] = $this->renderer->render($parameter); - } - } - - $startPosition = $methodDeclaration->openParen->getStartPosition(); - $edits->add(TextEdit::create( - $startPosition + 1, - $methodDeclaration->closeParen->getStartPosition() - $startPosition - 1, - implode(', ', $renderedParameters) - )); - } - - private function updateOrAddReturnType(Edits $edits, ReturnType $returnType, MethodDeclaration $methodDeclaration): void - { - if (false === $returnType->notNone()) { - return; - } - - $returnType = trim((string) $this->renderer->render($returnType->type())); - if ($returnType === '') { - return; - } - - // Add the new return type - if ($methodDeclaration->returnTypeList === null) { - $edits->after($methodDeclaration->closeParen, ': ' . $returnType); - return; - } - - $firstReturnType = QualifiedNameListUtil::firstQualifiedNameOrNullOrToken($methodDeclaration->returnTypeList); - if (null === $firstReturnType) { - return; - } - - $existingReturnType = $returnType ? NodeHelper::resolvedShortName($methodDeclaration, $firstReturnType) : null; - if (null === $existingReturnType) { - // TODO: Add return type - return; - } - - if ($returnType === $existingReturnType) { - return; - } - - $startToken = $methodDeclaration->questionToken ?? $firstReturnType; - - $edits->replaceMultiple($startToken, $methodDeclaration->returnTypeList, $returnType); - } - - private function prototypeSameAsDeclaration(Method $methodPrototype, MethodDeclaration $methodDeclaration): bool - { - $parameters = []; - if (null !== $methodDeclaration->parameters) { - $parameters = array_filter($methodDeclaration->parameters->children, function ($parameter) { - return $parameter instanceof Parameter; - }); - - /** @var Parameter $parameter */ - foreach ($parameters as $parameter) { - $name = ltrim((string)$parameter->variableName->getText($methodDeclaration->getFileContents()), '$'); - - // if method prototype doesn't have the existing parameter - if (false === $methodPrototype->parameters()->has($name)) { - return false; - } - - $parameterPrototype = $methodPrototype->parameters()->get($name); - - $type = (string)$this->renderer->render($parameterPrototype->type()); - - // adding a parameter type - if (null === $parameter->typeDeclarationList && $type) { - return false; - } - - // if parameter has a different type - if (null !== $parameter->typeDeclarationList) { - $typeName = $parameter->typeDeclarationList->getText($methodDeclaration->getFileContents()); - if ($type && (string) $type !== $typeName) { - return false; - } - } - } - } - - // method prototype has all of the parameters, but does it have extra ones? - if ($methodPrototype->parameters()->count() !== count($parameters)) { - return false; - } - - // are we adding a return type? - if ($methodPrototype->returnType()->notNone() && null === $methodDeclaration->returnTypeList) { - return false; - } - - // is the return type the same? - if (null !== $methodDeclaration->returnTypeList) { - // TODO: Does this work? - $name = $methodDeclaration->returnTypeList->getText(); - if ($methodPrototype->returnType()->__toString() !== $name) { - return false; - } - } - - return true; - } - - private function updateDocblock(Edits $edits, Method $methodPrototype, MethodDeclaration $methodDeclaration): void - { - $edits->add(TextEdit::create( - $methodDeclaration->getFullStartPosition(), - strlen($methodDeclaration->getLeadingCommentAndWhitespaceText()), - $methodPrototype->docblock()->__toString() - )); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassLikeUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassLikeUpdater.php deleted file mode 100644 index 538322a164..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassLikeUpdater.php +++ /dev/null @@ -1,130 +0,0 @@ -methodUpdater = new ClassMethodUpdater($renderer); - } - - protected function resolvePropertyName(Node|Token $property): ?string - { - if ($property instanceof PropertyElement) { - $property = $property->variable; - } - if ($property instanceof Variable) { - return $property->getName(); - } - - if ($property instanceof AssignmentExpression) { - return $this->resolvePropertyName($property->leftOperand); - } - - throw new InvalidArgumentException(sprintf( - 'Do not know how to resolve property element of type "%s"', - get_debug_type($property) - )); - } - - /** @return array */ - abstract protected function memberDeclarations(Node $node): array; - - protected function updateProperties(Edits $edits, ClassLikePrototype $classPrototype, Node $classMembers): void - { - if (count($classPrototype->properties()) === 0) { - return; - } - - $memberDeclarations = $this->memberDeclarations($classMembers); - $lastProperty = $this->getInsertPlace($classMembers, $memberDeclarations); - - $nextMember = null; - $existingPropertyNames = []; - - foreach ($memberDeclarations as $memberNode) { - if (null === $nextMember) { - $nextMember = $memberNode; - } - - if ($memberNode instanceof PropertyDeclaration) { - foreach ($memberNode->propertyElements->getElements() as $property) { - $existingPropertyNames[] = $this->resolvePropertyName($property); - } - $lastProperty = $memberNode; - $nextMember = next($memberDeclarations) ?: $nextMember; - prev($memberDeclarations); - } - } - - foreach ($classPrototype->properties()->notIn($existingPropertyNames) as $property) { - // if property type exists then the last property has a docblock - add a line break - if ($lastProperty instanceof PropertyDeclaration && $property->type() != Type::none()) { - $edits->after($lastProperty, "\n"); - } - - $edits->after( - $lastProperty, - "\n" . $edits->indent($this->renderer->render($property), 1) - ); - - if ($classPrototype->properties()->isLast($property) && $nextMember instanceof MethodDeclaration) { - $edits->after($lastProperty, "\n"); - } - } - } - - /** - * @param Node[] $memberDeclarations - */ - protected function getInsertPlace(Node $classNode, array $memberDeclarations): Token - { - $insert = $classNode->openBrace; - foreach ($memberDeclarations as $member) { - if ($member instanceof ClassConstDeclaration) { - $insert = $member->semicolon; - } else { - break; - } - } - - return $insert; - } - - /** - * @param ClassDeclaration|TraitDeclaration|EnumDeclaration|InterfaceDeclaration $classLikeDeclaration - */ - protected function updateDocblock(Edits $edits, ClassLikePrototype $classPrototype, $classLikeDeclaration): void - { - if (!$classPrototype->docblock()->notNone()) { - return; - } - $edits->add(TextEdit::create( - $classLikeDeclaration->getFullStartPosition(), - strlen($classLikeDeclaration->getLeadingCommentAndWhitespaceText()), - $classPrototype->docblock()->__toString() - )); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassMethodUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassMethodUpdater.php deleted file mode 100644 index 25b6235c33..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassMethodUpdater.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ -class ClassMethodUpdater extends AbstractMethodUpdater -{ - /** - * @return ClassMembersNode|TraitMembers|EnumMembers - */ - public function memberDeclarationsNode(ClassLike $classNode) - { - if ($classNode instanceof ClassDeclaration) { - return $classNode->classMembers; - } - if ($classNode instanceof TraitDeclaration) { - return $classNode->traitMembers; - } - if ($classNode instanceof EnumDeclaration) { - return $classNode->enumMembers; - } - - throw new RuntimeException(sprintf( - 'Can not get member declarations for "%s"', - get_class($classNode) - )); - } - - public function renderMethod(Renderer $renderer, Method $method): string - { - return $renderer->render($method) . - "\n" . - $renderer->render($method->body()); - } - - /** @return array */ - protected function memberDeclarations(ClassLike $classNode): array - { - if ($classNode instanceof ClassDeclaration) { - return $classNode->classMembers->classMemberDeclarations; - } - if ($classNode instanceof TraitDeclaration) { - return $classNode->traitMembers->traitMemberDeclarations; - } - if ($classNode instanceof EnumDeclaration) { - return $classNode->enumMembers->enumMemberDeclarations; - } - - throw new RuntimeException(sprintf( - 'Can not get member declarations for "%s"', - get_class($classNode) - )); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php deleted file mode 100644 index a24784eec4..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php +++ /dev/null @@ -1,124 +0,0 @@ -applyUpdate()) { - return; - } - - $this->updateDocblock($edits, $classPrototype, $classNode); - $this->updateExtends($edits, $classPrototype, $classNode); - $this->updateImplements($edits, $classPrototype, $classNode); - $this->updateConstants($edits, $classPrototype, $classNode->classMembers); - $this->updateProperties($edits, $classPrototype, $classNode->classMembers); - - $this->methodUpdater->updateMethods($edits, $classPrototype, $classNode); - } - - protected function updateConstants(Edits $edits, ClassPrototype $classPrototype, Node $classMembers): void - { - if (count($classPrototype->constants()) === 0) { - return; - } - - $lastConstant = $classMembers->openBrace; - $memberDeclarations = $classMembers->classMemberDeclarations; - - $nextMember = null; - $existingConstantNames = []; - - foreach ($memberDeclarations as $memberNode) { - if (null === $nextMember) { - $nextMember = $memberNode; - } - - if ($memberNode instanceof ClassConstDeclaration) { - foreach ($memberNode->constElements->getElements() as $variable) { - $existingConstantNames[] = $variable->getName(); - } - $lastConstant = $memberNode; - $nextMember = next($memberDeclarations) ?: $nextMember; - prev($memberDeclarations); - } - } - - foreach ($classPrototype->constants()->notIn($existingConstantNames) as $constant) { - assert($constant instanceof Constant); - - $edits->after( - $lastConstant, - "\n" . $edits->indent($this->renderer->render($constant), 1) - ); - - if ($classPrototype->constants()->isLast($constant) && ( - $nextMember instanceof MethodDeclaration || - $nextMember instanceof PropertyDeclaration - )) { - $edits->after($lastConstant, "\n"); - } - } - } - - protected function memberDeclarations(Node $node): array - { - return $node->classMemberDeclarations; - } - - private function updateExtends(Edits $edits, ClassPrototype $classPrototype, ClassDeclaration $classNode): void - { - if (ExtendsClass::none() == $classPrototype->extendsClass()) { - return; - } - - if (null === $classNode->classBaseClause) { - $edits->after($classNode->name, ' extends ' . (string) $classPrototype->extendsClass()); - return; - } - - - $edits->replace($classNode->classBaseClause, ' extends ' . (string) $classPrototype->extendsClass()); - } - - private function updateImplements(Edits $edits, ClassPrototype $classPrototype, ClassDeclaration $classNode): void - { - if (ImplementsInterfaces::empty() == $classPrototype->implementsInterfaces()) { - return; - } - - if (null === $classNode->classInterfaceClause) { - $edits->after($classNode->name, ' implements ' . (string) $classPrototype->implementsInterfaces()->__toString()); - return; - } - - $existingNames = []; - foreach ($classNode->classInterfaceClause->interfaceNameList->getElements() as $name) { - $existingNames[] = $name->getText(); - } - - $additionalNames = $classPrototype->implementsInterfaces()->notIn($existingNames); - assert($additionalNames instanceof ImplementsInterfaces); - - if (0 === count($additionalNames)) { - return; - } - - $names = join(', ', [ implode(', ', $existingNames), $additionalNames->__toString()]); - - $edits->replace($classNode->classInterfaceClause, ' implements ' . $names); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/EnumUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/EnumUpdater.php deleted file mode 100644 index 57c0886706..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/EnumUpdater.php +++ /dev/null @@ -1,73 +0,0 @@ -methodUpdater = new ClassMethodUpdater($renderer); - } - - public function updateCases( - Edits $edits, - EnumPrototype $classPrototype, - EnumMembers $enumMembers - ): void { - if (count($classPrototype->cases()) === 0) { - return; - } - - $lastConstant = $enumMembers->openBrace; - - $nextMember = null; - $existingCasesNames = []; - $memberDeclarations = $enumMembers->enumMemberDeclarations; - - foreach ($memberDeclarations as $memberNode) { - if (null === $nextMember) { - $nextMember = $memberNode; - } - - if ($memberNode instanceof EnumCaseDeclaration) { - $existingCasesNames[] = $memberNode->name->getText(); - $lastConstant = $memberNode; - $nextMember = next($memberDeclarations) ?: $nextMember; - prev($memberDeclarations); - } - } - - foreach ($classPrototype->cases()->notIn($existingCasesNames) as $case) { - $edits->after( - $lastConstant, - "\n" . $edits->indent($this->renderer->render($case), 1) - ); - - if ($classPrototype->cases()->isLast($case) && ( - $nextMember instanceof MethodDeclaration || - $nextMember instanceof EnumCaseDeclaration - )) { - $edits->after($lastConstant, "\n"); - } - } - } - - public function updateEnum( - Edits $edits, - EnumPrototype $classPrototype, - EnumDeclaration $classNode - ): void { - $this->updateCases($edits, $classPrototype, $classNode->enumMembers); - $this->methodUpdater->updateMethods($edits, $classPrototype, $classNode); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceMethodUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceMethodUpdater.php deleted file mode 100644 index c13e4988ac..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceMethodUpdater.php +++ /dev/null @@ -1,34 +0,0 @@ - - */ -class InterfaceMethodUpdater extends AbstractMethodUpdater -{ - /** - * @return InterfaceMembers - */ - public function memberDeclarationsNode(ClassLike $classNode) - { - return $classNode->interfaceMembers; - } - - public function renderMethod(Renderer $renderer, Method $method): string - { - return $renderer->render($method) . ';'; - } - - /** @return array */ - protected function memberDeclarations(ClassLike $classNode): array - { - return $classNode->interfaceMembers->interfaceMemberDeclarations; - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceUpdater.php deleted file mode 100644 index 545eeb303f..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceUpdater.php +++ /dev/null @@ -1,26 +0,0 @@ -methodUpdater = new InterfaceMethodUpdater($renderer); - } - - public function updateInterface( - Edits $edits, - InterfacePrototype $classPrototype, - InterfaceDeclaration $classNode - ): void { - $this->methodUpdater->updateMethods($edits, $classPrototype, $classNode); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/TraitUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/TraitUpdater.php deleted file mode 100644 index 563917db2f..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/TraitUpdater.php +++ /dev/null @@ -1,28 +0,0 @@ -applyUpdate()) { - return; - } - - $this->updateProperties($edits, $classPrototype, $classNode->traitMembers); - - $this->methodUpdater->updateMethods($edits, $classPrototype, $classNode); - } - - /** @return array */ - protected function memberDeclarations(Node $node): array - { - return $node->traitMemberDeclarations; - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Updater/UseStatementUpdater.php b/lib/CodeBuilder/Adapter/TolerantParser/Updater/UseStatementUpdater.php deleted file mode 100644 index efc0e8e58f..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Updater/UseStatementUpdater.php +++ /dev/null @@ -1,199 +0,0 @@ -useStatements())) { - return; - } - - $startNode = $node; - foreach ($node->getChildNodes() as $childNode) { - if ($childNode instanceof InlineHtml) { - $startNode = $node->getFirstChildNode(InlineHtml::class); - } - if ($childNode instanceof DeclareStatement) { - $startNode = $childNode; - } - if ($childNode instanceof NamespaceDefinition) { - $startNode = $childNode; - } - if ($childNode instanceof NamespaceUseDeclaration) { - $startNode = $childNode; - } - } - - $bodyNode = null; - foreach ($node->getChildNodes() as $childNode) { - if ($childNode->getStartPosition() > $startNode->getStartPosition()) { - $bodyNode = $childNode; - break; - } - } - - $usePrototypes = $this->resolveUseStatements($prototype, $startNode); - - if ($usePrototypes === []) { - return; - } - - // When adding after the namespace definition the text is added before the new line - // And when adding after the php declaration the text is added after the new line - // Examples: - // $startNode->getText(); // Returns: namespace Test;\n - // $edits->after($startNode, 'TOTO'); // Result: namespace Test;TOTO\n - // $startNode->getText(); // Returns: after($startNode, 'TOTO'); // Result: after($startNode, "\n"); - } - - foreach ($usePrototypes as $usePrototype) { - $prototypeOrder = $usePrototype->type() === UseStatement::TYPE_FUNCTION ? '1' : '0'; - $editText = $this->buildEditText($usePrototype); - - foreach ($node->getChildNodes() as $childNode) { - if ($childNode instanceof NamespaceUseDeclaration) { - /** @phpstan-ignore-next-line */ - if (!$childNode->useClauses) { - continue; - } - foreach ($childNode->useClauses->getElements() as $useClause) { - assert($useClause instanceof NamespaceUseClause); - - $nodeOrder = $childNode->functionOrConst !== null ? '1' : '0'; - // try to find the first lexicographycally greater use - // statement and insert before if there is one - $cmp = strcmp( - $nodeOrder.$useClause->namespaceName->getText(), - $prototypeOrder.$usePrototype->__toString() - ); - if ($cmp === 0) { - continue 3; - } - if ($cmp > 0) { - // Add before one of the use import and add a new - // line so the new import is on its own line - $edits->before($childNode, $editText . "\n"); - continue 3; - } - } - } - } - - // Either add after the NamespaceUseDeclaration node if there - // already was use imports or after the namespace/php declaration - // Since it will add before the lasts new line of the node we - // preprend with another one so that the use statement is on its - // own line - $newUseStatement = "\n" . $editText; - $edits->after($startNode, $newUseStatement); - } - - if ($startNode instanceof InlineHtml) { - // Add a new line after the last use statement so that it's on its - // own line - $edits->after($startNode, "\n"); - } - - // Add another new line to separate the new use declaration from - // the code that follow - if ( - !$startNode instanceof NamespaceUseDeclaration && - $bodyNode && NodeHelper::emptyLinesPrecedingNode($bodyNode) === 0 - ) { - $edits->after($startNode, "\n"); - } - } - - /** - * @return UseStatement[] - */ - private function resolveUseStatements(SourceCode $prototype, Node $lastNode): array - { - $usePrototypes = $this->filterExisting($lastNode, $prototype); - $usePrototypes = $this->filterSameNamespace($lastNode, $usePrototypes); - - return $usePrototypes; - } - - /** - * @return list - */ - private function filterExisting(Node $lastNode, SourceCode $prototype): array - { - $existingNames = new ImportedNames($lastNode); - $usePrototypes = $prototype->useStatements()->sorted(); - - $usePrototypes = array_filter(iterator_to_array($usePrototypes), function (UseStatement $usePrototype) use ($existingNames) { - $existing = $usePrototype->type() === UseStatement::TYPE_FUNCTION ? - $existingNames->functionNames() : - $existingNames->classNames(); - - $candidate = $usePrototype->hasAlias() ? $usePrototype->alias() : $usePrototype->name()->__toString(); - - // when we are dealing with aliases, they are stored in the array - // keys... - $existing = $usePrototype->hasAlias() ? array_keys($existing) : array_values($existing); - - return false === in_array( - $candidate, - $existing, - true - ); - }); - - return $usePrototypes; - } - /** - * @param array $usePrototypes - * @return list - */ - private function filterSameNamespace(Node $lastNode, array $usePrototypes): array - { - $sourceNamespace = null; - if ($nsDef = $lastNode->getNamespaceDefinition()) { - if ($nsDef->name instanceof QualifiedName) { - $sourceNamespace = $nsDef->name->__toString(); - } - } - - $usePrototypes = array_filter($usePrototypes, function (UseStatement $usePrototype) use ($sourceNamespace) { - return $sourceNamespace !== $usePrototype->name()->namespace(); - }); - return $usePrototypes; - } - - private function buildEditText(UseStatement $usePrototype): string - { - $editText = [ - 'use ' - ]; - if ($usePrototype->type() === UseStatement::TYPE_FUNCTION) { - $editText[] = 'function '; - } - $editText[] = (string) $usePrototype . ';'; - $editText = implode('', $editText); - return $editText; - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php b/lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php deleted file mode 100644 index 6a6a544249..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php +++ /dev/null @@ -1,63 +0,0 @@ -buildTable($node); - } - - - public function getIterator(): Traversable - { - return new ArrayIterator($this->classNamesFromNode()); - } - - public function classNames(): array - { - return array_values($this->classNamesFromNode()); - } - - public function functionNames(): array - { - $names = []; - foreach ($this->table[1] as $shortName => $resolvedName) { - $names[$shortName] = (string) $resolvedName; - } - - return $names; - } - - private function classNamesFromNode(): array - { - $names = []; - foreach ($this->table[0] as $shortName => $resolvedName) { - $names[(string) $resolvedName] = (string) $resolvedName; - } - - return $names; - } - - private function buildTable(Node $node): void - { - if ('SourceFileNode' == $node->getNodeKindName()) { - $this->table = [ - [], - [], - [] - ]; - return; - } - - $this->table = $node->getImportTablesForCurrentScope(); - } -} diff --git a/lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php b/lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php deleted file mode 100644 index 3084320a5c..0000000000 --- a/lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php +++ /dev/null @@ -1,72 +0,0 @@ -getText($node->getFileContents()); - } - - $resolvedName = $type->getResolvedName(); - - if (is_string($resolvedName)) { - return $resolvedName; - } - - $parts = $resolvedName->getNameParts(); - - if (count($parts) === 0) { - return ''; - } - - $part = ''; - - if (count($parts) == 1) { - $part = reset($parts); - } - - if (count($parts) > 1) { - $part = array_pop($parts); - } - - if ($part instanceof Token) { - return $part->getText($type->getFileContents()); - } - - return $part; - } - - public static function emptyLinesPrecedingNode(Node $node): int - { - $contents = $node->getFileContents(); - $preceding = substr($contents, 0, $node->getStartPosition()); - - $lines = 0; - $lastChar = null; - for ($i = $node->getStartPosition() - 1; $i > 0; $i--) { - $char = $contents[$i]; - - if ($char !== "\n") { - break; - } - - $lines++; - } - - return $lines - 1; - } -} diff --git a/lib/CodeBuilder/Adapter/Twig/ClassShortNameResolver.php b/lib/CodeBuilder/Adapter/Twig/ClassShortNameResolver.php deleted file mode 100644 index 7d43fde986..0000000000 --- a/lib/CodeBuilder/Adapter/Twig/ClassShortNameResolver.php +++ /dev/null @@ -1,13 +0,0 @@ -originalType(); - if ($originalType instanceof PhpactorType) { - return $this->typeRenderer->render($originalType); - } - return $type->__toString(); - }), - ]; - } - - - public function indent(string $string, int $level = 0): string - { - return $this->textFormat->indent($string, $level); - } -} diff --git a/lib/CodeBuilder/Adapter/Twig/TwigRenderer.php b/lib/CodeBuilder/Adapter/Twig/TwigRenderer.php deleted file mode 100644 index e67a83ed0b..0000000000 --- a/lib/CodeBuilder/Adapter/Twig/TwigRenderer.php +++ /dev/null @@ -1,66 +0,0 @@ -twig = $twig ?: $this->createTwig(); - } - - public function render(Prototype $prototype, ?string $variant = null): TextDocument - { - $templateName = $baseTemplateName = $this->templateNameResolver->resolveName($prototype); - - if ($variant) { - $templateName = $variant . '/' . $templateName; - } - - try { - $code = $this->twigRender($prototype, $templateName, $variant); - } catch (LoaderError $error) { - if (null === $variant) { - throw $error; - } - - $code = $this->twigRender($prototype, $baseTemplateName, $variant); - } - - return TextDocumentBuilder::fromString(rtrim($code)); - } - - private function createTwig(): Environment - { - $twig = new Environment(new FilesystemLoader(__DIR__ . '/../../../../templates/code'), [ - 'strict_variables' => true, - 'autoescape' => false, - ]); - $twig->addExtension(new TwigExtension(new TextFormat(), new WorseTypeRenderer82())); - - return $twig; - } - - private function twigRender(Prototype $prototype, string $templateName, ?string $variant = null): string - { - return $this->twig->render($templateName, [ - 'prototype' => $prototype, - 'generator' => $this, - 'variant' => $variant, - ]); - } -} diff --git a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer.php b/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer.php deleted file mode 100644 index 7a06655beb..0000000000 --- a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer.php +++ /dev/null @@ -1,10 +0,0 @@ -render($type->type); - } - - if ($type instanceof ObjectType) { - return $type->toPhpString(); - } - - if ($type instanceof AggregateType) { - return null; - } - - if ($type instanceof ArrayType) { - return $type->toPhpString(); - } - - if ($type instanceof BooleanType) { - return 'bool'; - } - - if ($type instanceof ScalarType) { - return $type->toPhpString(); - } - - if ($type instanceof GenericClassType) { - return $type->name()->short(); - } - - if ($type instanceof ClassType) { - return $type->short(); - } - - if ($type instanceof SelfType) { - return $type->__toString(); - } - - if ($type instanceof VoidType) { - return $type->__toString(); - } - - if ($type instanceof InvokeableType) { - return $type->toPhpString(); - } - - if ($type instanceof PseudoIterableType) { - return $type->toPhpString(); - } - - return parent::render($type); - } -} diff --git a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer80.php b/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer80.php deleted file mode 100644 index 547fe1650a..0000000000 --- a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer80.php +++ /dev/null @@ -1,22 +0,0 @@ - $this->render($t), $type->types))); - } - if ($type instanceof MixedType) { - return $type->toPhpString(); - } - - return parent::render($type); - } -} diff --git a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer81.php b/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer81.php deleted file mode 100644 index 3627ed017c..0000000000 --- a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer81.php +++ /dev/null @@ -1,28 +0,0 @@ - $this->render($t), $type->types))); - } - - if ($type instanceof StaticType) { - return $type->toPhpString(); - } - - if ($type instanceof NeverType) { - return $type->toPhpString(); - } - - return parent::render($type); - } -} diff --git a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer82.php b/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer82.php deleted file mode 100644 index f82370334d..0000000000 --- a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer82.php +++ /dev/null @@ -1,23 +0,0 @@ -toPhpString(); - } - - if ($type instanceof FalseType) { - return $type->toPhpString(); - } - - return parent::render($type); - } -} diff --git a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRendererFactory.php b/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRendererFactory.php deleted file mode 100644 index c17f1d6160..0000000000 --- a/lib/CodeBuilder/Adapter/WorseReflection/TypeRenderer/WorseTypeRendererFactory.php +++ /dev/null @@ -1,24 +0,0 @@ - $versionToRendererMap - */ - public function __construct(private array $versionToRendererMap) - { - } - - public function rendererFor(string $phpVersion): WorseTypeRenderer - { - foreach ($this->versionToRendererMap as $version => $renderer) { - if (str_starts_with($phpVersion, $version)) { - return $renderer; - } - } - - return new WorseTypeRenderer74(); - } -} diff --git a/lib/CodeBuilder/Adapter/WorseReflection/WorseBuilderFactory.php b/lib/CodeBuilder/Adapter/WorseReflection/WorseBuilderFactory.php deleted file mode 100644 index dd5068a443..0000000000 --- a/lib/CodeBuilder/Adapter/WorseReflection/WorseBuilderFactory.php +++ /dev/null @@ -1,167 +0,0 @@ -language('php') - ->build(); - } - - $classes = $this->reflector->reflectClassesIn($source); - $builder = SourceCodeBuilder::create(); - - foreach ($classes as $classLike) { - if ($classLike instanceof ReflectionClass) { - $this->build('class', $builder, $classLike); - continue; - } - - if ($classLike instanceof ReflectionInterface) { - $this->build('interface', $builder, $classLike); - continue; - } - - if ($classLike instanceof ReflectionTrait) { - $this->build('trait', $builder, $classLike); - continue; - } - - if ($classLike instanceof ReflectionEnum) { - $this->build('enum', $builder, $classLike); - } - } - - $builder->snapshot(); - - return $builder; - } - - private function build(string $type, SourceCodeBuilder $builder, ReflectionClassLike $reflectionClass): void - { - $classBuilder = $builder->$type($reflectionClass->name()->short()); - $builder->namespace($reflectionClass->name()->namespace()); - - if ($reflectionClass instanceof ReflectionClass || $reflectionClass instanceof ReflectionTrait) { - foreach ($reflectionClass->properties()->belongingTo($reflectionClass->name()) as $property) { - assert($property instanceof ReflectionProperty); - if ($property->isPromoted()) { - continue; - } - $this->buildProperty($classBuilder, $property); - } - } - - foreach ($reflectionClass->methods()->real()->belongingTo($reflectionClass->name()) as $method) { - $this->buildMethod($classBuilder, $method); - } - } - - private function buildProperty(ClassLikeBuilder $classBuilder, ReflectionProperty $property): void - { - assert($classBuilder instanceof ClassBuilder || $classBuilder instanceof TraitBuilder); - - $propertyBuilder = $classBuilder->property($property->name()); - $propertyBuilder->visibility((string) $property->visibility()); - - $type = $property->inferredType(); - if ($type->isDefined()) { - $this->importClassesForMemberType($classBuilder, $property->class()->name(), $type); - $propertyBuilder->type($type->short(), $type); - $propertyBuilder->docType((string)$type); - } - } - - private function buildMethod(ClassLikeBuilder $classBuilder, ReflectionMethod $method): void - { - $methodBuilder = $classBuilder->method($method->name()); - $methodBuilder->visibility((string) $method->visibility()); - - if ($method->returnType()->isDefined()) { - $type = $method->returnType(); - $this->importClassesForMemberType($classBuilder, $method->class()->name(), $type); - $typeName = $type->short(); - $methodBuilder->returnType($typeName, $type); - } - - if ($method->isStatic()) { - $methodBuilder->static(); - } - - foreach ($method->parameters() as $parameter) { - $this->buildParameter($methodBuilder, $method, $parameter); - } - } - - private function buildParameter(MethodBuilder $methodBuilder, ReflectionMethod $method, ReflectionParameter $parameter): void - { - $parameterBuilder = $methodBuilder->parameter($parameter->name()); - - if ($parameter->type()->isDefined()) { - $type = $parameter->type(); - $imports = $parameter->scope()->nameImports(); - - $this->importClassesForMemberType($methodBuilder->end(), $method->class()->name(), $type); - - if ($parameter->isVariadic()) { - if ($type instanceof ArrayType) { - $type = $type->iterableValueType(); - } - } - $type = $method->scope()->resolveLocalType($type); - $parameterBuilder->type($type->short(), $type); - } - - if ($parameter->isVariadic()) { - $parameterBuilder->asVariadic(); - } - - if ($parameter->default()->isDefined()) { - $parameterBuilder->defaultValue($parameter->default()->value()); - } - - if ($parameter->byReference()) { - $parameterBuilder->byReference(true); - } - } - - private function importClassesForMemberType(ClassLikeBuilder $classBuilder, ClassName $classType, Type $type): void - { - foreach ($type->allTypes()->classLike() as $types) { - if ($classType->namespace() == $types->name()->namespace()) { - return; - } - - $classBuilder->end()->use($types->name()->full()); - } - } -} diff --git a/lib/CodeBuilder/Domain/Builder/AbstractBuilder.php b/lib/CodeBuilder/Domain/Builder/AbstractBuilder.php deleted file mode 100644 index b8133bd6fb..0000000000 --- a/lib/CodeBuilder/Domain/Builder/AbstractBuilder.php +++ /dev/null @@ -1,74 +0,0 @@ - $property) { - if ($propertyName == 'originalProperties') { - continue; - } - $propertyValues[$propertyName] = is_object($this->$propertyName) ? clone $this->$propertyName : $this->$propertyName; - } - - $this->originalProperties = $propertyValues; - - foreach ($this->children() as $child) { - $child->snapshot(); - } - } - - public function isModified(): bool - { - if (empty($this->originalProperties)) { - return true; - } - - foreach ($this->originalProperties as $propertyName => $propertyValue) { - if ($this->$propertyName != $propertyValue) { - return true; - } - } - - foreach ($this->children() as $child) { - if ($child->isModified()) { - return true; - } - } - - return false; - } - - /** - * @return Generator - */ - public function children(): Generator - { - foreach (static::childNames() as $childName) { - $children = (array) $this->$childName; - - foreach ($children as $child) { - if (!$child instanceof Builder) { - throw new RuntimeException(sprintf( - 'Child "%s" is not a builder instance, it is a "%s"', - $childName, - get_debug_type($child), - )); - } - - yield $child; - } - } - } -} diff --git a/lib/CodeBuilder/Domain/Builder/Builder.php b/lib/CodeBuilder/Domain/Builder/Builder.php deleted file mode 100644 index 60c5cff872..0000000000 --- a/lib/CodeBuilder/Domain/Builder/Builder.php +++ /dev/null @@ -1,13 +0,0 @@ - - */ - public static function childNames(): array; -} diff --git a/lib/CodeBuilder/Domain/Builder/CaseBuilder.php b/lib/CodeBuilder/Domain/Builder/CaseBuilder.php deleted file mode 100644 index 63277e657d..0000000000 --- a/lib/CodeBuilder/Domain/Builder/CaseBuilder.php +++ /dev/null @@ -1,42 +0,0 @@ -name = $name; - } - - public function end(): EnumBuilder - { - return $this->enumBuilder; - } - - public static function childNames(): array - { - return ['name']; - } - - public function builderName(): string - { - return $this->name; - } - - public function build(): Case_ - { - return new Case_($this->name, null, UpdatePolicy::fromModifiedState($this->isModified())); - } -} diff --git a/lib/CodeBuilder/Domain/Builder/ClassBuilder.php b/lib/CodeBuilder/Domain/Builder/ClassBuilder.php deleted file mode 100644 index 73c722bd9b..0000000000 --- a/lib/CodeBuilder/Domain/Builder/ClassBuilder.php +++ /dev/null @@ -1,102 +0,0 @@ -extends = ExtendsClass::fromString($class); - - return $this; - } - - public function add(Builder $builder): void - { - if ($builder instanceof PropertyBuilder) { - $this->properties[$builder->builderName()] = $builder; - return; - } - - parent::add($builder); - } - - public function implements(string $interface): ClassBuilder - { - $this->interfaces[] = Type::fromString($interface); - - return $this; - } - - public function property(string $name): PropertyBuilder - { - if (isset($this->properties[$name])) { - return $this->properties[$name]; - } - - $this->properties[$name] = $builder = new PropertyBuilder($this, $name); - - return $builder; - } - - public function constant(string $name, $value): ConstantBuilder - { - $this->constants[] = $builder = new ConstantBuilder($this, $name, $value); - - return $builder; - } - - public function build(): ClassPrototype - { - return new ClassPrototype( - $this->name, - Properties::fromProperties(array_map(function (PropertyBuilder $builder) { - return $builder->build(); - }, $this->properties)), - Constants::fromConstants(array_map(function (ConstantBuilder $builder) { - return $builder->build(); - }, $this->constants)), - Methods::fromMethods(array_map(function (MethodBuilder $builder) { - return $builder->build(); - }, $this->methods)), - $this->extends, - ImplementsInterfaces::fromTypes($this->interfaces), - UpdatePolicy::fromModifiedState($this->isModified()), - $this->docblock - ); - } -} diff --git a/lib/CodeBuilder/Domain/Builder/ClassLikeBuilder.php b/lib/CodeBuilder/Domain/Builder/ClassLikeBuilder.php deleted file mode 100644 index 53b63b4e17..0000000000 --- a/lib/CodeBuilder/Domain/Builder/ClassLikeBuilder.php +++ /dev/null @@ -1,69 +0,0 @@ -docblock = null; - } - - public static function childNames(): array - { - return [ - 'methods', - ]; - } - - public function add(Builder $builder): void - { - if ($builder instanceof MethodBuilder) { - $this->methods[$builder->builderName()] = $builder; - return; - } - - throw new InvalidBuilderException($this, $builder); - } - - public function method(string $name): MethodBuilder - { - if (isset($this->methods[$name])) { - return $this->methods[$name]; - } - - $this->methods[$name] = $builder = new MethodBuilder($this, $name); - - return $builder; - } - - public function end(): SourceCodeBuilder - { - return $this->parent; - } - - public function docblock(string $docblock): ClassLikeBuilder - { - $this->docblock = Docblock::fromString($docblock); - - return $this; - } - - public function getDocblock(): ?Docblock - { - return $this->docblock; - } - -} diff --git a/lib/CodeBuilder/Domain/Builder/ConstantBuilder.php b/lib/CodeBuilder/Domain/Builder/ConstantBuilder.php deleted file mode 100644 index bdb43ba9b2..0000000000 --- a/lib/CodeBuilder/Domain/Builder/ConstantBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -value = Value::fromValue($value); - } - - public static function childNames(): array - { - return []; - } - - public function visibility(string $visibility): ConstantBuilder - { - $this->visibility = Visibility::fromString($visibility); - - return $this; - } - - public function build(): Constant - { - return new Constant( - $this->name, - $this->value, - $this->visibility, - UpdatePolicy::fromModifiedState($this->isModified()), - ); - } - - public function end(): ClassLikeBuilder - { - return $this->parent; - } - - public function builderName(): string - { - return $this->name; - } -} diff --git a/lib/CodeBuilder/Domain/Builder/EnumBuilder.php b/lib/CodeBuilder/Domain/Builder/EnumBuilder.php deleted file mode 100644 index 3f3970ab6a..0000000000 --- a/lib/CodeBuilder/Domain/Builder/EnumBuilder.php +++ /dev/null @@ -1,43 +0,0 @@ -cases[$name])) { - $this->cases[$name] = new CaseBuilder($this, $name); - } - - return $this->cases[$name]; - } - - public function build(): EnumPrototype - { - $updatePolicy = UpdatePolicy::fromModifiedState($this->isModified()); - return new EnumPrototype( - $this->name, - Cases::fromCases(array_map(function (CaseBuilder $case) { return $case->build(); }, $this->cases)), - Methods::fromMethods(array_map(function (MethodBuilder $builder) { return $builder->build(); }, $this->methods)), - $updatePolicy - ); - } -} diff --git a/lib/CodeBuilder/Domain/Builder/Exception/InvalidBuilderException.php b/lib/CodeBuilder/Domain/Builder/Exception/InvalidBuilderException.php deleted file mode 100644 index 9a976d4007..0000000000 --- a/lib/CodeBuilder/Domain/Builder/Exception/InvalidBuilderException.php +++ /dev/null @@ -1,18 +0,0 @@ -extends[] = Type::fromString($class); - - return $this; - } - - public function build(): InterfacePrototype - { - return new InterfacePrototype( - $this->name, - Methods::fromMethods(array_map(function (MethodBuilder $builder) { - return $builder->build(); - }, $this->methods)), - ExtendsInterfaces::fromTypes($this->extends), - UpdatePolicy::fromModifiedState($this->isModified()) - ); - } -} diff --git a/lib/CodeBuilder/Domain/Builder/MethodBodyBuilder.php b/lib/CodeBuilder/Domain/Builder/MethodBodyBuilder.php deleted file mode 100644 index e7096b6f90..0000000000 --- a/lib/CodeBuilder/Domain/Builder/MethodBodyBuilder.php +++ /dev/null @@ -1,35 +0,0 @@ -lines[] = Line::fromString($text); - - return $this; - } - - public function build(): MethodBody - { - return MethodBody::fromLines($this->lines); - } - - public function end(): MethodBuilder - { - return $this->parent; - } -} diff --git a/lib/CodeBuilder/Domain/Builder/MethodBuilder.php b/lib/CodeBuilder/Domain/Builder/MethodBuilder.php deleted file mode 100644 index 5dfe11ddbb..0000000000 --- a/lib/CodeBuilder/Domain/Builder/MethodBuilder.php +++ /dev/null @@ -1,169 +0,0 @@ -bodyBuilder = new MethodBodyBuilder($this); - } - - public static function childNames(): array - { - return [ - 'parameters', - ]; - } - - public function add(NamedBuilder $builder): void - { - if ($builder instanceof ParameterBuilder) { - $this->parameters[$builder->builderName()] = $builder; - } - - throw new InvalidBuilderException($this, $builder); - } - - public function visibility(string $visibility): MethodBuilder - { - $this->visibility = Visibility::fromString($visibility); - - return $this; - } - - /** - * @param list $arguments - */ - public function attribute(string $name, array $arguments): MethodBuilder - { - $this->attributes[] = new Attribute($name, array_map(function (mixed $argument) { - return Value::fromValue($argument); - }, $arguments)); - return $this; - } - - /** - * @param mixed $originalType - */ - public function returnType(string $returnType, $originalType = null): MethodBuilder - { - $this->returnType = new ReturnType(new Type($returnType, $originalType)); - - return $this; - } - - public function parameter(string $name): ParameterBuilder - { - if (isset($this->parameters[$name])) { - return $this->parameters[$name]; - } - - $this->parameters[$name] = $builder = new ParameterBuilder($this, $name); - - return $builder; - } - - public function docblock(string $docblock): MethodBuilder - { - $this->docblock = Docblock::fromString($docblock); - - return $this; - } - - public function getDocblock(): ?Docblock - { - return $this->docblock; - } - - public function build(): Method - { - $modifiers = 0; - - if ($this->static) { - $modifiers = $modifiers|Method::IS_STATIC; - } - - if ($this->abstract) { - $modifiers = $modifiers|Method::IS_ABSTRACT; - } - - $methodBody = $this->bodyBuilder->build(); - - return new Method( - $this->name, - $this->visibility ?? Visibility::public(), - Parameters::fromParameters(array_map(function (ParameterBuilder $builder) { - return $builder->build(); - }, $this->parameters)), - $this->returnType, - $this->docblock, - $modifiers, - $methodBody, - UpdatePolicy::fromModifiedState($this->isModified()), - $this->attributes - ); - } - - public function static(): MethodBuilder - { - $this->static = true; - return $this; - } - - public function abstract(): MethodBuilder - { - $this->abstract = true; - return $this; - } - - public function end(): ClassLikeBuilder - { - return $this->parent; - } - - public function body(): MethodBodyBuilder - { - return $this->bodyBuilder; - } - - public function builderName(): string - { - return $this->name; - } -} diff --git a/lib/CodeBuilder/Domain/Builder/NamedBuilder.php b/lib/CodeBuilder/Domain/Builder/NamedBuilder.php deleted file mode 100644 index 50ec7b1d3a..0000000000 --- a/lib/CodeBuilder/Domain/Builder/NamedBuilder.php +++ /dev/null @@ -1,8 +0,0 @@ -type = new Type($type, $originalType); - - return $this; - } - - public function visibility(?Visibility $visibility): ParameterBuilder - { - $methodName = $this->parent->builderName(); - if ($methodName !== '__construct') { - throw new Exception('Only constructors can have parameters with visibility. Current function: '.$methodName); - } - $this->visibility = $visibility; - - return $this; - } - - public function defaultValue($value): ParameterBuilder - { - $this->defaultValue = DefaultValue::fromValue($value); - - return $this; - } - - public function build(): Parameter - { - return new Parameter( - $this->name, - $this->type, - $this->defaultValue, - $this->byReference, - UpdatePolicy::fromModifiedState($this->isModified()), - $this->variadic, - $this->visibility - ); - } - - public function end(): MethodBuilder - { - return $this->parent; - } - - public function byReference(bool $bool): self - { - $this->byReference = $bool; - - return $this; - } - - public function asVariadic(): self - { - $this->variadic = true; - - return $this; - } -} diff --git a/lib/CodeBuilder/Domain/Builder/PropertyBuilder.php b/lib/CodeBuilder/Domain/Builder/PropertyBuilder.php deleted file mode 100644 index bc3934404c..0000000000 --- a/lib/CodeBuilder/Domain/Builder/PropertyBuilder.php +++ /dev/null @@ -1,87 +0,0 @@ -visibility = Visibility::fromString($visibility); - - return $this; - } - - /** - * @param mixed $originalType - */ - public function type(string $type, $originalType = null): PropertyBuilder - { - $this->type = new Type($type, $originalType); - - return $this; - } - - public function docType(string $type): PropertyBuilder - { - $this->docType = Type::fromString($type); - - return $this; - } - - /** - * @param mixed $value - */ - public function defaultValue($value): PropertyBuilder - { - $this->defaultValue = DefaultValue::fromValue($value); - - return $this; - } - - public function build(): Property - { - return new Property( - $this->name, - $this->visibility, - $this->defaultValue, - $this->type, - $this->docType, - UpdatePolicy::fromModifiedState($this->isModified()) - ); - } - - public function end(): ClassLikeBuilder - { - return $this->parent; - } - - public function builderName(): string - { - return $this->name; - } -} diff --git a/lib/CodeBuilder/Domain/Builder/SourceCodeBuilder.php b/lib/CodeBuilder/Domain/Builder/SourceCodeBuilder.php deleted file mode 100644 index 9a42e6b0a7..0000000000 --- a/lib/CodeBuilder/Domain/Builder/SourceCodeBuilder.php +++ /dev/null @@ -1,169 +0,0 @@ -namespace = NamespaceName::fromString($namespace); - - return $this; - } - - public function use(string $use, ?string $alias = null): SourceCodeBuilder - { - $this->useStatements[$use] = UseStatement::fromNameAndAlias($use, $alias); - - return $this; - } - - public function useFunction(string $name, ?string $alias = null): SourceCodeBuilder - { - $this->useStatements[$name] = UseStatement::fromNameAliasAndType($name, $alias, UseStatement::TYPE_FUNCTION); - - return $this; - } - - public function class(string $name): ClassBuilder - { - if (isset($this->classes[$name])) { - return $this->classes[$name]; - } - - $this->classes[$name] = $builder = new ClassBuilder($this, $name); - - return $builder; - } - - public function classLike(string $name): ClassLikeBuilder - { - if (isset($this->classes[$name])) { - return $this->classes[$name]; - } - - if (isset($this->interfaces[$name])) { - return $this->interfaces[$name]; - } - - if (isset($this->traits[$name])) { - return $this->traits[$name]; - } - - if (isset($this->enums[$name])) { - return $this->enums[$name]; - } - - throw new InvalidArgumentException( - 'classLike can only be called as an accessor. Use class() or interface() instead' - ); - } - - public function interface(string $name): InterfaceBuilder - { - if (isset($this->interfaces[$name])) { - return $this->interfaces[$name]; - } - - $this->interfaces[$name] = $builder = new InterfaceBuilder($this, $name); - - return $builder; - } - - public function trait(string $name): TraitBuilder - { - if (isset($this->traits[$name])) { - return $this->traits[$name]; - } - - $this->traits[$name] = $builder = new TraitBuilder($this, $name); - - return $builder; - } - - public function enum(string $name): EnumBuilder - { - if (isset($this->enums[$name])) { - return $this->enums[$name]; - } - - $this->enums[$name] = $builder = new EnumBuilder($this, $name); - - return $builder; - } - - public function build(): SourceCode - { - return new SourceCode( - $this->namespace, - UseStatements::fromUseStatements($this->useStatements), - Classes::fromClasses(array_map(function (ClassBuilder $builder) { - return $builder->build(); - }, $this->classes)), - Interfaces::fromInterfaces(array_map(function (InterfaceBuilder $builder) { - return $builder->build(); - }, $this->interfaces)), - Traits::fromTraits(array_map(function (TraitBuilder $builder) { - return $builder->build(); - }, $this->traits)), - Enums::fromEnums(array_map(function (EnumBuilder $builder) { - return $builder->build(); - }, $this->enums)), - UpdatePolicy::fromModifiedState($this->isModified()) - ); - } -} diff --git a/lib/CodeBuilder/Domain/Builder/TraitBuilder.php b/lib/CodeBuilder/Domain/Builder/TraitBuilder.php deleted file mode 100644 index 253090b6c8..0000000000 --- a/lib/CodeBuilder/Domain/Builder/TraitBuilder.php +++ /dev/null @@ -1,75 +0,0 @@ -properties[$builder->builderName()] = $builder; - return; - } - - parent::add($builder); - } - - public function property(string $name): PropertyBuilder - { - if (isset($this->properties[$name])) { - return $this->properties[$name]; - } - - $this->properties[$name] = $builder = new PropertyBuilder($this, $name); - - return $builder; - } - - public function constant(string $name, $value): ConstantBuilder - { - $this->constants[] = $builder = new ConstantBuilder($this, $name, $value); - - return $builder; - } - - public function build(): TraitPrototype - { - return new TraitPrototype( - $this->name, - Properties::fromProperties(array_map(function (PropertyBuilder $builder) { - return $builder->build(); - }, $this->properties)), - Constants::fromConstants(array_map(function (ConstantBuilder $builder) { - return $builder->build(); - }, $this->constants)), - Methods::fromMethods(array_map(function (MethodBuilder $builder) { - return $builder->build(); - }, $this->methods)), - UpdatePolicy::fromModifiedState($this->isModified()) - ); - } -} diff --git a/lib/CodeBuilder/Domain/BuilderFactory.php b/lib/CodeBuilder/Domain/BuilderFactory.php deleted file mode 100644 index 2f972674f8..0000000000 --- a/lib/CodeBuilder/Domain/BuilderFactory.php +++ /dev/null @@ -1,11 +0,0 @@ - $arguments - */ - public function __construct( - public string $name, - public array $arguments - ) { - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Case_.php b/lib/CodeBuilder/Domain/Prototype/Case_.php deleted file mode 100644 index 857bd49891..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Case_.php +++ /dev/null @@ -1,26 +0,0 @@ -name; - } - - public function value(): ?Value - { - return $this->value; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Cases.php b/lib/CodeBuilder/Domain/Prototype/Cases.php deleted file mode 100644 index 173d768466..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Cases.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -class Cases extends Collection -{ - /** - * @param Case_[] $cases - */ - public static function fromCases(array $cases): self - { - return new self(array_reduce($cases, function ($acc, $case) { - $acc[$case->name()] = $case; - return $acc; - }, [])); - } - - protected function singularName(): string - { - return 'case'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/ClassLikePrototype.php b/lib/CodeBuilder/Domain/Prototype/ClassLikePrototype.php deleted file mode 100644 index 70f02fd2c5..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/ClassLikePrototype.php +++ /dev/null @@ -1,54 +0,0 @@ -methods = $methods ?: Methods::empty(); - $this->properties = $properties ?: Properties::empty(); - $this->constants = $constants ?: Constants::empty(); - $this->docblock = $docblock ?: Docblock::none(); - } - - public function name(): string - { - return $this->name; - } - - public function methods(): Methods - { - return $this->methods; - } - - public function properties(): Properties - { - return $this->properties; - } - - public function constants(): Constants - { - return $this->constants; - } - - public function docblock(): Docblock - { - return $this->docblock; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/ClassPrototype.php b/lib/CodeBuilder/Domain/Prototype/ClassPrototype.php deleted file mode 100644 index 8f2b3ce54f..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/ClassPrototype.php +++ /dev/null @@ -1,35 +0,0 @@ -extendsClass = $extendsClass ?: ExtendsClass::none(); - $this->implementsInterfaces = $implementsInterfaces ?: ImplementsInterfaces::empty(); - } - - public function extendsClass(): ExtendsClass - { - return $this->extendsClass; - } - - public function implementsInterfaces(): ImplementsInterfaces - { - return $this->implementsInterfaces; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Classes.php b/lib/CodeBuilder/Domain/Prototype/Classes.php deleted file mode 100644 index b26e1d0ef6..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Classes.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -class Classes extends Collection -{ - /** - * @param list $classes - */ - public static function fromClasses(array $classes): self - { - return new static(array_reduce($classes, function ($acc, $class) { - $acc[$class->name()] = $class; - return $acc; - }, [])); - } - - protected function singularName(): string - { - return 'class'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Collection.php b/lib/CodeBuilder/Domain/Prototype/Collection.php deleted file mode 100644 index 57f9a6e7ce..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Collection.php +++ /dev/null @@ -1,113 +0,0 @@ - - */ -abstract class Collection implements IteratorAggregate, Countable -{ - /** - * @param T[] $items - */ - protected function __construct(protected array $items) - { - } - - /** - * @return static - */ - public static function empty() - { - /** @phpstan-ignore-next-line */ - return new static([]); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } - - /** - * @param T $item - */ - public function isLast($item): bool - { - return end($this->items) === $item; - } - - /** - * Return first - * @return T|null - */ - public function first() - { - $first = reset($this->items); - if (false === $first) { - return null; - } - - return $first; - } - - - public function count(): int - { - return count($this->items); - } - - /** - * @return T - */ - public function get(string $name) - { - if (!isset($this->items[$name])) { - throw new InvalidArgumentException(sprintf( - 'Unknown %s "%s", known items: "%s"', - $this->singularName(), - $name, - implode('", "', array_keys($this->items)) - )); - } - - return $this->items[$name]; - } - - public function has(string $name): bool - { - if (isset($this->items[$name])) { - return true; - } - - return false; - } - - /** - * @return static - */ - public function notIn(array $names): Collection - { - return new static(array_filter($this->items, function ($name) use ($names) { - return false === in_array($name, $names); - }, ARRAY_FILTER_USE_KEY)); - } - - /** - * @return static - */ - public function in(array $names): Collection - { - return new static(array_filter($this->items, function ($name) use ($names) { - return true === in_array($name, $names); - }, ARRAY_FILTER_USE_KEY)); - } - - abstract protected function singularName(): string; -} diff --git a/lib/CodeBuilder/Domain/Prototype/Constant.php b/lib/CodeBuilder/Domain/Prototype/Constant.php deleted file mode 100644 index 866a679af7..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Constant.php +++ /dev/null @@ -1,30 +0,0 @@ -name; - } - - public function value(): Value - { - return $this->value; - } - - public function visibility(): ?Visibility - { - return $this->visibility; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Constants.php b/lib/CodeBuilder/Domain/Prototype/Constants.php deleted file mode 100644 index c4350d2791..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Constants.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -class Constants extends Collection -{ - /** - * @param list $constants - */ - public static function fromConstants(array $constants): self - { - return new static(array_reduce($constants, function ($acc, $constant) { - $acc[$constant->name()] = $constant; - return $acc; - }, [])); - } - - protected function singularName(): string - { - return 'constant'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/DefaultValue.php b/lib/CodeBuilder/Domain/Prototype/DefaultValue.php deleted file mode 100644 index ce0ff10e98..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/DefaultValue.php +++ /dev/null @@ -1,26 +0,0 @@ -none = true; - - return $new; - } - - public static function null(): DefaultValue - { - return new static(null); - } - - public function notNone(): bool - { - return !$this->none; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Docblock.php b/lib/CodeBuilder/Domain/Prototype/Docblock.php deleted file mode 100644 index ca9c49d54a..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Docblock.php +++ /dev/null @@ -1,42 +0,0 @@ -docblock ?? ''; - } - - public static function fromString(string $string): self - { - return new self($string); - } - - public static function none(): self - { - return new self(); - } - - public function notNone(): bool - { - return null !== $this->docblock; - } - - /** - * @return list - */ - public function asLines(): array - { - if ($this->docblock === null) { - return []; - } - - return explode("\n", $this->docblock); - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/EnumPrototype.php b/lib/CodeBuilder/Domain/Prototype/EnumPrototype.php deleted file mode 100644 index 9b6efff9d0..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/EnumPrototype.php +++ /dev/null @@ -1,20 +0,0 @@ -cases; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Enums.php b/lib/CodeBuilder/Domain/Prototype/Enums.php deleted file mode 100644 index edfbba0cd2..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Enums.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -class Enums extends Collection -{ - /** - * @param list $enums - */ - public static function fromEnums(array $enums): self - { - return new static(array_reduce($enums, function ($arr, EnumPrototype $enum) { - $arr[$enum->name()] = $enum; - return $arr; - }, [])); - } - - protected function singularName(): string - { - return 'trait'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/ExtendsClass.php b/lib/CodeBuilder/Domain/Prototype/ExtendsClass.php deleted file mode 100644 index 518921ae86..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/ExtendsClass.php +++ /dev/null @@ -1,31 +0,0 @@ -class; - } - - public static function fromString(string $string): self - { - return new self(Type::fromString($string)); - } - - public static function none(): self - { - return new self(Type::none()); - } - - public function notNone(): bool - { - return $this->class->notNone(); - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/ExtendsInterfaces.php b/lib/CodeBuilder/Domain/Prototype/ExtendsInterfaces.php deleted file mode 100644 index b2469322a3..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/ExtendsInterfaces.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ -class ExtendsInterfaces extends Collection -{ - /** - * @param list $types - */ - public static function fromTypes(array $types): self - { - return new self($types); - } - - protected function singularName(): string - { - return 'extend interface'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php b/lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php deleted file mode 100644 index 0be93e9ae2..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -class ImplementsInterfaces extends Collection -{ - public function __toString(): string - { - return implode(', ', array_reduce($this->items, function ($acc, $interfaceName) { - $acc[] = $interfaceName->__toString(); - return $acc; - })); - } - - public static function fromTypes(array $types): self - { - return new static(array_reduce($types, function ($acc, $type) { - $acc[(string) $type] = $type; - return $acc; - }, [])); - } - - protected function singularName(): string - { - return 'implement interface'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/InterfacePrototype.php b/lib/CodeBuilder/Domain/Prototype/InterfacePrototype.php deleted file mode 100644 index 9b91d641f7..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/InterfacePrototype.php +++ /dev/null @@ -1,23 +0,0 @@ -extendsInterfaces = $extendsInterfaces ?: ExtendsInterfaces::empty(); - } - - public function extendsInterfaces(): ExtendsInterfaces - { - return $this->extendsInterfaces; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Interfaces.php b/lib/CodeBuilder/Domain/Prototype/Interfaces.php deleted file mode 100644 index a9ee094f75..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Interfaces.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -class Interfaces extends Collection -{ - public static function fromInterfaces(array $interfaces): self - { - return new static($interfaces); - } - - protected function singularName(): string - { - return 'interface'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Line.php b/lib/CodeBuilder/Domain/Prototype/Line.php deleted file mode 100644 index d32d9e3305..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Line.php +++ /dev/null @@ -1,20 +0,0 @@ -line; - } - - public static function fromString(string $line): Line - { - return new self($line); - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Lines.php b/lib/CodeBuilder/Domain/Prototype/Lines.php deleted file mode 100644 index 7e88bb6076..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Lines.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ -class Lines extends Collection -{ - public function __toString(): string - { - return implode("\n", $this->items); - } - - /** - * @param array $lines - */ - public static function fromLines(array $lines): self - { - return new self($lines); - } - - protected function singularName(): string - { - return 'line'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Method.php b/lib/CodeBuilder/Domain/Prototype/Method.php deleted file mode 100644 index a48429ff18..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Method.php +++ /dev/null @@ -1,98 +0,0 @@ -visibility = $visibility ?? Visibility::public(); - $this->parameters = $parameters ?? Parameters::empty(); - $this->returnType = $returnType ?? ReturnType::none(); - $this->docblock = $docblock ?? Docblock::none(); - $this->isStatic = (bool)($modifierFlags & self::IS_STATIC); - $this->isAbstract = (bool)($modifierFlags & self::IS_ABSTRACT); - $this->methodBody = $methodBody ?? MethodBody::empty(); - } - - public function name(): string - { - return $this->name; - } - - public function visibility(): Visibility - { - return $this->visibility; - } - - public function parameters(): Parameters - { - return $this->parameters; - } - - public function returnType(): ReturnType - { - return $this->returnType; - } - - /** - * @return Attribute[] - */ - public function attributes(): array - { - return $this->attributes; - } - - public function docblock(): Docblock - { - return $this->docblock; - } - - public function isStatic(): bool - { - return $this->isStatic; - } - - public function isAbstract(): bool - { - return $this->isAbstract; - } - - public function body(): MethodBody - { - return $this->methodBody; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/MethodBody.php b/lib/CodeBuilder/Domain/Prototype/MethodBody.php deleted file mode 100644 index eed7c7b059..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/MethodBody.php +++ /dev/null @@ -1,29 +0,0 @@ - $lines - */ - public static function fromLines(array $lines): MethodBody - { - return new self(Lines::fromLines($lines)); - } - - public static function empty(): MethodBody - { - return new self(Lines::empty()); - } - - public function lines(): Lines - { - return $this->lines; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Methods.php b/lib/CodeBuilder/Domain/Prototype/Methods.php deleted file mode 100644 index eb944924a7..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Methods.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -class Methods extends Collection -{ - /** - * @param Method[] $methods - */ - public static function fromMethods(array $methods): self - { - return new self(array_reduce($methods, function ($acc, $method) { - $acc[$method->name()] = $method; - return $acc; - }, [])); - } - - protected function singularName(): string - { - return 'method'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/NamespaceName.php b/lib/CodeBuilder/Domain/Prototype/NamespaceName.php deleted file mode 100644 index 229a9ad21c..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/NamespaceName.php +++ /dev/null @@ -1,11 +0,0 @@ -type = $type ?: Type::none(); - $this->defaultValue = $defaultValue ?: DefaultValue::none(); - $this->updatePolicy = $updatePolicy; - } - - public function name(): string - { - return $this->name; - } - - public function type(): Type - { - return $this->type; - } - - public function defaultValue(): DefaultValue - { - return $this->defaultValue; - } - - public function byReference(): bool - { - return $this->byReference; - } - - public function visibility(): ?Visibility - { - return $this->visibility; - } - - public function isVariadic(): bool - { - return $this->isVariadic; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Parameters.php b/lib/CodeBuilder/Domain/Prototype/Parameters.php deleted file mode 100644 index 7cad622c69..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Parameters.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ -class Parameters extends Collection -{ - /** - * @param array $parameters - */ - public static function fromParameters(array $parameters): self - { - return new self($parameters); - } - - protected function singularName(): string - { - return 'parameter'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Properties.php b/lib/CodeBuilder/Domain/Prototype/Properties.php deleted file mode 100644 index 2c2380064f..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Properties.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -class Properties extends Collection -{ - /** - * @param array $properties - */ - public static function fromProperties(array $properties): self - { - return new static(array_reduce($properties, function ($acc, Property $property) { - $acc[$property->name()] = $property; - return $acc; - }, [])); - } - - protected function singularName(): string - { - return 'property'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Property.php b/lib/CodeBuilder/Domain/Prototype/Property.php deleted file mode 100644 index 17fbac31ca..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Property.php +++ /dev/null @@ -1,69 +0,0 @@ -visibility = $visibility ?: Visibility::public(); - $this->defaultValue = $defaultValue ?: DefaultValue::none(); - $this->type = $type ?: Type::none(); - $this->docType = $docType ?: Type::none(); - $this->updatePolicy = $updatePolicy; - } - - public function name(): string - { - return $this->name; - } - - public function visibility(): Visibility - { - return $this->visibility; - } - - public function defaultValue(): DefaultValue - { - return $this->defaultValue; - } - - public function type(): Type - { - return $this->type; - } - - public function docTypeOrType(): Type - { - if ($this->docType->notNone()) { - return $this->docType; - } - - return $this->type; - } - - public function docType(): Type - { - return $this->docType; - } - - public function docTypeAddsAdditionalInfo(): bool - { - return (string)$this->docType !== (string)$this->type; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Prototype.php b/lib/CodeBuilder/Domain/Prototype/Prototype.php deleted file mode 100644 index 45d83a503a..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Prototype.php +++ /dev/null @@ -1,18 +0,0 @@ -updatePolicy = $updatePolicy ?: UpdatePolicy::update(); - } - - public function applyUpdate(): bool - { - return $this->updatePolicy->applyUpdate(); - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/QualifiedName.php b/lib/CodeBuilder/Domain/Prototype/QualifiedName.php deleted file mode 100644 index 616a68188a..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/QualifiedName.php +++ /dev/null @@ -1,20 +0,0 @@ -name; - } - - public static function fromString(string $name): QualifiedName - { - return new static($name); - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/ReturnType.php b/lib/CodeBuilder/Domain/Prototype/ReturnType.php deleted file mode 100644 index cdbd7e71a8..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/ReturnType.php +++ /dev/null @@ -1,36 +0,0 @@ -type; - } - - public static function fromString(string $string): self - { - return new self(Type::fromString($string)); - } - - public static function none(): self - { - return new self(Type::none()); - } - - public function notNone(): bool - { - return $this->type->notNone(); - } - - public function type(): Type - { - return $this->type; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/SourceCode.php b/lib/CodeBuilder/Domain/Prototype/SourceCode.php deleted file mode 100644 index 6f615381ef..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/SourceCode.php +++ /dev/null @@ -1,66 +0,0 @@ -namespace = $namespace ?: NamespaceName::fromString(''); - $this->useStatements = $useStatements ?: UseStatements::empty(); - $this->classes = $classes ?: Classes::empty(); - $this->interfaces = $interfaces ?: Interfaces::empty(); - $this->traits = $traits ?: Traits::empty(); - $this->enums = $enums ?: Enums::empty(); - } - - public function namespace(): QualifiedName - { - return $this->namespace; - } - - public function useStatements(): UseStatements - { - return $this->useStatements; - } - - public function classes(): Classes - { - return $this->classes; - } - - public function interfaces(): Interfaces - { - return $this->interfaces; - } - - public function traits(): Traits - { - return $this->traits; - } - - public function enums(): Enums - { - return $this->enums; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/TraitPrototype.php b/lib/CodeBuilder/Domain/Prototype/TraitPrototype.php deleted file mode 100644 index 329574b2e5..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/TraitPrototype.php +++ /dev/null @@ -1,16 +0,0 @@ - - */ -class Traits extends Collection -{ - /** - * @param list $traits - */ - public static function fromTraits(array $traits): self - { - return new static(array_reduce($traits, function ($arr, TraitPrototype $trait) { - $arr[$trait->name()] = $trait; - return $arr; - }, [])); - } - - protected function singularName(): string - { - return 'trait'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Type.php b/lib/CodeBuilder/Domain/Prototype/Type.php deleted file mode 100644 index 8679503e6b..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Type.php +++ /dev/null @@ -1,62 +0,0 @@ -type ?? ''; - } - - public function originalType(): mixed - { - return $this->originalType; - } - - public static function fromString(string $type): Type - { - return new self($type); - } - - public static function none(): Type - { - $new = new self(); - $new->none = true; - - return $new; - } - - public function namespace(): ?string - { - $type = $this->type; - if (null === $type) { - return null; - } - - if (str_starts_with($type, '?')) { - $type = substr($type, 1); - } - - if (false === strrpos($type, '\\')) { - return null; - } - - return substr($type, 0, strrpos($type, '\\')); - } - - public function notNone(): bool - { - return false === $this->none; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/UpdatePolicy.php b/lib/CodeBuilder/Domain/Prototype/UpdatePolicy.php deleted file mode 100644 index 0bdc7e62a2..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/UpdatePolicy.php +++ /dev/null @@ -1,25 +0,0 @@ -doUpdate; - } - - public static function update(): UpdatePolicy - { - return new self(true); - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/UseStatement.php b/lib/CodeBuilder/Domain/Prototype/UseStatement.php deleted file mode 100644 index 6e30dd9b49..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/UseStatement.php +++ /dev/null @@ -1,67 +0,0 @@ -alias) { - return (string) $this->className . ' as ' . $this->alias; - } - - return (string) $this->className; - } - - public static function fromNameAndAlias(string $type, ?string $alias = null): self - { - return new self(Type::fromString($type), $alias); - } - - public static function fromNameAliasAndType(string $name, ?string $alias, string $type): self - { - return new self(Type::fromString($name), $alias, $type); - } - - public static function fromType(string $type): self - { - return new self(Type::fromString($type)); - } - - public function hasAlias(): bool - { - return null !== $this->alias; - } - - public function alias(): ?string - { - return $this->alias; - } - - public function name(): Type - { - return $this->className; - } - - public function type(): ?string - { - return $this->type; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/UseStatements.php b/lib/CodeBuilder/Domain/Prototype/UseStatements.php deleted file mode 100644 index b5d3a299fa..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/UseStatements.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ -class UseStatements extends Collection -{ - /** - * @param list $useStatements - */ - public static function fromUseStatements(array $useStatements): self - { - return new self($useStatements); - } - - public function sorted(): UseStatements - { - $items = iterator_to_array($this); - usort($items, function (UseStatement $left, UseStatement $right): int { - return strcmp((string) $left, $right); - }); - - return new self($items); - } - - protected function singularName(): string - { - return 'use statement'; - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Value.php b/lib/CodeBuilder/Domain/Prototype/Value.php deleted file mode 100644 index 59fed46d6a..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Value.php +++ /dev/null @@ -1,58 +0,0 @@ -value; - } - - public function export(): string - { - if ($this->value === null) { - return 'null'; - } - - if (is_array($this->value)) { - return self::renderArray($this->value); - } - - return var_export($this->value, true); - } - - /** - * @param array $array - */ - private static function renderArray(array $array): string - { - $parts = []; - $isList = array_keys($array) === range(0, count($array) - 1); - - foreach ($array as $key => $value) { - if (is_array($value)) { - $value = self::renderArray($array); - } - if (!is_scalar($value)) { - continue; - } - if ($isList) { - $parts[] = sprintf('%s', json_encode($value)); - continue; - } - $parts[] = sprintf('%s => %s', json_encode($key), json_encode($value)); - } - - return sprintf('[%s]', implode(', ', $parts)); - } -} diff --git a/lib/CodeBuilder/Domain/Prototype/Visibility.php b/lib/CodeBuilder/Domain/Prototype/Visibility.php deleted file mode 100644 index 79437d1a8d..0000000000 --- a/lib/CodeBuilder/Domain/Prototype/Visibility.php +++ /dev/null @@ -1,57 +0,0 @@ -visibility = $visibility; - } - - public function __toString(): string - { - return $this->visibility; - } - - public static function fromString(string $string): self - { - return new self($string); - } - - public static function private(): self - { - return new self(self::PRIVATE); - } - - public static function protected(): self - { - return new self(self::PROTECTED); - } - - public static function public(): self - { - return new self(self::PUBLIC); - } -} diff --git a/lib/CodeBuilder/Domain/Renderer.php b/lib/CodeBuilder/Domain/Renderer.php deleted file mode 100644 index d21f433180..0000000000 --- a/lib/CodeBuilder/Domain/Renderer.php +++ /dev/null @@ -1,11 +0,0 @@ -current(); - if (!$file instanceof SplFileInfo) { - throw new RuntimeException( - sprintf( - 'Expected instance of "\SplFileInfo", got "%s".', - get_debug_type($file) - ) - ); - } - - $filename = $file->getFilename(); - - if (!$file->isDir() || // Keep only directy - !preg_match('/^\d+\.\d+/', $filename) || // Should have at leasts major and minor version - !version_compare($filename, $this->phpVersion, '<=') // Should be at maximum equals to the defined version - ) { - return false; - } - - return true; - } -} diff --git a/lib/CodeBuilder/Domain/TemplatePathResolver/PhpVersionPathResolver.php b/lib/CodeBuilder/Domain/TemplatePathResolver/PhpVersionPathResolver.php deleted file mode 100644 index 457c409192..0000000000 --- a/lib/CodeBuilder/Domain/TemplatePathResolver/PhpVersionPathResolver.php +++ /dev/null @@ -1,44 +0,0 @@ - $paths - * - * @return list - */ - public function resolve(iterable $paths): iterable - { - $resolvedPaths = []; - - foreach ($paths as $path) { - if (!file_exists($path)) { - continue; - } - - $phpDirectoriesIterator = new FilterPhpVersionDirectoryIterator( - new FilesystemIterator($path), - $this->phpVersion - ); - $phpDirectories = array_keys(iterator_to_array($phpDirectoriesIterator)); - rsort($phpDirectories, SORT_NATURAL); - - $resolvedPaths = array_merge($resolvedPaths, $phpDirectories); - $resolvedPaths[] = $path; - } - - return $resolvedPaths; - } -} diff --git a/lib/CodeBuilder/Domain/Updater.php b/lib/CodeBuilder/Domain/Updater.php deleted file mode 100644 index 14c0e3e045..0000000000 --- a/lib/CodeBuilder/Domain/Updater.php +++ /dev/null @@ -1,12 +0,0 @@ -generator->render($prototype); - } - - public function apply(Prototype\Prototype $prototype, TextDocument $code): string - { - return $this->updater->textEditsFor($prototype, $code)->apply($code); - } -} diff --git a/lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php b/lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php deleted file mode 100644 index 1406ae00a0..0000000000 --- a/lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php +++ /dev/null @@ -1,681 +0,0 @@ -renderer()->render($prototype); - $this->assertEquals(rtrim(TextDocumentBuilder::fromString($expectedCode), "\n"), rtrim($code, "\n")); - } - - /** - * @return Generator - */ - public static function provideRender(): Generator - { - yield 'Renders an empty PHP file' => [ - new SourceCode(), - ' [ - new SourceCode( - NamespaceName::fromString('Acme') - ), - <<<'EOT' - [ - new SourceCode( - NamespaceName::root(), - UseStatements::empty(), - Classes::fromClasses([ new ClassPrototype('Dog'), new ClassPrototype('Cat') ]) - ), - <<<'EOT' - [ - new SourceCode( - NamespaceName::root(), - UseStatements::empty(), - Classes::empty(), - Interfaces::fromInterfaces([ new InterfacePrototype('Cat'), new InterfacePrototype('Squirrel') ]) - ), - <<<'EOT' - [ - new SourceCode( - NamespaceName::root(), - UseStatements::empty(), - Classes::empty(), - Interfaces::empty(), - Traits::fromTraits([ new TraitPrototype('Fox'), new TraitPrototype('Hare') ]) - ), - <<<'EOT' - [ - new SourceCode( - NamespaceName::root(), - UseStatements::fromUseStatements([ - UseStatement::fromType('Acme\Post\Board'), - UseStatement::fromType('Acme\Post\Zebra') - ]) - ), - <<<'EOT' - [ - new ClassPrototype('Dog'), - <<<'EOT' - class Dog - { - } - EOT - ]; - yield 'Renders a class with properties' => [ - new ClassPrototype( - 'Dog', - Properties::fromProperties([ - new Property('planes') - ]) - ), - <<<'EOT' - class Dog - { - public $planes; - } - EOT - ]; - yield 'Renders a property' => [ - new Property('planes'), - <<<'EOT' - public $planes; - EOT - ]; - yield 'Renders private properties with default value' => [ - new Property('trains', Visibility::private(), DefaultValue::null()), - <<<'EOT' - private $trains = null; - EOT - ]; - yield 'Renders a class with constants' => [ - new ClassPrototype( - 'Dog', - Properties::empty(), - Constants::fromConstants([ - new Constant('AAA', Value::fromValue('aaa')) - ]) - ), - <<<'EOT' - class Dog - { - const AAA = 'aaa'; - } - EOT - ]; - yield 'Renders a class with methods' => [ - new ClassPrototype( - 'Dog', - Properties::empty(), - Constants::empty(), - Methods::fromMethods([ - new Method('hello'), - ]) - ), - <<<'EOT' - class Dog - { - public function hello() - { - } - } - EOT - ]; - yield 'Renders a class method with a body' => [ - new ClassPrototype( - 'Dog', - Properties::empty(), - Constants::empty(), - Methods::fromMethods([ - new Method( - 'hello', - null, - Parameters::empty(), - ReturnType::none(), - Docblock::none(), - 0, - MethodBody::fromLines([ - Line::fromString('$this->foobar = $barfoo;') - ]) - ), - ]) - ), - <<<'EOT' - class Dog - { - public function hello() - { - $this->foobar = $barfoo; - } - } - EOT - ]; - yield 'Renders a class method with attributes' => [ - new ClassPrototype( - 'Dog', - Properties::empty(), - Constants::empty(), - Methods::fromMethods([ - new Method( - 'hello', - null, - Parameters::empty(), - ReturnType::none(), - Docblock::none(), - 0, - MethodBody::fromLines([]), - null, - [ - new Attribute('Foobar', [Value::fromValue('bar'), Value::fromValue(123)]), - new Attribute('\Barfoo\Bagg', []), - ] - ), - ]) - ), - <<<'EOT' - class Dog - { - #[Foobar('bar', 123)] - #[\Barfoo\Bagg] - public function hello() - { - } - } - EOT - ]; - yield 'Renders a method parameters' => [ - new Method('hello', Visibility::private(), Parameters::fromParameters([ - new Parameter('one'), - new Parameter('two', Type::fromString('string')), - new Parameter('three', Type::none(), DefaultValue::fromValue(42)), - ])), - <<<'EOT' - private function hello($one, string $two, $three = 42) - EOT - ]; - yield 'Renders a method nullable parameter' => [ - new Method('hello', Visibility::private(), Parameters::fromParameters([ - new Parameter('two', Type::fromString('?string')), - ])), - <<<'EOT' - private function hello(?string $two) - EOT - ]; - yield 'Renders a method parameter passed as a reference' => [ - new Method('hello', Visibility::private(), Parameters::fromParameters([ - new Parameter('three', Type::none(), DefaultValue::none(), true), - ])), - <<<'EOT' - private function hello(&$three) - EOT - ]; - yield 'Renders static method' => [ - new Method( - 'hello', - Visibility::private(), - Parameters::empty(), - ReturnType::none(), - Docblock::none(), - Method::IS_STATIC - ), - <<<'EOT' - private static function hello() - EOT - ]; - yield 'Renders abstract method' => [ - new Method( - 'hello', - Visibility::private(), - Parameters::empty(), - ReturnType::none(), - Docblock::none(), - Method::IS_ABSTRACT - ), - <<<'EOT' - abstract private function hello() - EOT - ]; - yield 'Renders method with a docblock' => [ - new Method( - 'hello', - Visibility::private(), - Parameters::empty(), - ReturnType::none(), - Docblock::fromString('Hello bob') - ), - <<<'EOT' - /** - * Hello bob - */ - private function hello() - EOT - ]; - yield 'Renders method with a with special chars' => [ - new Method( - 'hello', - Visibility::private(), - Parameters::empty(), - ReturnType::none(), - Docblock::fromString('') - ), - <<<'EOT' - /** - * - */ - private function hello() - EOT - ]; - yield 'Renders method return type' => [ - new Method( - 'hello', - Visibility::private(), - Parameters::empty(), - ReturnType::fromString('Hello') - ), - <<<'EOT' - private function hello(): Hello - EOT - ]; - yield 'Renders method nullable return type' => [ - new Method( - 'hello', - Visibility::private(), - Parameters::empty(), - ReturnType::fromString('?Hello') - ), - <<<'EOT' - private function hello(): ?Hello - EOT - ]; - yield 'Renders a class with a parent' => [ - new ClassPrototype( - 'Kitten', - Properties::empty(), - Constants::empty(), - Methods::empty(), - ExtendsClass::fromString('Cat') - ), - <<<'EOT' - class Kitten extends Cat - { - } - EOT - ]; - yield 'Renders a class with interfaces' => [ - new ClassPrototype( - 'Kitten', - Properties::empty(), - Constants::empty(), - Methods::empty(), - ExtendsClass::none(), - ImplementsInterfaces::fromTypes([ - Type::fromString('Feline'), - Type::fromString('Infant') - ]) - ), - <<<'EOT' - class Kitten implements Feline, Infant - { - } - EOT - ]; - yield 'Renders a property with a comment' => [ - new Property( - 'planes', - Visibility::public(), - DefaultValue::none(), - Type::fromString('PlaneCollection') - ), - <<<'EOT' - /** - * @var PlaneCollection - */ - public $planes; - EOT - ]; - yield 'Renders an interface' => [ - new InterfacePrototype('Dog'), - <<<'EOT' - interface Dog - { - } - EOT - ]; - yield 'Renders an interface with methods' => [ - new InterfacePrototype('Dog', Methods::fromMethods([ - new Method('hello'), - ])), - <<<'EOT' - interface Dog - { - public function hello(); - } - EOT - ]; - yield 'Renders a trait' => [ - new TraitPrototype( - 'Butterfly' - ), - <<<'EOT' - trait Butterfly - { - } - EOT - ]; - - yield 'Renders a trait with properties' => [ - new TraitPrototype( - 'Butterfly', - Properties::fromProperties([ new Property('colour') ]) - ), - <<<'EOT' - trait Butterfly - { - public $colour; - } - EOT - ]; - - yield 'Renders a trait with constants' => [ - new TraitPrototype( - 'Butterfly', - Properties::empty(), - Constants::fromConstants([ - new Constant('WAS_CATERPILLAR', Value::fromValue(true)), - ]) - ), - <<<'EOT' - trait Butterfly - { - const WAS_CATERPILLAR = true; - } - EOT - ]; - yield 'Renders a trait with methods' => [ - new TraitPrototype( - 'Butterfly', - Properties::empty(), - Constants::empty(), - Methods::fromMethods([ - new Method('wings'), - ]) - ), - <<<'EOT' - trait Butterfly - { - public function wings() - { - } - } - EOT - ]; - - yield 'Renders a trait method with a body' => [ - new TraitPrototype( - 'Butterfly', - Properties::empty(), - Constants::empty(), - Methods::fromMethods([ - new Method( - 'hello', - null, - Parameters::empty(), - ReturnType::none(), - Docblock::none(), - 0, - MethodBody::fromLines([ - Line::fromString('$this->foobar = $barfoo;'), - ]) - ), - ]) - ), - <<<'EOT' - trait Butterfly - { - public function hello() - { - $this->foobar = $barfoo; - } - } - EOT - ]; - } - - public function testFromBuilder(): void - { - $expected = <<<'EOT' - namespace('Animals') - ->use('Measurements\\Height') - ->class('Rabbits') - ->extends('Leporidae') - ->implements('Animal') - ->property('force') - ->visibility('private') - ->type('int') - ->defaultValue(5) - ->end() - ->property('guile')->end() - ->method('jump') - ->docblock('All the world will be your enemy, prince with a thousand enemies') - ->parameter('how') - ->defaultValue('high') - ->type('Height') - ->end() - ->end() - ->method('bark') - ->parameter('volume') - ->type('int') - ->end() - ->end() - ->end() - ->interface('Animal') - ->method('sleep')->end() - ->end() - ->trait('Oryctolagus') - ->property('domesticated') - ->visibility('private') - ->defaultValue(true) - ->type('bool') - ->end() - ->method('burrow') - ->parameter('depth') - ->type('Depth') - ->defaultValue('deep') - ->end() - ->end() - ->end() - ->build(); - - $code = $this->renderer()->render($source); - - $this->assertEquals($expected, (string) $code); - } - - public function testConstantsAndProperties(): void - { - $expected = <<<'EOT' - namespace('Animals') - ->class('Rabbits') - ->implements('Animal') - ->property('force') - ->visibility('private') - ->type('int') - ->defaultValue(5) - ->end() - ->property('guile')->end() - ->constant('LEGS', 4)->end() - ->constant('SKIN', 'soft')->end() - ->end() - ->interface('Animal') - ->method('sleep')->end() - ->end() - ->build(); - - $code = $this->renderer()->render($source); - - $this->assertEquals($expected, (string) $code); - } - - abstract protected function renderer(): Renderer; -} diff --git a/lib/CodeBuilder/Tests/Adapter/TolerantParser/TolerantUpdaterTest.php b/lib/CodeBuilder/Tests/Adapter/TolerantParser/TolerantUpdaterTest.php deleted file mode 100644 index 0f8268e92c..0000000000 --- a/lib/CodeBuilder/Tests/Adapter/TolerantParser/TolerantUpdaterTest.php +++ /dev/null @@ -1,16 +0,0 @@ -renderer()->render($builder->build(), 'unknown'); - $this->assertEquals('assertUpdate($existingCode, $prototype, $expectedCode); - } - - /** - * @return Generator - */ - public static function provideClassImport(): Generator - { - yield 'It does nothing when given an empty source code prototype' => [ - - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->build(), - <<<'EOT' - class Aardvark - { - } - EOT - ]; - - yield 'It does not change the namespace if it is the same' => [ - - <<<'EOT' - namespace Animal\Kingdom; - - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->namespace('Animal\Kingdom')->build(), - <<<'EOT' - namespace Animal\Kingdom; - - class Aardvark - { - } - EOT - ]; - - yield 'It adds the namespace if it doesnt exist' => [ - - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->namespace('Animal\Kingdom')->build(), - <<<'EOT' - namespace Animal\Kingdom; - - class Aardvark - { - } - EOT - ]; - - yield 'It updates the namespace' => [ - - <<<'EOT' - namespace Animal\Kingdom; - - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->namespace('Bovine\Kingdom')->build(), - <<<'EOT' - namespace Bovine\Kingdom; - - class Aardvark - { - } - EOT - ]; - - yield 'It adds use statements' => [ - - <<<'EOT' - $bovine = new Bovine(); - EOT - , SourceCodeBuilder::create()->use('Foo\Bovine')->build(), - <<<'EOT' - - use Foo\Bovine; - - $bovine = new Bovine(); - EOT - ]; - - yield 'It adds use statements with an alias' => [ - - <<<'EOT' - // test - $bovine = new Bovine(); - EOT - , SourceCodeBuilder::create()->use('Foo\Bovine', 'Cow')->build(), - <<<'EOT' - - use Foo\Bovine as Cow; - - // test - $bovine = new Bovine(); - EOT - ]; - - yield 'It adds use statements with an alias with existing imports' => [ - - <<<'EOT' - use Foo\Dino; - - // test - $bovine = new Bovine(); - EOT - , SourceCodeBuilder::create()->use('Foo\Bovine', 'Cow')->build(), - <<<'EOT' - use Foo\Bovine as Cow; - use Foo\Dino; - - // test - $bovine = new Bovine(); - EOT - ]; - - yield 'It adds use statements after a namespace' => [ - - <<<'EOT' - namespace Kingdom; - - $bovine = new Bovine(); - EOT - , SourceCodeBuilder::create()->use('Bovine')->build(), - <<<'EOT' - namespace Kingdom; - - use Bovine; - - $bovine = new Bovine(); - EOT - ]; - - yield 'class import: It inserts use statements before the first lexicographically greater use statement' => [ - - <<<'EOT' - namespace Kingdom; - - use Aardvark; - use Badger; - use Antilope; - use Zebra; - use Primate; - EOT - , SourceCodeBuilder::create()->use('Bovine')->build(), - <<<'EOT' - namespace Kingdom; - - use Aardvark; - use Badger; - use Antilope; - use Bovine; - use Zebra; - use Primate; - EOT - ]; - - yield 'class import: It inserts use statements just before the first lexicographically greater use statement' => [ - - <<<'EOT' - namespace Kingdom; - - use Zebra; - use Primate; - EOT - , SourceCodeBuilder::create()->use('Bovine')->build(), - <<<'EOT' - namespace Kingdom; - - use Bovine; - use Zebra; - use Primate; - EOT - ]; - - yield 'class import: It inserts use statements after all lexicographically smaller use statements' => [ - - <<<'EOT' - namespace Kingdom; - - use Badger; - use Aardvark; - EOT - , SourceCodeBuilder::create()->use('Bovine')->build(), - <<<'EOT' - namespace Kingdom; - - use Badger; - use Aardvark; - use Bovine; - EOT - ]; - - yield 'class import: It ignores existing use statements' => [ - - <<<'EOT' - namespace Kingdom; - - use Primate; - EOT - , SourceCodeBuilder::create()->use('Primate')->build(), - <<<'EOT' - namespace Kingdom; - - use Primate; - EOT - ]; - - yield 'class import: It ignores repeated namespaced use statements' => [ - - <<<'EOT' - namespace Kingdom; - - EOT - , SourceCodeBuilder::create()->use('Primate\Ape')->use('Primate\Ape')->build(), - <<<'EOT' - namespace Kingdom; - - use Primate\Ape; - - EOT - ]; - - yield 'class import: It ignores existing aliased use statements' => [ - - <<<'EOT' - namespace Kingdom; - - use Primate as Foobar; - EOT - , SourceCodeBuilder::create()->use('Primate')->build(), - <<<'EOT' - namespace Kingdom; - - use Primate as Foobar; - EOT - ]; - - yield 'class import: It appends multiple use statements' => [ - - <<<'EOT' - namespace Kingdom; - - use Primate; - EOT - , SourceCodeBuilder::create()->use('Animal\Bovine')->use('Feline')->use('Canine')->build(), - <<<'EOT' - namespace Kingdom; - - use Animal\Bovine; - use Canine; - use Feline; - use Primate; - EOT - ]; - - yield 'class import: It maintains an empty line between the class and the use statements' => [ - - <<<'EOT' - namespace Kingdom; - - class Foobar - { - } - EOT - , SourceCodeBuilder::create()->use('Feline')->build(), - <<<'EOT' - namespace Kingdom; - - use Feline; - - class Foobar - { - } - EOT - ]; - - yield 'class import: It maintains an empty line between the trait and the use statements' => [ - - <<<'EOT' - namespace Kingdom; - - trait Foobar - { - } - EOT - , SourceCodeBuilder::create()->use('Feline')->build(), - <<<'EOT' - namespace Kingdom; - - use Feline; - - trait Foobar - { - } - EOT - ]; - - yield 'class import: it maintains empty line between class with no namespace' => [ - - <<<'EOT' - class Foobar - { - } - EOT - , SourceCodeBuilder::create()->use('Foo\Feline')->build(), - <<<'EOT' - - use Foo\Feline; - - class Foobar - { - } - EOT - ]; - - yield 'class import: it maintains empty line between trait with no namespace' => [ - - <<<'EOT' - trait Foobar - { - } - EOT - , SourceCodeBuilder::create()->use('Foo\Feline')->build(), - <<<'EOT' - - use Foo\Feline; - - trait Foobar - { - } - EOT - ]; - - yield 'class import: previously included class with a lexigraphically greater member before it' => [ - <<<'EOT' - use('Phpactor\WorseReflection\Core\Reflection\ReflectionClassLike') - ->build(), - <<<'EOT' - [ - - <<<'EOT' - namespace Animal; - EOT - , SourceCodeBuilder::create()->use('Animal\Primate')->build() - , <<<'EOT' - namespace Animal; - EOT - ]; - - yield 'it does not add additional space' => [ - - <<<'EOT' - namespace Animal; - - class Foo {} - EOT - , SourceCodeBuilder::create()->use('Animal\Primate')->build() - , <<<'EOT' - namespace Animal; - - class Foo {} - EOT - ]; - } - /** - * @return Generator - */ - public static function provideFunctionImport(): Generator - { - yield 'It adds use function statements' => [ - - <<<'EOT' - hello('you'); - EOT - , SourceCodeBuilder::create()->useFunction('Foo\hello')->build(), - <<<'EOT' - - use function Foo\hello; - - hello('you'); - EOT - ]; - - yield 'It adds use function statements with an alias' => [ - - <<<'EOT' - - hello('you'); - EOT - , SourceCodeBuilder::create()->useFunction('Foo\hello', 'bar')->build(), - <<<'EOT' - - use function Foo\hello as bar; - - hello('you'); - EOT - ]; - - yield 'It adds use function statements after with an alias' => [ - - <<<'EOT' - use function Foo\hello as boo; - - hello('you'); - EOT - , SourceCodeBuilder::create()->useFunction('Foo\hello', 'bar')->build(), - <<<'EOT' - use function Foo\hello as boo; - use function Foo\hello as bar; - - hello('you'); - EOT - ]; - - yield 'It ignores existing function imports' => [ - - <<<'EOT' - use function Foo\hello as boo; - - hello('you'); - EOT - , SourceCodeBuilder::create()->useFunction('Foo\hello', 'boo')->build(), - <<<'EOT' - use function Foo\hello as boo; - - hello('you'); - EOT - ]; - - yield 'adds function imports after class imports' => [ - - <<<'EOT' - use Foobar\Acme; - use Foobar\Hello; - use Foobar\Zoo; - - hello('you'); - EOT - , SourceCodeBuilder::create()->useFunction('Foobar\Bello')->build(), - <<<'EOT' - use Foobar\Acme; - use Foobar\Hello; - use Foobar\Zoo; - use function Foobar\Bello; - - hello('you'); - EOT - ]; - } - - #[DataProvider('provideClasses')] - #[DataProvider('provideMethodParameters')] - public function testClasses(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $this->assertUpdate($existingCode, $prototype, $expectedCode); - } - - /** - * @return Generator - */ - public static function provideClasses(): Generator - { - yield 'It does nothing when prototype has only the class' => [ - - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->class('Aardvark')->end()->build(), - <<<'EOT' - class Aardvark - { - } - EOT - ]; - - yield 'It adds a class to an empty file' => [ - - <<<'EOT' - EOT - , SourceCodeBuilder::create()->class('Anteater')->end()->build(), - <<<'EOT' - - class Anteater - { - } - EOT - ]; - - yield 'It adds a class' => [ - - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->class('Anteater')->end()->build(), - <<<'EOT' - class Aardvark - { - } - - class Anteater - { - } - EOT - ]; - - yield 'It adds a class after a namespace' => [ - - <<<'EOT' - namespace Animals; - - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->class('Anteater')->end()->build(), - <<<'EOT' - namespace Animals; - - class Aardvark - { - } - - class Anteater - { - } - EOT - ]; - - yield 'It does not modify a class with a namespace' => [ - - <<<'EOT' - namespace Animals; - - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->namespace('Animals')->class('Aardvark')->end()->build(), - <<<'EOT' - namespace Animals; - - class Aardvark - { - } - EOT - ]; - - yield 'It adds multiple classes' => [ - <<<'EOT' - EOT - , SourceCodeBuilder::create()->class('Aardvark')->end()->class('Anteater')->end()->build(), - <<<'EOT' - - class Aardvark - { - } - - class Anteater - { - } - EOT - ]; - - yield 'It extends a class' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->class('Aardvark')->extends('Animal')->end()->build(), - <<<'EOT' - class Aardvark extends Animal - { - } - EOT - ]; - - yield 'It modifies an existing extends' => [ - <<<'EOT' - class Aardvark extends Giraffe - { - } - EOT - , SourceCodeBuilder::create()->class('Aardvark')->extends('Animal')->end()->build(), - <<<'EOT' - class Aardvark extends Animal - { - } - EOT - ]; - - yield 'It is idempotent extends' => [ - <<<'EOT' - class Aardvark extends Animal - { - } - EOT - , SourceCodeBuilder::create()->class('Aardvark')->extends('Animal')->end()->build(), - <<<'EOT' - class Aardvark extends Animal - { - } - EOT - ]; - - yield 'It is implements an interface' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->class('Aardvark')->implements('Animal')->end()->build(), - <<<'EOT' - class Aardvark implements Animal - { - } - EOT - ]; - - yield 'It is implements implementss' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create()->class('Aardvark')->implements('Zoo')->implements('Animal')->end()->build(), - <<<'EOT' - class Aardvark implements Zoo, Animal - { - } - EOT - ]; - - yield 'It is adds implements' => [ - <<<'EOT' - class Aardvark implements Zoo - { - } - EOT - , SourceCodeBuilder::create()->class('Aardvark')->implements('Animal')->end()->build(), - <<<'EOT' - class Aardvark implements Zoo, Animal - { - } - EOT - ]; - - yield 'It ignores existing implements names' => [ - <<<'EOT' - class Aardvark implements Animal - { - } - EOT - , SourceCodeBuilder::create()->class('Aardvark')->implements('Zoo')->implements('Animal')->end()->build(), - <<<'EOT' - class Aardvark implements Animal, Zoo - { - } - EOT - ]; - yield 'It adds a documented class' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->docblock("/** Hello */\n") - ->end() - ->build(), - <<<'EOT' - /** Hello */ - class Aardvark - { - } - EOT - ]; - } - - #[DataProvider('provideEnums')] - public function testEnums(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $this->assertUpdate($existingCode, $prototype, $expectedCode); - } - - public static function provideEnums(): Generator - { - yield 'Rendering an enum' => [ - '', - SourceCodeBuilder::create() - ->enum('SomeEnum') - ->case('ONE')->end() - ->case('TWO')->end() - ->end() - ->build(), - <<<'EOT' - - enum SomeEnum - { - case ONE; - case TWO; - } - EOT, - ]; - yield 'Adding a case to an already existing enum' => [ - <<<'EOT' - enum SomeEnum - { - case ONE; - case TWO; - } - EOT, - SourceCodeBuilder::create() - ->enum('SomeEnum') - ->case('THREE')->end() - ->end() - ->build(), - <<<'EOT' - enum SomeEnum - { - case ONE; - case TWO; - case THREE; - - } - EOT, - ]; - yield 'Adding a case to break a backed enum' => [ - <<<'EOT' - enum SomeEnum: string - { - case ONE = '1'; - case TWO = '2'; - } - EOT, - SourceCodeBuilder::create() - ->enum('SomeEnum') - ->case('THREE')->end() - ->end() - ->build(), - <<<'EOT' - enum SomeEnum: string - { - case ONE = '1'; - case TWO = '2'; - case THREE; - - } - EOT, - ]; - } - - #[DataProvider('provideTraits')] - public function testTraits(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $this->assertUpdate($existingCode, $prototype, $expectedCode); - } - - /** - * @return Generator - */ - public static function provideTraits(): Generator - { - yield 'It does nothing when prototype has only the trait' => [ - - <<<'EOT' - trait Aardvark - { - } - EOT - , SourceCodeBuilder::create()->trait('Aardvark')->end()->build(), - <<<'EOT' - trait Aardvark - { - } - EOT - ]; - - yield 'It adds a trait to an empty file' => [ - - <<<'EOT' - EOT - , SourceCodeBuilder::create()->trait('Anteater')->end()->build(), - <<<'EOT' - - trait Anteater - { - } - EOT - ]; - - yield 'It adds a trait' => [ - - <<<'EOT' - trait Aardvark - { - } - EOT - , SourceCodeBuilder::create()->trait('Anteater')->end()->build(), - <<<'EOT' - trait Aardvark - { - } - - trait Anteater - { - } - EOT - ]; - - yield 'It adds a trait after a namespace' => [ - - <<<'EOT' - namespace Animals; - - trait Aardvark - { - } - EOT - , SourceCodeBuilder::create()->trait('Anteater')->end()->build(), - <<<'EOT' - namespace Animals; - - trait Aardvark - { - } - - trait Anteater - { - } - EOT - ]; - - yield 'It does not modify a trait with a namespace' => [ - - <<<'EOT' - namespace Animals; - - trait Aardvark - { - } - EOT - , SourceCodeBuilder::create()->namespace('Animals')->trait('Aardvark')->end()->build(), - <<<'EOT' - namespace Animals; - - trait Aardvark - { - } - EOT - ]; - - yield 'It adds multiple traites' => [ - <<<'EOT' - EOT - , SourceCodeBuilder::create()->trait('Aardvark')->end()->trait('Anteater')->end()->build(), - <<<'EOT' - - trait Aardvark - { - } - - trait Anteater - { - } - EOT - ]; - } - - #[DataProvider('provideProperties')] - public function testProperties(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $this->assertUpdate($existingCode, $prototype, $expectedCode); - } - - /** - * @return Generator - */ - public static function provideProperties(): Generator - { - yield 'It adds a property' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public $propertyOne; - } - EOT - ]; - - yield 'It adds a property idempotently' => [ - <<<'EOT' - class Aardvark - { - public $propertyOne; - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public $propertyOne; - } - EOT - ]; - - yield 'It adds a property with existing assigned property' => [ - <<<'EOT' - class Aardvark - { - public $propertyOne = false; - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public $propertyOne = false; - } - EOT - ]; - - yield 'It adds a property after existing properties' => [ - <<<'EOT' - class Aardvark - { - public $eyes - public $nose; - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public $eyes - public $nose; - public $propertyOne; - } - EOT - ]; - - yield 'It adds multiple properties' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->end()->property('propertyTwo')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public $propertyOne; - public $propertyTwo; - } - EOT - ]; - - yield 'It adds a typed property' => [ - <<<'EOT' - class Aardvark - { - public $eyes - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->type('Hello')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public $eyes - - /** - * @var Hello - */ - public $propertyOne; - } - EOT - ]; - - yield 'It adds a generic typed property' => [ - <<<'EOT' - class Aardvark - { - public $eyes - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->type('Hello')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public $eyes - - /** - * @var Hello - */ - public $propertyOne; - } - EOT - ]; - - yield 'It adds a nullable typed property' => [ - <<<'EOT' - class Aardvark - { - public $eyes - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->type( - TypeFactory::fromString('?Hello') - )->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public $eyes - - /** - * @var ?Hello - */ - public $propertyOne; - } - EOT - ]; - - yield 'It adds before methods' => [ - <<<'EOT' - class Aardvark - { - public function crawl() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->property('propertyOne')->type('Hello')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - /** - * @var Hello - */ - public $propertyOne; - - public function crawl() - { - } - } - EOT - ]; - } - - #[DataProvider('provideTraitProperties')] - public function testTraitProperties(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $this->assertUpdate($existingCode, $prototype, $expectedCode); - } - - /** - * @return Generator - */ - public static function provideTraitProperties(): Generator - { - yield 'trait: It adds a property' => [ - <<<'EOT' - trait Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->trait('Aardvark') - ->property('propertyOne')->end() - ->end() - ->build(), - <<<'EOT' - trait Aardvark - { - public $propertyOne; - } - EOT - ]; - - yield 'trait: It adds a property idempotently' => [ - <<<'EOT' - trait Aardvark - { - public $propertyOne; - } - EOT - , SourceCodeBuilder::create() - ->trait('Aardvark') - ->property('propertyOne')->end() - ->end() - ->build(), - <<<'EOT' - trait Aardvark - { - public $propertyOne; - } - EOT - ]; - - yield 'trait: It adds a property with existing assigned property' => [ - <<<'EOT' - trait Aardvark - { - public $propertyOne = false; - } - EOT - , SourceCodeBuilder::create() - ->trait('Aardvark') - ->property('propertyOne')->end() - ->end() - ->build(), - <<<'EOT' - trait Aardvark - { - public $propertyOne = false; - } - EOT - ]; - - yield 'trait: It adds a property after existing properties' => [ - <<<'EOT' - trait Aardvark - { - public $eyes - public $nose; - } - EOT - , SourceCodeBuilder::create() - ->trait('Aardvark') - ->property('propertyOne')->end() - ->end() - ->build(), - <<<'EOT' - trait Aardvark - { - public $eyes - public $nose; - public $propertyOne; - } - EOT - ]; - - yield 'trait: It adds multiple properties' => [ - <<<'EOT' - trait Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->trait('Aardvark') - ->property('propertyOne')->end()->property('propertyTwo')->end() - ->end() - ->build(), - <<<'EOT' - trait Aardvark - { - public $propertyOne; - public $propertyTwo; - } - EOT - ]; - - yield 'trait: It adds documented properties' => [ - <<<'EOT' - trait Aardvark - { - public $eyes - } - EOT - , SourceCodeBuilder::create() - ->trait('Aardvark') - ->property('propertyOne')->type('Hello')->end() - ->end() - ->build(), - <<<'EOT' - trait Aardvark - { - public $eyes - - /** - * @var Hello - */ - public $propertyOne; - } - EOT - ]; - - yield 'trait: It adds a property before methods' => [ - <<<'EOT' - trait Aardvark - { - public function crawl() - { - } - } - EOT - , SourceCodeBuilder::create() - ->trait('Aardvark') - ->property('propertyOne')->type('Hello')->end() - ->end() - ->build(), - <<<'EOT' - trait Aardvark - { - /** - * @var Hello - */ - public $propertyOne; - - public function crawl() - { - } - } - EOT - ]; - } - - #[DataProvider('provideMethods')] - public function testMethods(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $this->assertUpdate($existingCode, $prototype, $expectedCode); - } - /** - * @return Generator - */ - public static function provideMethods(): Generator - { - yield 'It adds a method' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function methodOne() - { - } - } - EOT - ]; - - yield 'It adds multiple methods' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne')->end() - ->method('methodTwo')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function methodOne() - { - } - - public function methodTwo() - { - } - } - EOT - ]; - - yield 'It generates a constructor' => [ - <<<'EOT' - EOT, - SourceCodeBuilder::create() - ->class('Foo') - ->method('__construct') - ->parameter('config') - ->type('int') - ->end() - ->end() - ->end() - ->build(), - <<<'EOT' - - class Foo - { - public function __construct(int $config) - { - } - } - EOT - ]; - - yield 'It generates a constructor with promoted properties' => [ - <<<'EOT' - class Foo - { - } - EOT, - SourceCodeBuilder::create() - ->class('Foo') - ->method('__construct') - ->parameter('config') - ->type('int') - ->visibility(Visibility::private()) - ->end() - ->end() - ->end() - ->build(), - <<<'EOT' - class Foo - { - public function __construct(private int $config) - { - } - - } - EOT - ]; - - yield 'It adds parameterized method' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne') - ->parameter('sniff') - ->type('Snort') - ->end() - ->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function methodOne(Snort $sniff) - { - } - } - EOT - ]; - - yield 'It adds parameterized method with array shape' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne') - ->parameter('sniff') - ->asVariadic() - ->type('Snort') - ->end() - ->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function methodOne(Snort ...$sniff) - { - } - } - EOT - ]; - - yield 'It is idempotent' => [ - <<<'EOT' - class Aardvark - { - public function methodOne() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function methodOne() - { - } - } - EOT - ]; - - yield 'It adds a method after existing methods' => [ - <<<'EOT' - class Aardvark - { - public function eyes() - { - } - - public function nose() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function eyes() - { - } - - public function nose() - { - } - - public function methodOne() - { - } - } - EOT - ]; - - yield 'It adds a documented methods' => [ - <<<'EOT' - class Aardvark - { - public function eyes() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne')->docblock('Hello')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function eyes() - { - } - - /** - * Hello - */ - public function methodOne() - { - } - } - EOT - ]; - - yield 'It adds a method with a body' => [ - <<<'EOT' - class Aardvark - { - public function eyes() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne')->body()->line('echo "Hello World";')->end()->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function eyes() - { - } - - public function methodOne() - { - echo "Hello World"; - } - } - EOT - ]; - - yield 'Add line to a methods body' => [ - <<<'EOT' - class Aardvark - { - public function eyes() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('eyes')->body()->line('echo "Hello World";')->end()->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function eyes() - { - echo "Hello World"; - } - } - EOT - ]; - - yield 'Add lines after existing lines' => [ - <<<'EOT' - class Aardvark - { - public function eyes() - { - echo "Goodbye world!"; - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('eyes')->body()->line('echo "Hello World";')->end()->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function eyes() - { - echo "Goodbye world!"; - echo "Hello World"; - } - } - EOT - ]; - - yield 'Should not add the same line twice' => [ - <<<'EOT' - class Aardvark - { - public function eyes() - { - echo "Hello World"; - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('eyes')->body()->line('echo "Hello World";')->end()->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function eyes() - { - echo "Hello World"; - } - } - EOT - ]; - - yield 'It does not modify existing methods 1' => [ - <<<'EOT' - class Aardvark - { - public function hello( - array $foobar = [] - ) - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('hello')->parameter('foobar')->type('array')->defaultValue([])->end()->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function hello( - array $foobar = [] - ) - { - } - } - EOT - ]; - - yield 'It does not modify existing methods with imported names' => [ - <<<'EOT' - - use Barfoo as Foobar; - - class Aardvark - { - public function hello(Foobar $foobar): Foobar - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('hello')->parameter('foobar')->type('Barfoo')->end()->returnType('Barfoo')->end() - ->end() - ->build(), - <<<'EOT' - - use Barfoo as Foobar; - - class Aardvark - { - public function hello(Foobar $foobar): Foobar - { - } - } - EOT - ]; - - yield 'It modifies the return type' => [ - <<<'EOT' - class Aardvark - { - public function hello(): Foobar - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('hello')->returnType('Barfoo')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function hello(): Barfoo - { - } - } - EOT - ]; - } - - #[DataProvider('provideConstants')] - public function testConstants(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $this->assertUpdate($existingCode, $prototype, $expectedCode); - } - - /** - * @return Generator - */ - public static function provideConstants(): Generator - { - yield 'It adds a constant' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->constant('constantOne', 'foo')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - const constantOne = 'foo'; - } - EOT - ]; - - yield 'It adds is idempotent' => [ - <<<'EOT' - class Aardvark - { - const constantOne = 'aaa'; - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->constant('constantOne', 'aaa')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - const constantOne = 'aaa'; - } - EOT - ]; - - yield 'It adds a constant after existing constants' => [ - <<<'EOT' - class Aardvark - { - const constantOne = 'aaa'; - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->constant('constantTwo', 'bbb')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - const constantOne = 'aaa'; - const constantTwo = 'bbb'; - } - EOT - ]; - - yield 'It adds multiple constants' => [ - <<<'EOT' - class Aardvark - { - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->constant('constantOne', 'a')->end()->constant('constantTwo', 'b')->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - const constantOne = 'a'; - const constantTwo = 'b'; - } - EOT - ]; - - yield 'It adds before methods' => [ - <<<'EOT' - class Aardvark - { - public function crawl() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->constant('constantOne', 1)->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - const constantOne = 1; - - public function crawl() - { - } - } - EOT - ]; - - yield 'It adds before properties' => [ - <<<'EOT' - class Aardvark - { - private $crawlSpace; - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->constant('constantOne', 1)->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - const constantOne = 1; - - private $crawlSpace; - } - EOT - ]; - } - - #[DataProvider('provideInterfaces')] - public function testInterfaces(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $this->assertUpdate($existingCode, $prototype, $expectedCode); - } - - /** - * @return Generator - */ - public static function provideInterfaces(): Generator - { - yield 'It adds an interface' => [ - - <<<'EOT' - EOT - , SourceCodeBuilder::create()->interface('Aardvark')->end()->build(), - <<<'EOT' - - interface Aardvark - { - } - EOT - ]; - - yield 'It adds an interface in a namespace' => [ - - <<<'EOT' - namespace Foobar; - EOT - , SourceCodeBuilder::create()->interface('Aardvark')->end()->build(), - <<<'EOT' - namespace Foobar; - - interface Aardvark - { - } - EOT - ]; - - yield 'It adds methods to an interface' => [ - - <<<'EOT' - interface Aardvark - { - } - EOT - , SourceCodeBuilder::create()->interface('Aardvark')->method('foo')->end()->end()->build(), - <<<'EOT' - interface Aardvark - { - public function foo(); - } - EOT - ]; - } - /** - * @return Generator - */ - public static function provideMethodParameters(): Generator - { - yield 'It adds parameters' => [ - <<<'EOT' - class Aardvark - { - public function methodOne() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne') - ->parameter('sniff') - ->type('Barf') - ->end() - ->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function methodOne(Barf $sniff) - { - } - } - EOT - ]; - - yield 'It adds nullable typed parameters' => [ - <<<'EOT' - class Aardvark - { - public function methodOne() - { - } - } - EOT - , SourceCodeBuilder::create() - ->class('Aardvark') - ->method('methodOne') - ->parameter('sniff')->type( - TypeFactory::fromString('?Barf') - ) - ->end() - ->end() - ->end() - ->build(), - <<<'EOT' - class Aardvark - { - public function methodOne(?Barf $sniff) - { - } - } - EOT - ]; - } - - abstract protected function updater(): Updater; - - private function assertUpdate(string $existingCode, SourceCode $prototype, string $expectedCode): void - { - $existingCode = 'updater()->textEditsFor($prototype, TextDocumentBuilder::fromString($existingCode)); - $this->assertEquals('apply($existingCode)); - } -} diff --git a/lib/CodeBuilder/Tests/Adapter/WorseReflection/WorseBuilderFactoryTest.php b/lib/CodeBuilder/Tests/Adapter/WorseReflection/WorseBuilderFactoryTest.php deleted file mode 100644 index 4df762234c..0000000000 --- a/lib/CodeBuilder/Tests/Adapter/WorseReflection/WorseBuilderFactoryTest.php +++ /dev/null @@ -1,279 +0,0 @@ -build('assertInstanceOf(SourceCode::class, $source); - } - - public function testSimpleClass(): void - { - $source = $this->build('assertEquals('Foobar', $this->getFirstClass($source)->name()); - } - - public function testSimpleClassWithNamespace(): void - { - $source = $this->build('getFirstClass($source); - $this->assertEquals('Foobar', $source->namespace()); - } - - public function testClassWithProperty(): void - { - $source = $this->build('getFirstClass($source); - - $this->assertCount(1, $firstClass->properties()); - $this->assertEquals('foo', $firstClass->properties()->first()?->name()); - } - - public function testClassWithProtectedProperty(): void - { - $source = $this->build('getFirstClass($source); - $this->assertCount(1, $firstClass->properties()); - $this->assertEquals('private', (string) $firstClass->properties()->first()?->visibility()); - } - - public function testClassWithPropertyDefaultValue(): void - { - $this->markTestSkipped('Worse reflection doesn\'t support default property values atm'); - $source = $this->build('assertEquals('foobar', $this->getFirstClass($source)->properties()->first()?->defaultValue()->export()); - } - - public function testClassWithPropertyTyped(): void - { - $source = $this->build('assertEquals('Foobar', $this->getFirstClass($source)->properties()->first()?->type()->__toString()); - } - - public function testClassWithPropertyScalarTyped(): void - { - $source = $this->build('assertEquals('string', $this->getFirstClass($source)->properties()->first()?->type()->__toString()); - } - - public function testClassWithPropertyImportedType(): void - { - $source = $this->build('assertEquals('Foobar', $this->getFirstClass($source)->properties()->first()?->type()->__toString()); - $this->assertEquals('Bar\Foobar', (string) $source->useStatements()->first()); - } - - public function testSimpleTrait(): void - { - $source = $this->build('traits(); - $this->assertCount(1, $traits); - $this->assertEquals('Foobar', $traits->first()?->name()); - } - - public function testSimpleTraitWithNamespace(): void - { - $source = $this->build('traits(); - $this->assertCount(1, $traits); - $this->assertEquals('Foobar', $source->namespace()); - } - - public function testTraitWithProperty(): void - { - $source = $this->build('traits()->first(); - - $this->assertNotNull($firstTrait); - $this->assertCount(1, $firstTrait->properties()); - $this->assertEquals('foo', $firstTrait->properties()->first()?->name()); - } - - public function testTraitWithMethod(): void - { - $source = $this->build('assertEquals('method', $source->traits()->first()?->methods()->first()?->name()); - } - - public function testMethod(): void - { - $source = $this->build('assertEquals('method', $this->getFirstClass($source)->methods()->first()?->name()); - } - - public function testNoVirtualMethod(): void - { - $source = $this->build('assertCount(0, $this->getFirstClass($source)->methods()); - } - - public function testMethodWithReturnType(): void - { - $source = $this->build('assertEquals('string', $this->getFirstMethodInFirstClass($source)->returnType()->__toString()); - } - - public function testMethodWithNullableReturnType(): void - { - $source = $this->build('assertEquals('?string', $this->getFirstMethodInFirstClass($source)->returnType()->__toString()); - } - - public function testMethodProtected(): void - { - $source = $this->build('assertEquals('protected', $this->getFirstMethodInFirstClass($source)->visibility()); - } - - public function testMethodWithParameter(): void - { - $source = $this->build('assertEquals('param', $this->getFirstMethodInFirstClass($source)->parameters()->first()?->name()); - } - - public function testMethodWithNullableParameter(): void - { - $source = $this->build('getFirstMethodInFirstClass($source)->parameters()->first()?->type()); - } - - public function testMethodWithParameterByReference(): void - { - $source = $this->build('assertTrue($this->getFirstMethodInFirstClass($source)->parameters()->first()?->byReference()); - } - - public function testMethodWithTypedParameter(): void - { - $source = $this->build('assertEquals('string', (string) $this->getFirstMethodInFirstClass($source)->parameters()->first()?->type()); - } - - public function testMethodWithVariadicParameter(): void - { - $source = $this->build('assertEquals('string', (string) $this->getFirstMethodInFirstClass($source)->parameters()->first()?->type()); - } - - public function testMethodWithMissingParameterType(): void - { - $source = $this->build('assertEquals('', (string) $this->getFirstMethodInFirstClass($source)->parameters()->first()?->type()); - } - - public function testMethodWithAliasedParameter(): void - { - $source = $this->build('assertEquals('Barfoo', (string) $this->getFirstMethodInFirstClass($source)->parameters()->first()?->type()); - } - - public function testMethodWithDefaultValue(): void - { - $source = $this->build('assertEquals(1234, (string) $this->getFirstMethodInFirstClass($source)->parameters()->first()?->defaultValue()->value()); - } - - public function testMethodWithDefaultValueQuoted(): void - { - $source = $this->build('assertEquals('1234', (string) $this->getFirstMethodInFirstClass($source)->parameters()->first()?->defaultValue()->value()); - } - - public function testStaticMethod(): void - { - $source = $this->build('assertTrue($this->getFirstMethodInFirstClass($source)->isStatic()); - } - - public function testClassWhichExtendsClassWithMethods(): void - { - $source = $this->build( - <<<'EOT' - assertCount(0, $source->classes()->get('BarBar')->methods()); - $this->assertCount(0, $source->classes()->get('BarBar')->properties()); - } - - public function testInterface(): void - { - $source = $this->build('interfaces()->first(); - $this->assertNotNull($firstInterface); - $this->assertEquals('Foobar', (string) $firstInterface->name()); - } - - public function testInterfaceWithMethod(): void - { - $source = $this->build('interfaces()->first(); - $this->assertNotNull($firstInterface); - $this->assertEquals('hello', (string) $firstInterface->methods()->get('hello')->name()); - } - - public function testInterfaceWithMethodParameters(): void - { - $source = $this->build('interfaces()->first(); - $this->assertNotNull($firstInterface); - $this->assertEquals('hello', (string) $firstInterface->methods()->get('hello')->name()); - $this->assertEquals('world', (string) $firstInterface->methods()->get('hello')->parameters()->first()?->name()); - $this->assertEquals('foo', (string) $firstInterface->methods()->get('hello')->parameters()->get('foo')->name()); - } - - public function testDoesNotBuildPHP8PromotedProperties(): void - { - $source = $this->build('getFirstClass($source)->properties()->count()); - } - - private function getFirstClass(SourceCode $sourceCode): ClassPrototype - { - $class = $sourceCode->classes()->first(); - self::assertNotNull($class); - return $class; - } - - private function getFirstMethodInFirstClass(SourceCode $sourceCode): Method - { - $method = $this->getFirstClass($sourceCode)->methods()->first(); - self::assertNotNull($method); - - return $method; - } - - private function build(string $source): SourceCode - { - $reflector = ReflectorBuilder::create() - ->addMemberProvider(new DocblockMemberProvider()) - ->addSource($source)->build(); - - $worseFactory = new WorseBuilderFactory($reflector); - return $worseFactory->fromSource($source)->build(); - } -} diff --git a/lib/CodeBuilder/Tests/Functional/Adapter/TolerantParser/Util/NodeHelperTest.php b/lib/CodeBuilder/Tests/Functional/Adapter/TolerantParser/Util/NodeHelperTest.php deleted file mode 100644 index fe9d59d219..0000000000 --- a/lib/CodeBuilder/Tests/Functional/Adapter/TolerantParser/Util/NodeHelperTest.php +++ /dev/null @@ -1,39 +0,0 @@ -parser = new TolerantAstProvider(); - } - - public function testSelf(): void - { - [$methodNode, $nameNode] = $this->findSelfNode(); - $result = NodeHelper::resolvedShortName($methodNode, $nameNode); - $this->assertEquals('self', $result); - } - - /** - * @return array{Node, Node} - */ - private function findSelfNode(): array - { - [$source, $methodOffset, $nameOffset] = ExtractOffset::fromSource('oo(): sel<>f() { return $this; }}'); - $root = $this->parser->parseString($source); - return [ - $root->getDescendantNodeAtPosition($methodOffset), - $root->getDescendantNodeAtPosition($nameOffset), - ]; - } -} diff --git a/lib/CodeBuilder/Tests/IntegrationTestCase.php b/lib/CodeBuilder/Tests/IntegrationTestCase.php deleted file mode 100644 index 74466f5893..0000000000 --- a/lib/CodeBuilder/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,33 +0,0 @@ - [ - $filename - ]; - } - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Adapter/TolerantParser/Util/ImporterNamesTest.php b/lib/CodeBuilder/Tests/Unit/Adapter/TolerantParser/Util/ImporterNamesTest.php deleted file mode 100644 index 0ac2772aab..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Adapter/TolerantParser/Util/ImporterNamesTest.php +++ /dev/null @@ -1,52 +0,0 @@ -parse( - <<<'EOT' - assertEquals([], $iterator->classNames()); - } - - public function testReturnsFullyQualifiedNames(): void - { - $node = $this->parse( - <<<'EOT' - getDescendantNodes() as $node) { - } - - $iterator = new ImportedNames($node); - $this->assertEquals(['Foobar', 'Barfoo\Barfoo'], $iterator->classNames()); - } - - private function parse(string $source): Node - { - $parser = new Parser(); - return $parser->parseSourceFile($source); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Adapter/TolerantParser/Util/NodeHelperTest.php b/lib/CodeBuilder/Tests/Unit/Adapter/TolerantParser/Util/NodeHelperTest.php deleted file mode 100644 index 7c3ada7303..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Adapter/TolerantParser/Util/NodeHelperTest.php +++ /dev/null @@ -1,55 +0,0 @@ -parseSourceFile($source)->getDescendantNodeAtPosition($offset); - self::assertEquals($expectedLines, NodeHelper::emptyLinesPrecedingNode($node)); - } - - /** - * @return Generator - */ - public static function provideEmptyLinesPrecedingNode(): Generator - { - yield [ - "obar();", - 0 - ]; - - yield [ - "obar();", - 1 - ]; - - yield [ - "obar();", - 2 - ]; - - yield [ - <<<'EOT' - class Foobar - { - } - EOT - - , 1 - ]; - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Adapter/Twig/ClassShortNameResolverTest.php b/lib/CodeBuilder/Tests/Unit/Adapter/Twig/ClassShortNameResolverTest.php deleted file mode 100644 index 8ce96bbcd6..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Adapter/Twig/ClassShortNameResolverTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertEquals( - 'TestPrototype.php.twig', - $resolver->resolveName(new TestPrototype()) - ); - } -} - -class TestPrototype extends Prototype -{ -} diff --git a/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/TypeRendererTestCase.php b/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/TypeRendererTestCase.php deleted file mode 100644 index 38192b6e54..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/TypeRendererTestCase.php +++ /dev/null @@ -1,22 +0,0 @@ -createRenderer())->render($type)); - } - - abstract public static function provideType(): Generator; - - abstract protected function createRenderer(): WorseTypeRenderer; -} diff --git a/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer74Test.php b/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer74Test.php deleted file mode 100644 index 3a07c67768..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer74Test.php +++ /dev/null @@ -1,60 +0,0 @@ -build()), - 'Closure', - ]; - yield [ - new CallableType(), - 'callable', - ]; - yield [ - new PseudoIterableType(), - 'iterable', - ]; - yield [ - new UnionType(new StringType(), new FalseType()), - '', - ]; - yield [ - new ObjectType(), - 'object', - ]; - } - - protected function createRenderer(): WorseTypeRenderer - { - return new WorseTypeRenderer74(); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer81Test.php b/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer81Test.php deleted file mode 100644 index f6d137a595..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Adapter/WorseReflection/TypeRenderer/WorseTypeRenderer81Test.php +++ /dev/null @@ -1,39 +0,0 @@ -expectException(InvalidBuilderException::class); - - $builder = $this->prophesize(NamedBuilder::class); - - SourceCodeBuilder::create() - ->class('One') - ->method('two') - ->add($builder->reveal()); - } - - public function testBuildingAConstructor(): void - { - $methodBuilder = SourceCodeBuilder::create() ->class('One') ->method('__construct'); - $methodBuilder->parameter('config')->visibility(Visibility::public()); - - $result = $methodBuilder->build(); - $this->assertSame((string) Visibility::public(), (string) $result->parameters()->first()->visibility()); - } - - public function testNoVisibilityForNormalMethods(): void - { - $this->expectExceptionMessage('Only constructors can have parameters with visibility. Current function: doStuff'); - $methodBuilder = SourceCodeBuilder::create()->class('One')->method('doStuff') - ->parameter('config')->visibility(Visibility::public()); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php deleted file mode 100644 index 85eafd0884..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php +++ /dev/null @@ -1,312 +0,0 @@ -builder(); - $setup($builder); - $assertion($builder); - - $builder->class('Hello')->method('goodbye'); - $this->assertTrue($builder->isModified(), 'method has been modified since last snapshot'); - } - - public function provideModificationTracking(): Generator - { - yield 'new builder is modified by default' => [ - function (SourceCodeBuilder $builder): void { - $builder->class('foobar'); - }, - function (SourceCodeBuilder $builder): void { - $this->assertTrue($builder->isModified()); - } - ]; - - yield 'is not modified after snapshot' => [ - function (SourceCodeBuilder $builder): void { - $builder->class('foobar'); - $builder->snapshot(); - }, - function (SourceCodeBuilder $builder): void { - $this->assertFalse($builder->isModified()); - } - ]; - - yield 'is not modified if updated values are the same 1' => [ - function (SourceCodeBuilder $builder): void { - $builder->class('foobar')->method('foobar')->parameter('barfoo'); - $builder->snapshot(); - $builder->class('foobar')->method('foobar')->parameter('barfoo'); - }, - function (SourceCodeBuilder $builder): void { - $this->assertFalse($builder->isModified()); - } - ]; - - yield 'is not modified if updated values are the same 2' => [ - function (SourceCodeBuilder $builder): void { - $builder->class('foobar')->method('foobar'); - $builder->snapshot(); - $builder->class('foobar')->method('foobar'); - }, - function (SourceCodeBuilder $builder): void { - $this->assertFalse($builder->isModified()); - } - ]; - - yield 'is modified when values are different 1' => [ - function (SourceCodeBuilder $builder): void { - $builder->class('foobar')->method('foobar'); - $builder->snapshot(); - $builder->class('foobar')->method('barbarr'); - }, - function (SourceCodeBuilder $builder): void { - $this->assertTrue($builder->isModified()); - } - ]; - - yield 'is modified when values are different 2' => [ - function (SourceCodeBuilder $builder): void { - $builder->class('foobar')->method('foobar')->parameter('barbar'); - $builder->snapshot(); - $builder->class('foobar')->method('barbar')->parameter('fofo'); - }, - function (SourceCodeBuilder $builder): void { - $this->assertTrue($builder->isModified()); - } - ]; - } - - public function testSourceCodeBuilderUse(): void - { - $builder = $this->builder(); - $builder->namespace('Barfoo'); - $builder->use('Foobar'); - $builder->use('Foobar'); - $builder->use('Barfoo'); - $builder->class('Hello'); - $builder->trait('Goodbye'); - - $code = $builder->build(); - - $this->assertInstanceOf(SourceCode::class, $code); - $this->assertEquals('Barfoo', $code->namespace()->__toString()); - $this->assertCount(2, $code->useStatements()); - $this->assertEquals('Barfoo', $code->useStatements()->sorted()->first()->__toString()); - $this->assertEquals('Foobar', $code->useStatements()->first()->__toString()); - $this->assertEquals('Hello', $code->classes()->first()->name()); - $this->assertEquals('Goodbye', $code->traits()->first()->name()); - } - - public function testFunctionUse(): void - { - $builder = $this->builder(); - $builder->useFunction('hello'); - $builder->useFunction('hello\goodbye'); - $code = $builder->build(); - - $this->assertCount(2, $code->useStatements()); - $this->assertEquals('hello', $code->useStatements()->first()->__toString()); - $this->assertEquals(UseStatement::TYPE_FUNCTION, $code->useStatements()->first()->type()); - } - - public function testClassBuilder(): void - { - $builder = $this->builder(); - $classBuilder = $builder->class('Dog') - ->extends('Canine') - ->implements('Teeth') - ->property('one')->end() - ->property('two')->end() - ->method('method1')->end() - ->method('method2')->end(); - - $class = $classBuilder->build(); - - $this->assertSame($classBuilder, $builder->class('Dog')); - $this->assertEquals('Canine', $class->extendsClass()->__toString()); - $this->assertEquals('Teeth', $class->implementsInterfaces()->first()); - $this->assertEquals('one', $class->properties()->first()->name()); - $this->assertEquals('method1', $class->methods()->first()->name()); - } - - public function testClassBuilderAddMethodBuilder(): void - { - $builder = $this->builder(); - $methodBuilder = $this->builder()->class('Cat')->method('Whiskers'); - $classBuilder = $builder->class('Dog'); - $classBuilder->add($methodBuilder); - - $this->assertSame($classBuilder->method('Whiskers'), $methodBuilder); - } - - public function testClassBuilderAddPropertyBuilder(): void - { - $builder = $this->builder(); - $propertyBuilder = $this->builder()->class('Cat')->property('whiskers'); - $classBuilder = $builder->class('Dog'); - $classBuilder->add($propertyBuilder); - - $this->assertSame($classBuilder->property('whiskers'), $propertyBuilder); - } - - public function testInterfaceBuilder(): void - { - $builder = $this->builder(); - $interfaceBuilder = $builder->interface('Dog') - ->extends('Canine') - ->method('method1')->end() - ->method('method2')->end(); - - $class = $interfaceBuilder->build(); - - $this->assertSame($interfaceBuilder, $builder->interface('Dog')); - } - - public function testTraitBuilder(): void - { - $builder = $this->builder(); - $traitBuilder = $builder->trait('Dog') - ->property('one')->end() - ->property('two')->end() - ->method('method1')->end() - ->method('method2')->end(); - - $trait = $traitBuilder->build(); - - $this->assertSame($traitBuilder, $builder->trait('Dog')); - $this->assertEquals('one', $trait->properties()->first()->name()); - $this->assertEquals('method1', $trait->methods()->first()->name()); - } - - public function testTraitBuilderAddMethodBuilder(): void - { - $builder = $this->builder(); - $methodBuilder = $this->builder()->trait('Cat')->method('Whiskers'); - $traitBuilder = $builder->trait('Dog'); - $traitBuilder->add($methodBuilder); - - $this->assertSame($traitBuilder->method('Whiskers'), $methodBuilder); - } - - public function testTraitBuilderAddPropertyBuilder(): void - { - $builder = $this->builder(); - $propertyBuilder = $this->builder()->trait('Cat')->property('whiskers'); - $traitBuilder = $builder->trait('Dog'); - $traitBuilder->add($propertyBuilder); - - $this->assertSame($traitBuilder->property('whiskers'), $propertyBuilder); - } - - public function testPropertyBuilder(): void - { - $builder = $this->builder(); - $propertyBuilder = $builder->class('Dog')->property('one') - ->type('string') - ->defaultValue(null); - - $property = $propertyBuilder->build(); - - $this->assertEquals('string', $property->type()->__toString()); - $this->assertEquals('null', $property->defaultValue()->export()); - $this->assertSame($propertyBuilder, $builder->class('Dog')->property('one')); - } - - public function testClassMethodBuilderAccess(): void - { - $builder = $this->builder(); - $methodBuilder = $builder->class('Bar')->method('foo'); - - $this->assertSame($methodBuilder, $builder->class('Bar')->method('foo')); - } - - public function testTraitMethodBuilderAccess(): void - { - $builder = $this->builder(); - $methodBuilder = $builder->trait('Bar')->method('foo'); - - $this->assertSame($methodBuilder, $builder->trait('Bar')->method('foo')); - } - - #[DataProvider('provideMethodBuilder')] - public function testMethodBuilder(MethodBuilder $methodBuilder, Closure $assertion): void - { - $builder = $this->builder(); - $method = $methodBuilder->build(); - $assertion($method); - } - - /** @return Generator */ - public function provideMethodBuilder(): Generator - { - yield 'Method return type' => [ - $this->builder()->class('Dog')->method('one') - ->returnType('?string') - ->visibility('private') - ->parameter('one') - ->type('One') - ->defaultValue(1) - ->end(), - function (Method $method): void { - $this->assertEquals('?string', $method->returnType()->__toString()); - } - ]; - - yield 'One method modifier' => [ - $this->builder()->class('Dog')->method('one')->static()->abstract(), - function ($method): void { - $this->assertTrue($method->isStatic()); - $this->assertTrue($method->isAbstract()); - } - ]; - yield 'Two method modifiers' => [ - $this->builder()->class('Dog')->method('one')->abstract(), - function ($method): void { - $this->assertFalse($method->isStatic()); - $this->assertTrue($method->isAbstract()); - } - ]; - yield 'Method lines' => [ - $this->builder()->class('Dog')->method('one')->body()->line('one')->line('two')->end(), - function ($method): void { - $this->assertCount(2, $method->body()->lines()); - $this->assertEquals('one', (string) $method->body()->lines()->first()); - } - ]; - yield 'Attributes' => [ - $this->builder()->class('Dog')->method('one')->attribute('Foobar', ['foo', 'bar']), - function (Method $method): void { - $this->assertCount(1, $method->attributes()); - } - ]; - } - - public function testParameterBuilder(): void - { - $builder = $this->builder(); - $method = $builder->class('Bar')->method('foo'); - $parameterBuilder = $method->parameter('foo'); - - $this->assertSame($parameterBuilder, $method->parameter('foo')); - } - - private function builder(): SourceCodeBuilder - { - return SourceCodeBuilder::create(); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/ClassesTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/ClassesTest.php deleted file mode 100644 index e861542ec3..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/ClassesTest.php +++ /dev/null @@ -1,21 +0,0 @@ -assertCount(2, iterator_to_array($classes)); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/CollectionTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/CollectionTest.php deleted file mode 100644 index 41b4f16355..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/CollectionTest.php +++ /dev/null @@ -1,38 +0,0 @@ -expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unknown test "foo", known items'); - $collection = TestCollection::fromArray([ - 'one' => new stdClass() - ]); - - $collection->get('foo'); - } -} - -/** - * @extends Collection - */ -class TestCollection extends Collection -{ - public static function fromArray(array $items) - { - return new self($items); - } - - protected function singularName(): string - { - return 'test'; - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/DefaultValueTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/DefaultValueTest.php deleted file mode 100644 index dfcde4fbf5..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/DefaultValueTest.php +++ /dev/null @@ -1,52 +0,0 @@ -assertEquals($expected, $value->export()); - } - - public static function provideExportValues(): Generator - { - yield 'escaped string' => [ - 'hello', - '\'hello\'', - ]; - yield 'Int' => [ - 1234, - '1234', - ]; - yield 'It returns lowercase null' => [ - null, - 'null', - ]; - yield 'It returns new array syntax' => [ - [], - '[]', - ]; - yield 'list 1' => [ - ['foobar'], - '["foobar"]', - ]; - yield 'list 2' => [ - ['foobar', 'bazbar'], - '["foobar", "bazbar"]', - ]; - yield 'array syntax 2' => [ - ['assoc' => 'foobar'], - '["assoc" => "foobar"]', - ]; - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/DocblockTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/DocblockTest.php deleted file mode 100644 index 11f882d3ba..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/DocblockTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertEquals([''], Docblock::fromString('')->asLines()); - $this->assertEquals(['One', 'Two'], Docblock::fromString( - <<<'EOT' - One - Two - EOT - )->asLines()); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/MethodTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/MethodTest.php deleted file mode 100644 index ea030d22fd..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/MethodTest.php +++ /dev/null @@ -1,32 +0,0 @@ -createMethodModifier(Method::IS_STATIC); - $this->assertTrue($method->isStatic()); - $this->assertFalse($method->isAbstract()); - - $method = $this->createMethodModifier(Method::IS_ABSTRACT); - $this->assertTrue($method->isAbstract()); - $this->assertFalse($method->isStatic()); - - $method = $this->createMethodModifier(Method::IS_ABSTRACT|Method::IS_STATIC); - $this->assertTrue($method->isAbstract()); - $this->assertTrue($method->isStatic()); - } - - private function createMethodModifier(int $modifier): Method - { - return new Method('test', modifierFlags: $modifier); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/SourceCodeTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/SourceCodeTest.php deleted file mode 100644 index 2be154e7c4..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/SourceCodeTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertSame($namespace, $code->namespace()); - $this->assertSame($useStatements, $code->useStatements()); - $this->assertSame($classes, $code->classes()); - $this->assertSame($interfaces, $code->interfaces()); - $this->assertSame($traits, $code->traits()); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/TraitsTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/TraitsTest.php deleted file mode 100644 index 5be04dee8f..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/TraitsTest.php +++ /dev/null @@ -1,21 +0,0 @@ -assertCount(2, iterator_to_array($traits)); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/TypeTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/TypeTest.php deleted file mode 100644 index 3c4bff8235..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/TypeTest.php +++ /dev/null @@ -1,59 +0,0 @@ -assertEquals($expectedNamespace, $type->namespace()); - } - - /** - * @return Generator - */ - public static function provideNamespace(): Generator - { - yield [ - 'Foo\\Bar', - 'Foo', - ]; - - yield [ - 'Foo\\Bar\\Zoo', - 'Foo\\Bar', - ]; - - yield [ - 'Foo\\Bar\\Zoo\\Zog', - 'Foo\\Bar\\Zoo', - ]; - - yield [ - '?Foo\\Bar', - 'Foo', - ]; - - yield [ - '?Bar', - null - ]; - - yield [ - 'Bar', - null - ]; - - yield [ - '', - null - ]; - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/VisibilityTest.php b/lib/CodeBuilder/Tests/Unit/Domain/Prototype/VisibilityTest.php deleted file mode 100644 index c1d00c9a81..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/Prototype/VisibilityTest.php +++ /dev/null @@ -1,19 +0,0 @@ -expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid visibility'); - Visibility::fromString('foobar'); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/FilterPhpVersionDirectoryIteratorTest.php b/lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/FilterPhpVersionDirectoryIteratorTest.php deleted file mode 100644 index a66615a725..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/FilterPhpVersionDirectoryIteratorTest.php +++ /dev/null @@ -1,65 +0,0 @@ -assertEqualsCanonicalizing( - $expectedFilteredDirectories, - $filteredDirectories - ); - } - - public function provideDirectoriesToFilter(): iterable - { - $directories = new ArrayIterator([ - $this->createFakeFile('a-file'), - $php72 = $this->createFakeDirectory('7.2'), - $this->createFakeDirectory('a-directory'), - $php74 = $this->createFakeDirectory('7.4'), - $php74Special = $this->createFakeDirectory('7.4-special'), - ]); - - yield 'For PHP 7.3' => [$directories, '7.3.3', [$php72]]; - yield 'For PHP 7.4' => [$directories, '7.4.0', [$php72, $php74, $php74Special]]; - } - - private function createFakeFile(string $filename): SplFileInfo - { - return $this->createFakeSplFileInfo($filename, false); - } - - private function createFakeDirectory(string $filename): SplFileInfo - { - return $this->createFakeSplFileInfo($filename, true); - } - - private function createFakeSplFileInfo(string $filename, bool $isDir = false): SplFileInfo - { - $file = $this->prophesize(\Symfony\Component\Finder\SplFileInfo::class); - $file->getFilename()->willReturn($filename); - $file->isDir()->willReturn($isDir); - - return $file->reveal(); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php b/lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php deleted file mode 100644 index 54071dff8f..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php +++ /dev/null @@ -1,104 +0,0 @@ -workspace()->reset(); - } - - #[DataProvider('provideResolvePaths')] - public function testResolvePaths( - string $phpVersion, - array $fullTemplatePaths, - array $templatePaths, - array $expectedPaths - ): void { - foreach ($fullTemplatePaths as $fullTemplatePath) { - $this->workspace()->mkdir($fullTemplatePath); - } - - $resolver = new PhpVersionPathResolver($phpVersion); - self::assertEquals(array_map(function (string $path) { - return $this->workspace()->path($path); - }, $expectedPaths), $resolver->resolve(array_map(function (string $path) { - return $this->workspace()->path($path); - }, $templatePaths))); - } - - public static function provideResolvePaths(): Generator - { - yield 'none' => [ - '5.6', - [], - [], - [] - ]; - - yield 'resolves given path but not the version path' => [ - '7.0', - [ - '/path1/7.1' - ], - [ - '/path1' - ], - [ - '/path1', - ] - ]; - - yield 'priorities the version path if the version matches' => [ - '7.1', - [ - '/path1/7.1' - ], - [ - '/path1' - ], - [ - '/path1/7.1', - '/path1', - ] - ]; - - yield 'returns previous versions for higher versions' => [ - '7.2', - [ - '/path1/7.1' - ], - [ - '/path1' - ], - [ - '/path1/7.1', - '/path1', - ] - ]; - - yield 'returns multiple previous versions' => [ - '7.2', - [ - '/path1/7.0', - '/path1/7.1', - '/path1/7.2' - ], - [ - '/path1' - ], - [ - '/path1/7.2', - '/path1/7.1', - '/path1/7.0', - '/path1', - ] - ]; - } -} diff --git a/lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php b/lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php deleted file mode 100644 index c9a7e5ce12..0000000000 --- a/lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php +++ /dev/null @@ -1,64 +0,0 @@ - - */ - private ObjectProphecy $updater; - - private $builder; - - private $generator; - - private $prototype; - - protected function setUp(): void - { - $this->generator = $this->prophesize(Renderer::class); - $this->updater = $this->prophesize(Updater::class); - $this->builder = new SourceBuilder( - $this->generator->reveal(), - $this->updater->reveal() - ); - $this->prototype = $this->prophesize(Prototype\Prototype::class); - } - - /** - * @testdoc It should delegate to the generator. - */ - public function testGenerate(): void - { - $expectedCode = TextDocumentBuilder::fromString(''); - $this->generator->render($this->prototype->reveal())->willReturn($expectedCode); - $code = $this->builder->render($this->prototype->reveal()); - - $this->assertSame($expectedCode, $code); - } - - /** - * @testdoc It should delegate to the updater. - */ - public function testUpdate(): void - { - $sourceCode = TextDocumentBuilder::fromString(''); - $this->updater->textEditsFor($this->prototype->reveal(), $sourceCode)->willReturn(TextEdits::none()); - $code = $this->builder->apply($this->prototype->reveal(), $sourceCode); - - $this->assertEquals('', $code); - } -} diff --git a/lib/CodeBuilder/Tests/Unit/Util/TextFormatTest.php b/lib/CodeBuilder/Tests/Unit/Util/TextFormatTest.php deleted file mode 100644 index f184e4a1e0..0000000000 --- a/lib/CodeBuilder/Tests/Unit/Util/TextFormatTest.php +++ /dev/null @@ -1,165 +0,0 @@ -indentRemove($text)); - } - - /** - * @return Generator - */ - public static function provideRemoveIndentation(): Generator - { - yield 'empty' => [ - '', - '' - ]; - - yield 'uniform' => [ - <<<'EOT' - asd - asd - asd - EOT - , - <<<'EOT' - asd - asd - asd - EOT - ]; - - yield 'tabs' => [ - << [ - <<<'EOT' - asd - asd - asd - EOT - , - <<<'EOT' - asd - asd - asd - EOT - ]; - - yield 'code' => [ - <<<'EOT' - class Foo - { - public function bar() - { - echo $hello; - } - } - EOT - , - <<<'EOT' - class Foo - { - public function bar() - { - echo $hello; - } - } - EOT - ]; - - yield 'preserve new line' => [ - <<<'EOT' - - class Foo - { - public function bar() - { - echo $hello; - } - } - EOT - , - <<<'EOT' - - class Foo - { - public function bar() - { - echo $hello; - } - } - EOT - ]; - } - - #[DataProvider('provideIndent')] - public function testIndent(string $text, int $level, string $expected): void - { - self::assertEquals($expected, (new TextFormat())->indent($text, $level)); - } - - /** - * @return Generator - */ - public static function provideIndent(): Generator - { - yield 'empty' => [ - '', - 0, - '' - ]; - - yield 'exmaple 1' => [ - <<<'EOT' - private $bar; - EOT - , - 1, - <<<'EOT' - private $bar; - EOT - ]; - } - - public function testIndentExceptionIfLevelLessThan0(): void - { - $this->expectException(RuntimeException::class); - (new TextFormat())->indent('foobar', -1); - } - - public function testReplacesIndentation(): void - { - $this->assertEquals(<<<'EOT' - foo - bar - EOT - , (new TextFormat())->indentReplace(<<<'EOT' - foo - bar - EOT - , 1)); - } -} diff --git a/lib/CodeBuilder/Util/TextFormat.php b/lib/CodeBuilder/Util/TextFormat.php deleted file mode 100644 index 5420576187..0000000000 --- a/lib/CodeBuilder/Util/TextFormat.php +++ /dev/null @@ -1,40 +0,0 @@ -indentation, $level) . $line; - }, $lines); - - return implode($this->newLineChar, $lines); - } - - public function indentRemove(string $text): string - { - return preg_replace("/^[ \t]+/m", '', $text); - } - - public function indentReplace($text, int $level): string - { - return $this->indent($this->indentRemove($text), $level); - } -} diff --git a/lib/CodeBuilder/Util/TextUtil.php b/lib/CodeBuilder/Util/TextUtil.php deleted file mode 100644 index f7682454ab..0000000000 --- a/lib/CodeBuilder/Util/TextUtil.php +++ /dev/null @@ -1,11 +0,0 @@ -parser->parse($docblockText); - - if (!$docblock instanceof Docblock) { - return $docblockText; - } - - return TextEdits::fromTextEdits($this->edits($docblock, $prototype))->apply($docblockText); - } - - /** - * @return array - */ - private function edits(Docblock $docblock, TagPrototype $prototype): array - { - if ($prototype instanceof ReturnTagPrototype) { - return $this->updateTag( - $docblock, - $prototype, - sprintf('@return %s', $prototype->type->__toString()) - ); - } - - if ($prototype instanceof ParamTagPrototype) { - return $this->updateTag( - $docblock, - $prototype, - sprintf('@param %s $%s', $prototype->type->__toString(), $prototype->name) - ); - } - - if ($prototype instanceof ExtendsTagPrototype) { - return $this->updateTag( - $docblock, - $prototype, - sprintf('@extends %s', $prototype->type->short()), - 0 - ); - } - - if ($prototype instanceof ImplementsTagPrototype) { - return $this->updateTag( - $docblock, - $prototype, - sprintf('@implements %s', $prototype->type->short()), - 0 - ); - } - - throw new RuntimeException(sprintf( - 'Do not know how to update tag "%s"', - get_class($prototype) - )); - } - - /** - * @return array - */ - private function updateTag(Docblock $docblock, TagPrototype $prototype, string $tagText, int $indent = 1): array - { - // create - if (strlen(trim($docblock->toString())) === 0) { - $indent = $this->textFormat->indent('', $indent); - return [ - TextEdit::create( - $docblock->start(), - 0, - sprintf( - "\n%s/**\n%s * %s\n%s */\n%s", - $indent, - $indent, - $tagText, - $indent, - $indent, - ), - ) - ]; - } - - // update - $edits = []; - foreach ($docblock->tags() as $tag) { - if ($prototype->matches($tag)) { - $edits[] = - TextEdit::create( - $tag->start(), - $prototype->endOffsetFor($tag) - $tag->start(), - $tagText - ); - } - } - - if ($edits) { - return $edits; - } - - if ($line = $docblock->lastMultilineContentToken()) { - return [ - TextEdit::create( - $line->end(), - 0, - sprintf( - "* %s\n%s", - $tagText, - str_repeat(' ', $docblock->indentationLevel()), - ), - ) - ]; - } - - if ($open = $docblock->phpDocOpen()) { - if (!str_contains($docblock->toString(), "\n")) { - return [ - TextEdit::create( - $open->end(), - 0, - sprintf( - ' %s', - $tagText - ), - ) - ]; - } - return [ - TextEdit::create( - $open->end(), - 0, - sprintf( - "\n%s* %s", - str_repeat(' ', $docblock->indentationLevel()), - $tagText - ), - ) - ]; - } - - return []; - } -} diff --git a/lib/CodeTransform/Adapter/Native/GenerateNew/ClassGenerator.php b/lib/CodeTransform/Adapter/Native/GenerateNew/ClassGenerator.php deleted file mode 100644 index 35f19e94ad..0000000000 --- a/lib/CodeTransform/Adapter/Native/GenerateNew/ClassGenerator.php +++ /dev/null @@ -1,30 +0,0 @@ -namespace($targetName->namespace()); - $classPrototype = $builder->class($targetName->short()); - - return SourceCode::fromString( - (string) $this->renderer->render($builder->build(), $this->variant) - ); - } -} diff --git a/lib/CodeTransform/Adapter/TolerantParser/ClassToFile/Transformer/ClassNameFixerTransformer.php b/lib/CodeTransform/Adapter/TolerantParser/ClassToFile/Transformer/ClassNameFixerTransformer.php deleted file mode 100644 index 80554a1c6b..0000000000 --- a/lib/CodeTransform/Adapter/TolerantParser/ClassToFile/Transformer/ClassNameFixerTransformer.php +++ /dev/null @@ -1,207 +0,0 @@ - - */ - public function transform(SourceCode $code): Promise - { - if ($code->uri()->scheme() !== 'file') { - throw new TransformException(sprintf('Source is not a file:// it is "%s"', $code->uri()->scheme())); - } - $classFqn = $this->determineClassFqn($code); - $correctClassName = $classFqn->name(); - $correctNamespace = $classFqn->namespace(); - - $rootNode = $this->parser->get($code); - $edits = []; - - if ($textEdit = $this->fixNamespace($rootNode, $correctNamespace)) { - $edits[] = $textEdit; - } - - if ($textEdit = $this->fixClassName($rootNode, $correctClassName)) { - $edits[] = $textEdit; - } - - return new Success(TextEdits::fromTextEdits($edits)); - } - - - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - if ($code->uri()->scheme() !== 'file') { - return new Success(Diagnostics::none()); - } - $rootNode = $this->parser->get($code); - try { - $classFqn = $this->determineClassFqn($code); - } catch (RuntimeException) { - return new Success(Diagnostics::none()); - } - $correctClassName = $classFqn->name(); - $correctNamespace = $classFqn->namespace(); - - $diagnostics = []; - - if (null !== $this->fixNamespace($rootNode, $correctNamespace)) { - $namespaceDefinition = $rootNode->getFirstDescendantNode(NamespaceDefinition::class); - $diagnostics[] = new Diagnostic( - ByteOffsetRange::fromInts( - $namespaceDefinition ? $namespaceDefinition->getStartPosition() : 0, - $namespaceDefinition ? $namespaceDefinition->getEndPosition() : 0, - ), - sprintf('Namespace should probably be "%s"', $correctNamespace), - Diagnostic::WARNING - ); - } - if (null !== $edits = $this->fixClassName($rootNode, $correctClassName)) { - $classLike = $rootNode->getFirstDescendantNode(ClassLike::class); - $nameToken = $this->nameToken($classLike); - - if ($nameToken) { - $diagnostics[] = new Diagnostic( - ByteOffsetRange::fromInts( - $nameToken->getStartPosition(), - $nameToken->getEndPosition(), - ), - sprintf('Class name should probably be "%s"', $correctClassName), - Diagnostic::WARNING - ); - } - } - - return new Success(new Diagnostics($diagnostics)); - } - - - private function fixClassName(SourceFileNode $rootNode, string $correctClassName): ?TextEdit - { - $classLike = $rootNode->getFirstDescendantNode(ClassLike::class); - - if (null === $classLike) { - return null; - } - - assert($classLike instanceof EnumDeclaration || $classLike instanceof ClassDeclaration || $classLike instanceof InterfaceDeclaration || $classLike instanceof TraitDeclaration); - - $name = $classLike->name->getText($rootNode->getFileContents()); - - if (!is_string($name) || $name === $correctClassName) { - return null; - } - - return TextEdit::create($classLike->name->start, strlen($name), $correctClassName); - } - - private function fixNamespace(SourceFileNode $rootNode, string $correctNamespace): ?TextEdit - { - $namespaceDefinition = $rootNode->getFirstDescendantNode(NamespaceDefinition::class); - assert($namespaceDefinition instanceof NamespaceDefinition || is_null($namespaceDefinition)); - $statement = sprintf('namespace %s;', $correctNamespace); - - if ($correctNamespace && null === $namespaceDefinition) { - $scriptStart = $rootNode->getFirstDescendantNode(InlineHtml::class); - $scriptStart = $scriptStart ? $scriptStart->getEndPosition() : 0; - - $statement = "\n" . $statement . "\n"; - - if (0 === $scriptStart) { - $statement = 'name instanceof QualifiedName) { - if ($namespaceDefinition->name->__toString() === $correctNamespace) { - return null; - } - } - - return TextEdit::create( - $namespaceDefinition->getStartPosition(), - $namespaceDefinition->getEndPosition() - $namespaceDefinition->getStartPosition(), - $statement - ); - } - - private function determineClassFqn(SourceCode $code): ClassName - { - if (!$code->uri()->path()) { - throw new RuntimeException('Source code has no path associated with it'); - } - - $candidates = $this->fileToClass->fileToClassCandidates( - FilePath::fromString((string) $code->uri()->path()) - ); - - $classFqn = $candidates->best(); - - return $classFqn; - } - - private function nameToken(?Node $classLike): ?Token - { - if (null === $classLike) { - return null; - } - - if (!property_exists($classLike, 'name')) { - return null; - } - - $name = $classLike->name; - - if (!$name instanceof Token) { - return null; - } - - return $name; - } -} diff --git a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantChangeVisiblity.php b/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantChangeVisiblity.php deleted file mode 100644 index ed217e2c0a..0000000000 --- a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantChangeVisiblity.php +++ /dev/null @@ -1,87 +0,0 @@ -parser->get($source); - $node = $node->getDescendantNodeAtPosition($offset); - - $node = $this->resolveMemberNode($node); - - if (null === $node) { - return $source; - } - - /** @phpstan-ignore-next-line */ - $textEdit = $this->resolveNewVisiblityTextEdit($node); - - if (null === $textEdit) { - return $source; - } - - return $source->withSource(TextEdits::one($textEdit)->apply($source)); - } - - /** - * @param MethodDeclaration|PropertyDeclaration|ClassConstDeclaration $node - */ - private function resolveNewVisiblityTextEdit(Node $node): ?TextEdit - { - foreach ($node->modifiers as $modifier) { - if ($modifier->kind === TokenKind::PublicKeyword) { - return $this->visiblityTextEdit($modifier, 'protected'); - } - - if ($modifier->kind === TokenKind::ProtectedKeyword) { - return $this->visiblityTextEdit($modifier, 'private'); - } - - if ($modifier->kind === TokenKind::PrivateKeyword) { - return $this->visiblityTextEdit($modifier, 'public'); - } - } - - return null; - } - - private function visiblityTextEdit(Token $modifier, string $newVisiblity): TextEdit - { - return TextEdit::create($modifier->getStartPosition(), $modifier->getWidth(), $newVisiblity); - } - - private function resolveMemberNode(Node $node): ?Node - { - if (!( - $node instanceof MethodDeclaration || - $node instanceof PropertyDeclaration || - $node instanceof ClassConstDeclaration - )) { - $node = $node->getFirstAncestor( - MethodDeclaration::class, - PropertyDeclaration::class, - ClassConstDeclaration::class - ); - } - return $node; - } -} diff --git a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantExtractExpression.php b/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantExtractExpression.php deleted file mode 100644 index 90b26b5582..0000000000 --- a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantExtractExpression.php +++ /dev/null @@ -1,178 +0,0 @@ -getExtractedExpression($source, $offsetStart, $offsetEnd) !== null; - } - - public function extractExpression(SourceCode $source, int $offsetStart, ?int $offsetEnd, string $variableName): TextEdits - { - $expression = $this->getExtractedExpression($source, $offsetStart, $offsetEnd); - if ($expression === null) { - return TextEdits::none(); - } - - $startPosition = $expression->getStartPosition(); - $endPosition = $expression->getEndPosition(); - - $extractedString = rtrim(trim($source->extractSelection($startPosition, $endPosition)), ';'); - $assigment = sprintf('$%s = %s;', $variableName, $extractedString) . "\n"; - - $statement = $expression->getFirstAncestor(StatementNode::class); - assert($statement instanceof StatementNode); - - $edits = $this->resolveEdits($statement, $expression, $extractedString, $assigment, $variableName); - - return TextEdits::fromTextEdits($edits); - } - - private function getExtractedExpression(SourceCode $source, int $offsetStart, ?int $offsetEnd): ?Expression - { - // only apply to selections - if ($offsetStart === $offsetEnd) { - return null; - } - $rootNode = $this->parser->get($source); - $startNode = $rootNode->getDescendantNodeAtPosition($offsetStart); - - if ($offsetEnd) { - $endNode = $rootNode->getDescendantNodeAtPosition($offsetEnd); - $expression = $this->getCommonExpression($startNode, $endNode); - - if ($expression === null && $endNode instanceof ExpressionStatement) { - // := ; - // check if $endNode does not contain the semi-colon - // then find the last child expression that ends at the semi-colon - assert($endNode instanceof ExpressionStatement); - $expressions = array_filter( - iterator_to_array($endNode->getDescendantNodes(), false), - function (Node $item) use ($endNode) { - return - $item instanceof Expression && - $item->getEndPosition() == $endNode->expression->getEndPosition(); - } - ); - - if ($expressions === []) { - return null; - } - - $expression = $this->getCommonExpression($startNode, end($expressions)); - } - } else { - $expression = $this->outerExpression($startNode); - } - - if ($expression === null) { - return null; - } - - return $expression; - } - - private function getCommonExpression(Node $node1, Node $node2): ?Expression - { - if ($node1 === $node2 && $node1 instanceof Expression) { - return $node1; - } - $ancestor = $node1; - $expressions = []; - if ($node1 instanceof Expression) { - $expressions[] = $node1; - } - - while (($ancestor = $ancestor->parent) !== null) { - if ($ancestor instanceof FunctionLike) { - break; - } - if ($ancestor instanceof Expression === false) { - continue; - } - $expressions[] = $ancestor; - } - - if (empty($expressions)) { - return null; - } - - $ancestor = $node2; - if (in_array($ancestor, $expressions, true)) { - return $ancestor; - } - while (($ancestor = $ancestor->parent) !== null) { - if (in_array($ancestor, $expressions, true)) { - return $ancestor; - } - } - - return null; - } - - /** - * @return array - */ - private function resolveEdits( - Node $statement, - Node $expression, - string $extractedString, - string $assignment, - string $variableName - ): array { - if ($statement instanceof ExpressionStatement && $statement->expression === $expression) { - return [ - TextEdit::create($statement->getStartPosition(), $statement->getWidth(), $assignment) - ]; - } - - $matches = []; - $indentation = ''; - if (preg_match('/(\t| )*$/', $statement->getLeadingCommentAndWhitespaceText(), $matches) > 0) { - $indentation = $matches[0]; - } - - return [ - TextEdit::create($statement->getStartPosition(), 0, $assignment . $indentation), - TextEdit::create($expression->getStartPosition(), strlen($extractedString), '$' . $variableName), - ]; - } - - private function outerExpression(Node $node, ?Node $originalNode = null): ?Expression - { - $originalNode = $originalNode ?: $node; - - $parent = $node->getParent(); - - if (null === $parent) { - return $node instanceof Expression ? $node : null; - } - - if ($parent->getStartPosition() !== $originalNode->getStartPosition() && $originalNode instanceof Expression) { - return $originalNode; - } - - return $this->outerExpression($parent, $originalNode); - } -} diff --git a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantHereDoc.php b/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantHereDoc.php deleted file mode 100644 index 383e51c498..0000000000 --- a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantHereDoc.php +++ /dev/null @@ -1,74 +0,0 @@ -parser - ->get($document) - ->getDescendantNodeAtPosition($offset->toInt()) - ; - - // If we're inside a variable inside a string literal, get the surrounding string - if (!$node instanceof StringLiteral) { - $node = $node->getFirstAncestor(StringLiteral::class); - } - - if (!$node instanceof StringLiteral) { - return TextEdits::none(); - } - - if ($node->startQuote instanceof Token && $node->startQuote->kind === TokenKind::HeredocStart) { - if ($node->endQuote instanceof MissingToken) { - return TextEdits::none(); - } - return $this->convertFromHereDocToString($node); - } - - return $this->convertFromStringToHereDoc($node); - } - - private function convertFromStringToHereDoc(StringLiteral $node): TextEdits - { - // Trimming the quotes - $content = $node->getStringContentsText(); - - return TextEdits::fromTextEdits([TextEdit::create( - $node->getStartPosition(), - $node->getEndPosition() - $node->getStartPosition(), - '<<getStringContentsText()); - $hereDocContent = str_replace('"', '\\"', $hereDocContent); - - return TextEdits::fromTextEdits([TextEdit::create( - $node->getStartPosition(), - $node->getEndPosition() - $node->getStartPosition(), - '"'.$hereDocContent.'"', - )]); - } -} diff --git a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantImportName.php b/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantImportName.php deleted file mode 100644 index 2d52c3293b..0000000000 --- a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantImportName.php +++ /dev/null @@ -1,237 +0,0 @@ -isGlobalFunction($nameImport)) { - return TextEdits::none(); - } - - $sourceNode = $this->parser->get($source); - $node = $this->getLastNodeAtPosition($sourceNode, $offset); - - $this->assertNotAlreadyImported($node, $nameImport); - - $edits = $this->addImport($source, $nameImport); - - if ($nameImport->alias() !== null) { - $edits = $this->updateReferences($node, $nameImport, $edits); - } - - return $edits; - } - - public function importNameOnly(SourceCode $source, ByteOffset $offset, NameImport $nameImport): TextEdits - { - if ($this->isGlobalFunction($nameImport)) { - return TextEdits::none(); - } - - $sourceNode = $this->parser->get($source); - $node = $this->getLastNodeAtPosition($sourceNode, $offset); - - $this->assertNotAlreadyImported($node, $nameImport); - - return $this->addImport($source, $nameImport); - } - - private function assertNotAlreadyImported(Node $node, NameImport $nameImport): void - { - $currentClass = $this->currentClass($node); - $imports = $node->getImportTablesForCurrentScope()[$this->resolveImportTableOffset($nameImport)]; - - [ $existingName, $existingImport ] = $this->findExistingImport($nameImport, $imports); - if (null === $nameImport->alias() && $existingImport !== null) { - throw new NameAlreadyImportedException( - $nameImport, - $existingName, - $existingImport->getFullyQualifiedNameText() - ); - } - - if (null === $nameImport->alias() && $currentClass && $currentClass->short() === $nameImport->name()->head()->__toString()) { - throw new NameAlreadyImportedException($nameImport, $currentClass->short(), $currentClass->__toString()); - } - - if ($nameImport->alias() && isset($imports[$nameImport->alias()])) { - throw new AliasAlreadyUsedException($nameImport); - } - - if ($nameImport->isClass() && $this->currentClassIsSameAsImportClass($node, $nameImport->name())) { - throw new ClassIsCurrentClassException($nameImport); - } - - if ($this->importClassInSameNamespace($node, $nameImport->name())) { - throw new NameAlreadyInNamespaceException($nameImport); - } - } - - /** - * @param array $imports - */ - private function findExistingImport(NameImport $nameImport, array $imports): ?array - { - $nameImportParts = $nameImport->name()->toArray(); - - foreach ($imports as $name => $import) { - if ($import->getNameParts() === $nameImportParts) { - // fqn already used in imports - return [$name, $import]; - } - } - - $shortName = $nameImport->name()->head()->__toString(); - if (array_key_exists($shortName, $imports)) { - // short name already used in imports - return [$shortName, $imports[$shortName]]; - } - - return null; - } - - private function currentClassIsSameAsImportClass(Node $node, FullyQualifiedName $className): bool - { - if (!$node instanceof ClassLike || !$node instanceof NamespacedNameInterface) { - return false; - } - - if ((string) $node->getNamespacedName() === (string) $className) { - return true; - } - - return false; - } - - private function addImport(SourceCode $source, NameImport $nameImport): TextEdits - { - $builder = SourceCodeBuilder::create(); - - $this->addUse($builder, $nameImport); - $prototype = $builder->build(); - - return $this->updater->textEditsFor($prototype, $source); - } - - private function importClassInSameNamespace(Node $node, FullyQualifiedName $className): bool - { - $namespace = ''; - if ($definition = $node->getNamespaceDefinition()) { - $namespace = (string) $definition->getFirstChildNode(QualifiedName::class); - } - - if ($className->count() > 1 && $className->tail()->__toString() == $namespace) { - return true; - } - - return false; - } - - private function updateReferences(Node $node, NameImport $nameImport, TextEdits $edits): TextEdits - { - $alias = $nameImport->alias(); - - if (is_null($alias)) { - return $edits; - } - - return $edits->add(TextEdit::create( - $node->getStartPosition(), - $node->getEndPosition() - $node->getStartPosition(), - $alias - )); - } - - private function currentClass(Node $node): ?ClassName - { - $classDeclaration = $node->getFirstAncestor(ClassLike::class); - - if (!$classDeclaration instanceof NamespacedNameInterface) { - return null; - } - - - $name = (string)$classDeclaration->getNamespacedName(); - - if (!$name) { - return null; - } - - return ClassName::fromString($name); - } - - private function resolveImportTableOffset(NameImport $nameImport): int - { - return $nameImport->isFunction() ? 1 : 0; - } - - private function addUse(SourceCodeBuilder $builder, NameImport $nameImport): void - { - if ($nameImport->isFunction()) { - $builder->useFunction($nameImport->name()->__toString(), $nameImport->alias()); - return; - } - - $builder->use($nameImport->name()->__toString(), $nameImport->alias()); - } - - private function getLastNodeAtPosition(SourceFileNode $sourceNode, ByteOffset $offset): Node - { - $node = $sourceNode->getDescendantNodeAtPosition($offset->toInt()); - - /* - * In case the cursor is not on a recognized node we need to find the - * first available node after the cusror in order to make sure the - * import table will be loaded. - */ - if ($node instanceof SourceFileNode) { - /** @var Node $childNode */ - foreach ($node->getChildNodes() as $childNode) { - if ($childNode->getStartPosition() > $offset->toInt()) { - break; - } - - $node = $childNode; - } - } - - return $node; - } - - private function isGlobalFunction(NameImport $nameImport): bool - { - return $this->importGlobals === false && $nameImport->isFunction() && $nameImport->name()->count() === 1; - } -} diff --git a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantRenameVariable.php b/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantRenameVariable.php deleted file mode 100644 index 4e975cc001..0000000000 --- a/lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantRenameVariable.php +++ /dev/null @@ -1,139 +0,0 @@ -parser->get($sourceCode); - $variable = $this->variableNodeFromSource($sourceNode, $offset); - $scopeNode = $this->scopeNode($variable, $scope); - $textEdits = $this->textEditsToRename($scopeNode, $variable, $newName); - - return $sourceCode->withSource(TextEdits::fromTextEdits($textEdits)->apply($sourceCode->__toString())); - } - - private function variableNodeFromSource(SourceFileNode $sourceNode, int $offset): Node - { - $node = $sourceNode->getDescendantNodeAtPosition($offset); - - if ( - false === $node instanceof Variable && - false === $node instanceof UseVariableName && - false === $node instanceof Parameter - ) { - throw new TransformException(sprintf( - 'Expected Variable or Parameter node, got "%s"', - get_class($node) - )); - } - - return $node; - } - - private function textEditsToRename(Node $scopeNode, Node $variable, string $newName): array - { - $textEdits = []; - - if ($textEdit = $this->textEditForRenameFromNode($variable, $scopeNode, $newName)) { - $textEdits[] = $textEdit; - } - - /** @var Node $node */ - foreach ($scopeNode->getDescendantNodes() as $node) { - if (null === $textEdit = $this->textEditForRenameFromNode($variable, $node, $newName)) { - continue; - } - - $textEdits[] = $textEdit; - } - - return $textEdits; - } - - private function scopeNode(Node $variable, string $scope): Node - { - if ($scope === RenameVariable::SCOPE_FILE) { - return $variable->getRoot(); - } - - if ($variable instanceof UseVariableName) { - $variable = $variable->getFirstAncestor(MethodDeclaration::class) ?: $variable; - } - - $scopeNode = $variable->getFirstAncestor(FunctionLike::class, ClassLike::class, SourceFileNode::class); - - if (null === $scopeNode) { - throw new TransformException( - 'Could not determine scope node, this should not happen as ' . - 'there should always be a SourceFileNode.' - ); - } - - return $scopeNode; - } - - private function variableName(Node $variable): string - { - if ($variable instanceof Parameter) { - $name = $variable->variableName->getText($variable->getFileContents()); - return (string)$name; - } - - return $variable->getText(); - } - - private function textEditForRenameFromNode(Node $variable, Node $node, string $newName): ?TextEdit - { - if ( - false === $node instanceof UseVariableName && - false === $node instanceof Variable && - false === $node instanceof Parameter - ) { - return null; - } - - if ($this->variableName($variable) !== $this->variableName($node)) { - return null; - } - - - if ($node instanceof Variable || $node instanceof UseVariableName) { - return TextEdit::create( - $node->getStartPosition(), - $node->getEndPosition() - $node->getStartPosition(), - '$' . $newName - ); - } - - if ($node instanceof Parameter) { - /** @var Parameter $node */ - return TextEdit::create( - $node->variableName->getStartPosition(), - $node->variableName->getEndPosition() - $node->variableName->getStartPosition(), - '$' . $newName - ); - } - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/GenerateFromExisting/InterfaceFromExistingGenerator.php b/lib/CodeTransform/Adapter/WorseReflection/GenerateFromExisting/InterfaceFromExistingGenerator.php deleted file mode 100644 index fccce4135a..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/GenerateFromExisting/InterfaceFromExistingGenerator.php +++ /dev/null @@ -1,81 +0,0 @@ -reflector->reflectClass(ReflectionClassName::fromString((string) $existingClass)); - - /** @var SourceCodeBuilder $sourceBuilder */ - $sourceBuilder = SourceCodeBuilder::create(); - $sourceBuilder->namespace($targetName->namespace()); - $interfaceBuilder = $sourceBuilder->interface($targetName->short()); - $useClasses = []; - - /** @var ReflectionMethod $method */ - foreach ($existingClass->methods()->byVisibilities([ Visibility::public() ]) as $method) { - if ($method->name() === '__construct') { - continue; - } - - $methodBuilder = $interfaceBuilder->method($method->name()); - $methodBuilder->visibility((string) $method->visibility()); - - if ($method->docblock()->isDefined()) { - $methodBuilder->docblock($method->docblock()->formatted()); - } - - if ($method->returnType()->isDefined()) { - $methodBuilder->returnType($method->returnType()->short(), $method->returnType()); - - foreach ($method->returnType()->allTypes()->classLike() as $classType) { - $sourceBuilder->use($classType->toPhpString()); - } - } - - /** @var ReflectionParameter $parameter */ - foreach ($method->parameters() as $parameter) { - $parameterBuilder = $methodBuilder->parameter($parameter->name()); - $parameterType = $parameter->type(); - - if ($parameter->type()->isDefined()) { - $parameterBuilder->type($parameterType->short()); - - foreach ($parameterType->allTypes()->classLike() as $classType) { - $useClasses[$classType->name()->__toString()] = true; - } - - if ($parameter->default()->isDefined()) { - $parameterBuilder->defaultValue($parameter->default()->value()); - } - } - } - } - - foreach (array_keys($useClasses) as $useClass) { - $sourceBuilder->use($useClass); - } - - return SourceCode::fromString($this->renderer->render($sourceBuilder->build())); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Helper/EmptyValueRenderer.php b/lib/CodeTransform/Adapter/WorseReflection/Helper/EmptyValueRenderer.php deleted file mode 100644 index a208e44d04..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Helper/EmptyValueRenderer.php +++ /dev/null @@ -1,52 +0,0 @@ -reflectionOrNull(); - if ($reflection instanceof ReflectionClass) { - return sprintf('new %s()', $type->name()->short()); - } - if ($reflection instanceof ReflectionEnum) { - $firstCase = $reflection->cases()->firstOrNull(); - if ($firstCase) { - return sprintf('%s::%s', $type->name()->short(), $firstCase->name()); - } - return sprintf('/** enum `%s` has no cases */', $type->name()->short()); - } - } - - if (!$type instanceof Literal) { - return sprintf('/** %s */', $type->__toString()); - } - - if ($type instanceof StringLiteralType) { - return sprintf('\'%s\'', $type->value()); - } - - if ($type instanceof ArrayLiteral) { - return sprintf('[%s]', implode(', ', array_map(fn (Type $value) => $this->render($value), $type->iterableValueTypes()))); - } - - return $type->__toString(); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Helper/WorseInterestingOffsetFinder.php b/lib/CodeTransform/Adapter/WorseReflection/Helper/WorseInterestingOffsetFinder.php deleted file mode 100644 index 8e25b9a7a3..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Helper/WorseInterestingOffsetFinder.php +++ /dev/null @@ -1,54 +0,0 @@ -resolveInterestingOffset($source, $offset)) { - return $interestingOffset; - } - - $node = $this->parser->get($source)->getDescendantNodeAtPosition($offset->toInt()); - - do { - $offset = ByteOffset::fromInt($node->getStartPosition()); - - if ($interestingOffset = $this->resolveInterestingOffset($source, $offset)) { - return $interestingOffset; - } - - $node = $node->parent; - } while ($node); - - return $offset; - } - - private function resolveInterestingOffset(TextDocument $source, ByteOffset $offset): ?ByteOffset - { - $reflectionOffset = $this->reflector->reflectOffset($source, $offset->toInt()); - - $symbolType = $reflectionOffset->nodeContext()->symbol()->symbolType(); - - if ($symbolType !== Symbol::UNKNOWN) { - return $offset; - } - - return null; - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Helper/WorseMissingMemberFinder.php b/lib/CodeTransform/Adapter/WorseReflection/Helper/WorseMissingMemberFinder.php deleted file mode 100644 index 63b363916f..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Helper/WorseMissingMemberFinder.php +++ /dev/null @@ -1,38 +0,0 @@ -reflector->diagnostics($sourceCode))->byClass(MissingMemberDiagnostic::class); - $missing = []; - - /** @var MissingMemberDiagnostic $missingMethod */ - foreach ($diagnostics as $missingMethod) { - $missing[] = new MissingMember( - $missingMethod->methodName(), - $missingMethod->range(), - $missingMethod->memberType(), - ); - } - - return $missing; - }); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractConstant.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractConstant.php deleted file mode 100644 index a14ed6130b..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractConstant.php +++ /dev/null @@ -1,130 +0,0 @@ -reflector - ->reflectOffset($sourceCode, $offset) - ->nodeContext(); - - $textEdits = $this->addConstant($sourceCode, $symbolInformation, $constantName); - $textEdits = $textEdits->merge($this->replaceValues($sourceCode, $offset, $constantName)); - return new TextDocumentEdits(TextDocumentUri::fromString($sourceCode->uri()->path()), $textEdits); - } - - public function canExtractConstant(SourceCode $source, int $offset): bool - { - $node = $this->parser->get($source); - $targetNode = $node->getDescendantNodeAtPosition($offset); - try { - $this->getComparableValue($targetNode); - } catch (TransformException) { - return false; - } - return true; - } - - private function addConstant(SourceCode $sourceCode, NodeContext $symbolInformation, string $constantName): TextEdits - { - $symbol = $symbolInformation->symbol(); - - $builder = SourceCodeBuilder::create(); - $classType = $symbolInformation->containerType()->expandTypes()->classLike()->firstOrNull(); - - if (!$classType) { - throw new TransformException('Node does not belong to a class'); - } - - if ($classType->members()->constants()->has($constantName)) { - throw new TransformException( - sprintf( - 'Constant with name %s already exists on class %s', - $constantName, - $classType->name()->short() - ) - ); - } - - $builder->namespace($classType->name()->namespace()); - $builder - ->class($classType->name()->short()) - ->constant($constantName, TypeUtil::valueOrNull($symbolInformation->type())) - ->end(); - - return $this->updater->textEditsFor($builder->build(), $sourceCode); - } - - private function replaceValues(SourceCode $sourceCode, int $offset, string $constantName): TextEdits - { - $node = $this->parser->get($sourceCode); - $targetNode = $node->getDescendantNodeAtPosition($offset); - $targetValue = $this->getComparableValue($targetNode); - $classNode = $targetNode->getFirstAncestor(ClassLike::class); - - if (null === $classNode) { - throw new TransformException('Node does not belong to a class'); - } - - $textEdits = []; - foreach ($classNode->getDescendantNodes() as $node) { - if (!$node instanceof $targetNode) { - continue; - } - - if ($targetValue == $this->getComparableValue($node)) { - $textEdits[] = TextEdit::create( - $node->getStartPosition(), - $node->getEndPosition() - $node->getStartPosition(), - 'self::' . $constantName - ); - } - } - - return TextEdits::fromTextEdits($textEdits); - } - - private function getComparableValue(Node $node): string - { - if ($node instanceof StringLiteral) { - return $node->getStringContentsText(); - } - - if ($node instanceof NumericLiteral) { - return $node->getText(); - } - - throw new TransformException(sprintf( - 'Do not know how to replace node of type "%s"', - get_class($node) - )); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractMethod.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractMethod.php deleted file mode 100644 index 007edeb4b5..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractMethod.php +++ /dev/null @@ -1,468 +0,0 @@ -__toString())) { - return false; - } - $node = $this->parser->get($source); - $endNode = $node->getDescendantNodeAtPosition($offsetEnd); - $startNode = $node->getDescendantNodeAtPosition($offsetStart); - - if (!$startNode->getFirstAncestor(MethodDeclaration::class)) { - return false; - } - - if ( - $endNode instanceof CompoundStatementNode && - $endNode->openBrace->getEndPosition() < $offsetEnd && - $endNode->closeBrace->getStartPosition() >= $offsetEnd && - count($endNode->statements) > 0 - ) { - $stmt = end($endNode->statements); - - while ($stmt->getEndPosition() > $offsetEnd) { - $prev = prev($endNode->statements); - if ($prev === false) { - break; - } - $stmt = $prev; - } - - $endNode = $stmt ?? $endNode; - } - - while ($endNode->parent && !($endNode->parent instanceof CompoundStatementNode)) { - $endNode = $endNode->parent; - } - - if ( - $startNode instanceof CompoundStatementNode && - $startNode->openBrace->getEndPosition() <= $offsetStart && - $startNode->closeBrace->getStartPosition() > $offsetStart && - count($startNode->statements) > 0 - ) { - $stmt = current($startNode->statements); - if (!$stmt instanceof Node) { - return false; - } - while ($stmt && $stmt->getStartPosition() < $offsetStart) { - $stmt = next($startNode->statements); - } - - $startNode = $stmt ?? $startNode; - if (!$startNode instanceof Node) { - return false; - } - } - - while ($startNode->parent && !($startNode->parent instanceof CompoundStatementNode)) { - $startNode = $startNode->parent; - } - - if ($startNode->parent != $endNode->parent) { - return false; - } - - return true; - } - - public function extractMethod(SourceCode $source, int $offsetStart, int $offsetEnd, string $name): TextDocumentEdits - { - if (!$this->canExtractMethod($source, $offsetStart, $offsetEnd)) { - throw new TransformException('Cannot extract method. Check if start and end statements are in different scopes.'); - } - - $isExpression = $this->isSelectionAnExpression($source, $offsetStart, $offsetEnd); - - $selection = $source->extractSelection($offsetStart, $offsetEnd); - $builder = $this->factory->fromSource($source); - $reflectionMethod = $this->reflectMethod($offsetEnd, $source, $name); - - $methodBuilder = $this->createMethodBuilder($reflectionMethod, $builder, $name); - $newMethodBody = $this->removeIndentation($selection); - if ($isExpression) { - $newMethodBody = $this->addExpressionReturn($newMethodBody, $source, $offsetEnd, $methodBuilder); - } - $methodBuilder->body()->line($newMethodBody); - - $locals = $this->scopeLocalVariables($source, $offsetStart, $offsetEnd); - - if ($reflectionMethod->isStatic()) { - $methodBuilder->static(); - } - - $parameterVariables = $this->parameterVariables($locals->lessThan($offsetStart), $selection, $offsetStart); - $args = $this->addParametersAndGetArgs($parameterVariables, $methodBuilder, $builder); - - $returnVariables = $this->returnVariables($locals, $reflectionMethod, $source, $offsetStart, $offsetEnd); - - $returnAssignment = $this->addReturnAndGetAssignment( - $returnVariables, - $methodBuilder, - $args - ); - - $prototype = $builder->build(); - - $replacement = $this->replacement($name, $args, $selection, $returnAssignment); - - if ($isExpression) { - $replacement = rtrim($replacement, ';'); - } - - return new TextDocumentEdits( - TextDocumentUri::fromString($source->uri()->path()), - $this->updater->textEditsFor($prototype, $source) - ->add(TextEdit::create($offsetStart, $offsetEnd - $offsetStart, $replacement)) - ); - } - - /** - * @return array - */ - private function parameterVariables(Assignments $locals, string $selection, int $offsetStart): array - { - $variableNames = $this->variableNames($selection); - - $parameterVariables = []; - foreach ($variableNames as $variable) { - $variables = $locals->lessThanOrEqualTo($offsetStart)->byName($variable); - if ($variables->count()) { - $parameterVariables[$variable] = $variables->last(); - } - } - - return $parameterVariables; - } - - /** - * @return array - */ - private function returnVariables( - Assignments $locals, - ReflectionMethod $reflectionMethod, - string $source, - int $offsetStart, - int $offsetEnd - ): array { - // variables that are: - // - // - defined in the selection - // - and used in the parent scope - // - after the end offset - $tailDependencies = $this->variableNames( - $tail = mb_substr( - $source, - $offsetEnd, - $reflectionMethod->position()->end()->toInt() - $offsetEnd - ) - ); - - $returnVariables = []; - foreach ($tailDependencies as $variable) { - $variables = $locals->byName($variable) - ->assignmentsOnly() - ->lessThanOrEqualTo($offsetEnd) - ->greaterThanOrEqualTo($offsetStart); - - if ($variables->count()) { - $returnVariables[$variable] = $variables->last(); - } - } - - return $returnVariables; - } - - private function removeIndentation(string $selection): string - { - return TextUtils::removeIndentation($selection); - } - - private function createMethodBuilder(ReflectionMethod $reflectionMethod, SourceCodeBuilder $builder, string $name): MethodBuilder - { - $classLikeBuilder = $builder->classLike($reflectionMethod->class()->name()->short()); - - return $classLikeBuilder->method($name)->visibility('private'); - } - - private function reflectMethod(int $offsetEnd, SourceCode $source, string $name): ReflectionMethod - { - $offset = $this->reflector->reflectOffset($source, $offsetEnd); - $thisVariable = $offset->frame()->locals()->byName('this'); - - if ($thisVariable->count() === 0) { - throw new TransformException('Cannot extract method, not in class scope'); - } - - $type = $thisVariable->last()->type()->expandTypes()->classLike()->firstOrNull(); - - if (!$type) { - throw new TransformException('Cannot extract method, not in class scope'); - } - $className = $type->name(); - - $reflectionClass = $this->reflector->reflectClassLike((string) $className); - - $methods = $reflectionClass->methods(); - if ($methods->belongingTo($className)->has($name)) { - throw new TransformException(sprintf('Class "%s" already has method "%s"', (string) $className, $name)); - } - - // returns the method that the offset is within - $member = $methods->belongingTo($className)->atOffset($offsetEnd)->first(); - - if (!$member instanceof ReflectionMethod) { - throw new TransformException(sprintf('Member should have been a method but it was a "%s"', get_class($member))); - } - - return $member; - } - /** - * @param array $freeVariables - * - * @return list - */ - private function addParametersAndGetArgs(array $freeVariables, MethodBuilder $methodBuilder, SourceCodeBuilder $builder): array - { - $args = []; - - foreach ($freeVariables as $freeVariable) { - if (in_array($freeVariable->name(), [ 'this', 'self' ])) { - continue; - } - - $parameterBuilder = $methodBuilder->parameter($freeVariable->name()); - $variableType = $freeVariable->type(); - if ($variableType->isDefined()) { - $parameterBuilder->type($variableType->short()); - foreach ($variableType->expandTypes()->classLike() as $classType) { - $builder->use($classType->toPhpString()); - } - } - - $args[] = '$' . $freeVariable->name(); - } - - return $args; - } - - /** - * @return Assignments - */ - private function scopeLocalVariables(SourceCode $source, int $offsetStart, int $offsetEnd): Assignments - { - return $this->reflector->reflectOffset( - $source, - $offsetEnd - )->frame()->locals(); - } - - /** - * @return list - */ - private function variableNames(string $source): array - { - $node = $this->parseSelection($source); - return $this->extractVariableNamesFromNode($node, []); - } - - /** - * @param list $ignoreNames - * @return list - */ - private function extractVariableNamesFromNode(Node $node, array $ignoreNames): array - { - $fileContents = $node->getFileContents(); - if ($node instanceof CatchClause && $node->variableName !== null) { - $ignoreNames[] = (string) $node->variableName->getText($fileContents); - } - $variables = []; - foreach ($node->getChildNodesAndTokens() as $nodeOrToken) { - if ($nodeOrToken instanceof FunctionLike) { - continue; - } - - if ($nodeOrToken instanceof Node) { - $variables = array_merge($variables, $this->extractVariableNamesFromNode($nodeOrToken, $ignoreNames)); - continue; - } - - if ($nodeOrToken->kind == TokenKind::VariableName) { - $text = $nodeOrToken->getText($fileContents); - if (is_string($text) && !in_array($text, $ignoreNames, true)) { - $variables[] = substr($text, 1); - } - } - } - return $variables; - } - - /** - * @param array $returnVariables - * @param list $args - */ - private function addReturnAndGetAssignment(array $returnVariables, MethodBuilder $methodBuilder, array $args): ?string - { - $returnVariables = array_filter($returnVariables, function (Variable $variable) { - return false === in_array($variable->name(), ['self', 'this']); - }); - - $returnVariables = array_filter($returnVariables, function (Variable $variable) use ($args) { - if ($variable->type()->isPrimitive()) { - return true; - } - - return false === in_array('$' . $variable->name(), $args); - }); - - if ($returnVariables === []) { - return null; - } - - if (count($returnVariables) === 1) { - /** @var Variable $variable */ - $variable = reset($returnVariables); - $methodBuilder->body()->line('return $' . $variable->name() . ';'); - $type = $variable->type()->generalize()->reduce(); - if ($type->isDefined()) { - $methodBuilder->returnType($type->short(), $type); - foreach ($type->expandTypes()->classLike() as $classType) { - $methodBuilder->end()->end()->use($classType->name()->full()); - } - } - - return '$' . $variable->name(); - } - - $names = implode(', ', array_map(function (Variable $variable) { - return '$' . $variable->name(); - }, $returnVariables)); - - $methodBuilder->body()->line('return [' . $names . '];'); - $methodBuilder->returnType('array'); - - return 'list(' . $names . ')'; - } - - /** - * @param list $args - */ - private function replacement(string $name, array $args, string $selection, ?string $returnAssignment): string - { - $indentation = str_repeat(' ', TextUtils::stringIndentation($selection)); - $callString = '$this->' . $name . '(' . implode(', ', $args) . ');'; - - if (empty($returnAssignment)) { - $replacement = $indentation . $callString; - $selectionRootNode = $this->parseSelection($selection); - - if ($this->nodeContainsReturnStatement($selectionRootNode)) { - $replacement = 'return ' . $replacement; - } - - return $replacement; - } - - return $indentation . $returnAssignment . ' = ' . $callString; - } - - private function nodeContainsReturnStatement(Node $node): bool - { - foreach ($node->getDescendantNodes( - function (Node $n) { - return !($n instanceof FunctionLike); - } - ) as $node) { - if ($node instanceof ReturnStatement) { - return true; - } - } - return false; - } - - private function isSelectionAnExpression(SourceCode $source, int $offsetStart, int $offsetEnd): bool - { - $node = $this->parser->get($source); - $endNode = $node->getDescendantNodeAtPosition($offsetEnd); - - // end node is in the statement body, get last child node - if ($endNode instanceof CompoundStatementNode) { - $childNodes = iterator_to_array($endNode->getChildNodes()); - $endNode = end($childNodes); - assert($endNode instanceof Node); - } - - // get the positional parent of the node - while ($endNode->parent && $endNode->getEndPosition() === $endNode->parent->getEndPosition()) { - $endNode = $endNode->parent; - } - - return !$endNode->parent instanceof CompoundStatementNode; - } - - private function addExpressionReturn(string $newMethodBody, SourceCode $source, int $offsetEnd, MethodBuilder $methodBuilder): string - { - $newMethodBody = 'return ' . $newMethodBody .';'; - $offset = $this->reflector->reflectOffset($source, $offsetEnd); - $expressionType = $offset->nodeContext()->type(); - - if ($expressionType->isDefined()) { - $methodBuilder->returnType($expressionType->short(), $expressionType); - } - - foreach ($expressionType->expandTypes()->classLike() as $classType) { - $methodBuilder->end()->end()->use($classType->name()->full()); - } - - return $newMethodBody; - } - - private function parseSelection(string $source): SourceFileNode - { - $source = 'parser->get(TextDocumentBuilder::create($source)->build()); - return $node; - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseFillMatchArms.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseFillMatchArms.php deleted file mode 100644 index 3795503a6c..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseFillMatchArms.php +++ /dev/null @@ -1,111 +0,0 @@ -parser->get($document)->getDescendantNodeAtPosition($offset->toInt()); - $node = $node instanceof MatchExpression ? $node : $node->getFirstAncestor(MatchExpression::class); - if (!$node instanceof MatchExpression) { - return TextEdits::none(); - } - try { - $reflectionNode = $this->reflector->reflectNode($document, $node->getStartPosition()); - } catch (NotFound $notFound) { - return TextEdits::none(); - } - - if (!$reflectionNode instanceof ReflectionMatchExpression) { - return TextEdits::none(); - } - - $type = $reflectionNode->expressionType(); - if (!$type instanceof ReflectedClassType) { - return TextEdits::none(); - } - - $enum = $type->reflectionOrNull(); - if (!$enum instanceof ReflectionEnum) { - return TextEdits::none(); - } - - $edits = []; - [$prefix, $whitespace, $postfix, $start, $existingCases] = $this->existingCases($node); - if ($prefix) { - $edits[] = TextEdit::create($start, 0, $prefix); - } - $edits[] = TextEdit::create($start, 0, "\n"); - foreach ($enum->cases() as $case) { - if (in_array($case->name(), $existingCases)) { - continue; - } - $edits[] = TextEdit::create($start, 0, sprintf("%s%s::%s => null,\n", $whitespace, $enum->name()->short(), $case->name())); - } - $edits[] = TextEdit::create($start, 0, $whitespace); - if ($postfix) { - $edits[] = TextEdit::create($start, 0, $postfix); - } - - return TextEdits::fromTextEdits($edits); - } - - /** - * @return array{?string,string,?string,int,string[]} - */ - private function existingCases(MatchExpression $node): array - { - $start = $node->openBrace->getStartPosition() + 1; - $prefix = null; - $postfix = null; - $whitespace = ''; - if ($node->openBrace instanceof MissingToken) { - $prefix = "{\n"; - } - if ($node->closeBrace instanceof MissingToken) { - $postfix = '}'; - } - $cases = []; - foreach ($node->arms?->getChildNodes() ?? [] as $arm) { - assert($arm instanceof MatchArm); - $start = $arm->getEndPosition() + 1; - foreach ($arm->conditionList->getChildNodes() as $node) { - if (!$node instanceof ScopedPropertyAccessExpression) { - continue; - } - $cases[] = NodeUtil::nameFromTokenOrNode($node, $node->memberName); - - } - } - $line = LineAtOffset::lineAtByteOffset($node->getFileContents(), ByteOffset::fromInt($start)); - if (preg_match('{^\s+}', $line, $matches)) { - $whitespace = $matches[0]; - } - return [$prefix, $whitespace, $postfix, $start, $cases]; - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseFillObject.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseFillObject.php deleted file mode 100644 index e5fa41e44e..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseFillObject.php +++ /dev/null @@ -1,127 +0,0 @@ -valueRenderer = new EmptyValueRenderer(); - } - - public function refactor(TextDocument $document, ByteOffset $offset): TextEdits - { - /** @var ObjectCreationExpression|Attribute|null $node */ - $node = $this->parser - ->get($document) - ->getDescendantNodeAtPosition($offset->toInt()) - ->getFirstAncestor(ObjectCreationExpression::class, Attribute::class) - ; - if ($node === null) { - return TextEdits::none(); - } - - try { - $offset = $this->reflector->reflectNode($document, $node->getStartPosition()); - } catch (NotFound $notFound) { - return TextEdits::none(); - } - - if (!$offset instanceof ReflectionObjectCreationExpression) { - return TextEdits::none(); - } - - // do not support existing arguments - if ($offset->arguments()->count() !== 0) { - return TextEdits::none(); - } - - try { - $constructor = $offset->class()->methods()->get('__construct'); - } catch (NotFound) { - return TextEdits::none(); - } - - $args = []; - - $imports = []; - foreach ($constructor->parameters() as $parameter) { - assert($parameter instanceof ReflectionParameter); - $parameterType = $parameter->type(); - if ($parameterType instanceof ReflectedClassType && $parameterType->isInterface()->isFalse()) { - $imports[] = $parameterType; - } - $arg = []; - if ($this->namedParameters) { - $arg[] = sprintf( - '%s: ', - $parameter->name(), - ); - } - $arg[] = $this->renderEmptyValue($parameterType); - - if ($this->hint) { - $arg[] = sprintf(' /** $%s %s */', $parameter->name(), $parameter->type()->__toString()); - } - $args[] = implode('', $arg); - } - - $sourceCode = SourceCodeBuilder::create(); - foreach ($imports as $import) { - $sourceCode->use($import->__toString()); - } - $textEdits = $this->updater->textEditsFor($sourceCode->build(), $document); - - $openParen = $closedParen = ''; - if ($node->openParen) { - $endPosition = $node->openParen->getEndPosition(); - } else { - $endPosition = $node->getEndPosition(); - $openParen = '('; - } - if (!$node->closeParen) { - $closedParen = ')'; - } - - return $textEdits->add(TextEdit::create( - $endPosition, - 0, - sprintf('%s%s%s', $openParen, implode(', ', $args), $closedParen), - )); - } - - private function renderEmptyValue(Type $type): string - { - if (!$type instanceof HasEmptyType) { - return sprintf('/** %s */', $type->__toString()); - } - - return $this->valueRenderer->render($type->emptyType()); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateAccessor.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateAccessor.php deleted file mode 100644 index a7e31427e2..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateAccessor.php +++ /dev/null @@ -1,123 +0,0 @@ -upperCaseFirst = ($prefix && $upperCaseFirst === null) || $upperCaseFirst; - } - - /** - * @param string[] $propertyNames - */ - public function generate(SourceCode $sourceCode, array $propertyNames, int $offset): TextEdits - { - $class = $this->class($sourceCode, $offset); - $allProperties = $class->properties(); - - $properties = array_map(fn (string $name) => $allProperties->get($name), $propertyNames); - - $prototype = $this->buildPrototype($class, $properties); - $sourceCode = $this->sourceFromClassName($sourceCode, $class->name()); - - return $this->updater->textEditsFor( - $prototype, - $sourceCode, - ); - } - - private function formatName(string $name): string - { - if ($this->upperCaseFirst) { - $name = ucfirst($name); - } - - return $this->prefix . $name; - } - - /** - * @param ReflectionProperty[] $properties - */ - private function buildPrototype(ReflectionClass $class, array $properties): PrototypeSourceCode - { - $builder = SourceCodeBuilder::create(); - $className = $class->name(); - - $builder->namespace($className->namespace()); - - foreach ($properties as $reflectionProperty) { - $method = $builder - ->class($className->short()) - ->method($this->formatName($reflectionProperty->name())); - $method->body()->line(sprintf('return $this->%s;', $reflectionProperty->name())); - - $type = $reflectionProperty->inferredType(); - if ($type->isDefined()) { - $method->returnType($type->short(), $type); - } - } - - return $builder->build(); - } - - private function sourceFromClassName(SourceCode $sourceCode, ClassName $className): SourceCode - { - $containingClass = $this->reflector->reflectClassLike($className); - $worseSourceCode = $containingClass->sourceCode(); - - if ($worseSourceCode->uri()?->path() != $sourceCode->uri()->path()) { - return $sourceCode; - } - - return SourceCode::fromStringAndPath( - $worseSourceCode->__toString(), - $worseSourceCode->uri()?->path() - ); - } - - private function class(SourceCode $source, int $offset): ReflectionClass - { - $classes = $this->reflector->reflectClassesIn($source)->classes(); - - if (0 === $classes->count()) { - throw new InvalidArgumentException( - 'No classes in source file' - ); - } - - if (1 === $classes->count()) { - return $classes->first(); - } - - foreach ($classes as $class) { - $position = $class->position(); - - if ($position->start()->toInt() <= $offset && $offset <= $position->end()->toInt()) { - return $class; - } - } - - throw new RuntimeException('Impossible to determine which class to use.'); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateConstructor.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateConstructor.php deleted file mode 100644 index a18d9fdf64..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateConstructor.php +++ /dev/null @@ -1,133 +0,0 @@ -reflectionNode($document, $offset); - - if (null === $reflectionNode) { - return WorkspaceEdits::none(); - } - - if (!$reflectionNode instanceof ClassInvocation) { - return WorkspaceEdits::none(); - } - - try { - if ($reflectionNode->class()->methods()->has('__construct')) { - return WorkspaceEdits::none(); - } - } catch (NotFound) { - return WorkspaceEdits::none(); - } - - $arguments = $reflectionNode->arguments(); - - if (count($arguments) === 0) { - return WorkspaceEdits::none(); - } - - $builder = $this->factory->fromSource($reflectionNode->class()->sourceCode()); - $class = $builder->class($reflectionNode->class()->name()->short()); - $method = $class->method('__construct'); - - $docblockTypes = []; - foreach ($arguments->named() as $name => $argument) { - assert($argument instanceof ReflectionArgument); - $type = $argument->type(); - if ($type->isAugmented()) { - $docblockTypes[$name] = $type->toLocalType($reflectionNode->scope()); - } - foreach ($type->allTypes()->classLike() as $classType) { - $builder->use($classType->toPhpString()); - } - $param = $method->parameter($name); - $param->type($argument->type()->short()); - } - - // TODO: this should be handled by the code updater (e.g. $docblock->addParam(new ParamPrototype(...))) - $docblock = []; - foreach ($docblockTypes as $name => $type) { - $docblock[] = sprintf('@param %s $%s', $type->__toString(), $name); - } - - if ($docblock) { - $method->docblock(implode("\n", $docblock)); - } - - return new WorkspaceEdits( - new TextDocumentEdits( - $reflectionNode->class()->sourceCode()->uriOrThrow(), - $this->updater->textEditsFor( - $builder->build(), - $reflectionNode->class()->sourceCode(), - ) - ) - ); - } - - private function reflectionNode(TextDocument $document, ByteOffset $offset): ?ReflectionNode - { - $node = $this->node($document, $offset); - if (null === $node) { - return null; - } - - try { - $newObject = $this->reflector->reflectNode($document, $node->getStartPosition()); - } catch (NotFound) { - return null; - } - - return $newObject; - } - - private function node(TextDocument $document, ByteOffset $offset): ?Node - { - $node = $this->parser->get($document)->getDescendantNodeAtPosition($offset->toInt()); - - if ($node->parent instanceof Attribute) { - return $node->parent; - } - - $node = $node->getFirstAncestor(ObjectCreationExpression::class); - - if ($node instanceof ObjectCreationExpression) { - return $node; - } - - return null; - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateDecorator.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateDecorator.php deleted file mode 100644 index 9d493cbbd6..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateDecorator.php +++ /dev/null @@ -1,112 +0,0 @@ -reflector->reflectClassesIn($source)->classes()->first(); - - $builder = SourceCodeBuilder::create(); - $builder->namespace($class->name()->namespace()); - $classBuilder = $builder->class($class->name()->short()); - - - $interfaceType = TypeFactory::reflectedClass($this->reflector, $interfaceFQN); - $interfaceType = $interfaceType->toLocalType($class->scope()); - - $property = $classBuilder->property('inner') - ->visibility(Visibility::PRIVATE) - ->type($interfaceType->toPhpString()) - ->docType($interfaceType->__toString()); - - $constructor = $classBuilder->method('__construct'); - $constructor->parameter('inner')->type($interfaceType->toPhpString()); - $constructor->body()->line('$this->inner = $inner;'); - - $interface = $this->reflector->reflectInterface($interfaceFQN); - foreach ($interface->methods() as $interfaceMethod) { - $method = $classBuilder->method($interfaceMethod->name()); - - if ($interfaceMethod->returnType()->isDefined()) { - $method->returnType($interfaceMethod->returnType()->toLocalType($class->scope())->toPhpString()); - foreach ($interfaceMethod->returnType()->allTypes()->classLike() as $type) { - $builder->use($type->name()); - } - } - $method->visibility($interfaceMethod->visibility()); - - $this->attachParameters($builder, $class, $method, $interfaceMethod); - - $method->body()->line($this->generateMethodBody($interfaceMethod)); - } - - return $this->updater->textEditsFor($builder->build(), $source); - } - - /** - * Copying over the method parameters from the interface to the decoration - */ - private function attachParameters(SourceCodeBuilder $builder, ReflectionClassLike $class, MethodBuilder $method, ReflectionMethod $interfaceMethod): void - { - foreach ($interfaceMethod->parameters() as $interfaceMethodParameter) { - $parameter = $method->parameter($interfaceMethodParameter->name()) - ->type($interfaceMethodParameter->type()->toLocalType($class->scope())->toPhpString()); - - foreach ($interfaceMethodParameter->type()->expandTypes()->classLike() as $type) { - $builder->use($type->name()); - } - - $defaultValue = $interfaceMethodParameter->default(); - - if ($defaultValue->isDefined()) { - $parameter ->defaultValue($interfaceMethodParameter->default()->value()); - } - } - } - - /** - * This method creates the method body which means copying parameters of the interface method to the body of the function. - * So if the interface contains: - * - * function someFunction(string $a, int $b) - * - * then the content of the decoration method needs to be - * - * $this->inner->someFunction($a, $b); - */ - private function generateMethodBody(ReflectionMethod $interfaceMethod): string - { - $code = '$this->inner->'.$interfaceMethod->name().'('; - foreach ($interfaceMethod->parameters() as $interfaceMethodParameter) { - $code .= '$'.$interfaceMethodParameter->name().', '; - } - $code = trim($code, ', '); - $code .= ');'; - - if (!$interfaceMethod->returnType()->isVoid()) { - $code = 'return '. $code; - } - - return $code; - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateMember.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateMember.php deleted file mode 100644 index be8d62f19f..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateMember.php +++ /dev/null @@ -1,211 +0,0 @@ -contextType($sourceCode, $offset); - $worseSourceCode = TextDocumentBuilder::fromPathAndString((string) $sourceCode->uri()->path(), (string) $sourceCode); - $memberAccess = $this->reflector->reflectNode($worseSourceCode, $offset); - - if ($memberAccess instanceof ReflectionMethodCall) { - $this->validate($memberAccess); - $visibility = $this->determineVisibility($contextType, $memberAccess->class()); - - $prototype = $this->addMethodCallToBuilder($memberAccess, $visibility, $memberAccess->isStatic(), $methodName); - $sourceCode = $this->resolveSourceCode($sourceCode, $memberAccess->class(), $visibility); - - $textEdits = $this->updater->textEditsFor($prototype, $sourceCode); - - return new TextDocumentEdits(TextDocumentUri::fromString($sourceCode->uri()->path()), $textEdits); - } - - if ($memberAccess instanceof ReflectionStaticMemberAccess) { - $visibility = $this->determineVisibility($contextType, $memberAccess->class()); - $prototype = $this->addMemberToBuilder($memberAccess, $visibility, $methodName); - $sourceCode = $this->resolveSourceCode($sourceCode, $memberAccess->class(), $visibility); - - $textEdits = $this->updater->textEditsFor($prototype, $sourceCode); - - return new TextDocumentEdits(TextDocumentUri::fromString($sourceCode->uri()->path()), $textEdits); - } - - throw new RuntimeException(sprintf( - 'Could not generate member for "%s"', - $memberAccess::class - )); - } - - private function resolveSourceCode(SourceCode $sourceCode, ReflectionClassLike $class, string $visibility): SourceCode - { - $containerSourceCode = SourceCode::fromStringAndPath( - (string) $class->sourceCode(), - $class->sourceCode()->uri()?->path() - ); - - if ($sourceCode->uri()->path() != $containerSourceCode->uri()->path()) { - return $containerSourceCode; - } - - return $sourceCode; - } - - private function contextType(SourceCode $sourceCode, int $offset): ?Type - { - $worseSourceCode = TextDocumentBuilder::fromPathAndString((string) $sourceCode->uri()->path(), (string) $sourceCode); - $reflectionOffset = $this->reflector->reflectOffset($worseSourceCode, $offset); - - /** - * @var Variable $variable - */ - foreach ($reflectionOffset->frame()->locals()->byName('$this') as $variable) { - return $variable->type(); - } - - return null; - } - - private function addMethodCallToBuilder( - ReflectionMethodCall $methodCall, - Visibility $visibility, - bool $static, - ?string $methodName - ): PhpactorSourceCode { - $methodName = $methodName ?: $methodCall->name(); - - $reflectionClass = $methodCall->class(); - $builder = $this->factory->fromSource($reflectionClass->sourceCode()); - - $classBuilder = $builder->classLike($reflectionClass->name()->short()); - $methodBuilder = $classBuilder->method($methodName); - $methodBuilder->visibility((string) $visibility); - if ($static) { - $methodBuilder->static(); - } - - $docblockTypes = []; - - /** @var ReflectionArgument $argument */ - foreach ($methodCall->arguments()->named() as $name => $argument) { - $type = $argument->type(); - - if ($type->isAugmented()) { - $docblockTypes[$name] = $type->toLocalType($reflectionClass->scope()); - } - - $parameterBuilder = $methodBuilder->parameter($name); - - if ($type->isDefined()) { - $parameterBuilder->type($type->short(), $type); - - foreach ($type->allTypes()->classLike() as $classType) { - $builder->use($classType->toPhpString()); - } - } - } - - // TODO: this should be handled by the code updater (e.g. $docblock->addParam(new ParamPrototype(...))) - $docblock = []; - foreach ($docblockTypes as $name => $type) { - $docblock[] = sprintf('@param %s $%s', $type->__toString(), $name); - } - - if ($docblock) { - $methodBuilder->docblock(implode("\n", $docblock)); - } - - - $inferredType = $methodCall->inferredReturnType(); - if ($inferredType->isDefined()) { - $methodBuilder->returnType($inferredType->toPhpString(), $inferredType); - // this will not render localized types see https://github.com/phpactor/phpactor/issues/1453 - // if ($inferredType->__toString() !== $inferredType->toPhpString()) { - // $methodBuilder->docblock('@return ' . $inferredType->__toString()); - // } - } - - return $builder->build(); - } - - private function addMemberToBuilder( - ReflectionStaticMemberAccess $access, - Visibility $visibility, - ?string $caseName - ): PhpactorSourceCode { - $caseName = $caseName ?: $access->name(); - - $reflectionClass = $access->class(); - $builder = $this->factory->fromSource($reflectionClass->sourceCode()); - - $classLikeBuilder = $builder->classLike($reflectionClass->name()->short()); - if ($classLikeBuilder instanceof EnumBuilder) { - $classLikeBuilder->case($caseName); - } - if ($classLikeBuilder instanceof ClassBuilder) { - $constantBuuilder = $classLikeBuilder->constant($caseName, 0); - $constantBuuilder->visibility($visibility); - } - - - return $builder->build(); - } - - private function determineVisibility(?Type $contextType, ReflectionClassLike $targetClass): Visibility - { - if (null === $contextType) { - return Visibility::public(); - } - - if ($contextType instanceof ClassType && $contextType->name() == $targetClass->name()) { - return Visibility::private(); - } - - return Visibility::public(); - } - - private function validate(ReflectionMethodCall $methodCall): void - { - $target = $methodCall->class(); - if (!$target instanceof ReflectionClass && !$target instanceof ReflectionInterface && !$target instanceof ReflectionEnum) { - throw new TransformException(sprintf( - 'Can only generate methods on classes or interfaces (trying on %s)', - get_class($target->name()) - )); - } - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateMutator.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateMutator.php deleted file mode 100644 index e17607209b..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateMutator.php +++ /dev/null @@ -1,133 +0,0 @@ -upperCaseFirst = ($prefix && $upperCaseFirst === null) || $upperCaseFirst; - } - - /** - * @param string[] $propertyNames - */ - public function generate(SourceCode $sourceCode, array $propertyNames, int $offset): TextEdits - { - $class = $this->class($sourceCode, $offset); - $allProperties = $class->properties(); - - $properties = array_map(fn (string $name) => $allProperties->get($name), $propertyNames); - - $prototype = $this->buildPrototype($class, $properties); - $sourceCode = $this->sourceFromClassName($sourceCode, $class->name()); - - return $this->updater->textEditsFor( - $prototype, - $sourceCode - ); - } - - private function formatName(string $name): string - { - if ($this->upperCaseFirst) { - $name = ucfirst($name); - } - - return $this->prefix . $name; - } - - /** - * @param ReflectionProperty[] $properties - */ - private function buildPrototype(ReflectionClass $class, array $properties): PrototypeSourceCode - { - $builder = SourceCodeBuilder::create(); - $className = $class->name(); - - $builder->namespace($className->namespace()); - - foreach ($properties as $reflectionProperty) { - $method = $builder - ->class($className->short()) - ->method($this->formatName($reflectionProperty->name())); - $method->returnType('void'); - - $type = $reflectionProperty->inferredType(); - - $parameter = $method->parameter($reflectionProperty->name()); - if ($type->isDefined()) { - $parameter->type($type->short(), $type); - } - - $method->body()->line(sprintf('$this->%1$s = $%1$s;', $reflectionProperty->name())); - - if ($this->fluent) { - $method->returnType('self'); - $method->body()->line('return $this;'); - } - } - - return $builder->build(); - } - - private function sourceFromClassName(SourceCode $sourceCode, ClassName $className): SourceCode - { - $containingClass = $this->reflector->reflectClassLike($className); - $worseSourceCode = $containingClass->sourceCode(); - - if ($worseSourceCode->uri()?->path() != $sourceCode->uri()->path()) { - return $sourceCode; - } - - return SourceCode::fromStringAndPath( - $worseSourceCode->__toString(), - $worseSourceCode->uri()?->path() - ); - } - - private function class(SourceCode $source, int $offset): ReflectionClass - { - $classes = $this->reflector->reflectClassesIn($source)->classes(); - - if (0 === $classes->count()) { - throw new InvalidArgumentException( - 'No classes in source file' - ); - } - - if (1 === $classes->count()) { - return $classes->first(); - } - - foreach ($classes as $class) { - $position = $class->position(); - - if ($position->start()->toInt() <= $offset && $offset <= $position->end()->toInt()) { - return $class; - } - } - - throw new RuntimeException('Impossible to determine which class to use.'); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseOverrideMethod.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseOverrideMethod.php deleted file mode 100644 index c7a4c4a24c..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseOverrideMethod.php +++ /dev/null @@ -1,121 +0,0 @@ -getReflectionClass($source, $className); - $method = $this->getAncestorReflectionMethod($class, $methodName); - - $methodBuilder = $this->getMethodPrototype($class, $method); - $sourcePrototype = $this->getSourcePrototype($class, $method, $source, $methodBuilder); - - return $this->updater->textEditsFor($sourcePrototype, $source); - } - - private function getReflectionClass(SourceCode $source, string $className): ReflectionClass - { - $builder = TextDocumentBuilder::create($source)->language('php'); - if ($source->uri()->path()) { - $builder->uri($source->uri()); - } - - $classes = $this->reflector->reflectClassesIn($builder->build())->classes(); - - return $classes->get($className); - } - - private function getMethodPrototype(ReflectionClass $class, ReflectionMethod $method): MethodBuilder - { - /** @var ReflectionMethod $method */ - $builder = $this->factory->fromSource( - $method->class()->sourceCode() - ); - - $methodBuilder = $builder->class($method->declaringClass()->name()->short())->method($method->name()); - if (version_compare($this->phpVersion, '8.3', '>=')) { - $methodBuilder->attribute('\Override', []); - } - - return $methodBuilder; - } - - private function getAncestorReflectionMethod(ReflectionClass $class, string $methodName): ReflectionMethod - { - if (null === $class->parent()) { - throw new TransformException(sprintf( - 'Class "%s" has no parent, cannot override any method', - $class->name() - )); - } - - return $class->parent()->methods()->get($methodName); - } - - private function getSourcePrototype( - ReflectionClass $class, - ReflectionMethod $method, - SourceCode $source, - MethodBuilder $methodBuilder, - ): PhpactorSourceCode { - $sourceBuilder = $this->factory->fromSource($source); - $sourceBuilder->class($class->name()->short())->add($methodBuilder); - $this->importClasses($class, $method, $sourceBuilder); - - return $sourceBuilder->build(); - } - - private function importClasses(ReflectionClass $class, ReflectionMethod $method, SourceCodeBuilder $sourceBuilder): void - { - $usedClasses = []; - - foreach ($method->returnType()->allTypes()->classLike() as $classType) { - $usedClasses[] = $classType; - } - - /** - * @var ReflectionParameter $parameter */ - foreach ($method->parameters() as $parameter) { - foreach ($parameter->type()->expandTypes()->classLike() as $classType) { - $usedClasses[] = $classType; - } - } - - foreach ($usedClasses as $usedClass) { - assert($usedClass instanceof ClassType); - $className = $usedClass->name(); - - if ($class->name()->namespace() == $className->namespace()) { - continue; - } - - $sourceBuilder->use((string) $usedClass); - } - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseReplaceQualifierWithImport.php b/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseReplaceQualifierWithImport.php deleted file mode 100644 index 25d7663a4e..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseReplaceQualifierWithImport.php +++ /dev/null @@ -1,78 +0,0 @@ -reflector - ->reflectOffset($sourceCode, $offset) - ->nodeContext(); - $type = $nodeContext->type(); - - if (!$type instanceof ClassType) { - return new TextDocumentEdits($sourceCode->uri(), TextEdits::none()); - } - - $textEdits = $this->getTextEditForImports($sourceCode, $type); - - $newClassName = $type->name()->short(); - $position = $nodeContext->symbol()->position(); - - return new TextDocumentEdits( - $sourceCode->uri(), - $textEdits->merge(TextEdits::fromTextEdits([ - TextEdit::create( - $position->start()->toInt(), - $position->end()->toInt() - $position->start()->toInt(), - $newClassName - ) - ])) - ); - } - - public function canReplaceWithImport(SourceCode $source, int $offset): bool - { - $node = $this->parser->get($source); - $targetNode = $node->getDescendantNodeAtPosition($offset); - - if ($targetNode instanceof QualifiedName) { - return $targetNode->isFullyQualifiedName(); - } - - return false; - } - - private function getTextEditForImports(SourceCode $sourceCode, ClassType $type): TextEdits - { - $sourceBuilder = $this->factory->fromSource($sourceCode); - $sourceBuilder->use((string) $type->name()); - - return $this->updater->textEditsFor( - $sourceBuilder->build(), - $sourceCode - ); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/AddMissingProperties.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/AddMissingProperties.php deleted file mode 100644 index 69c81df5d8..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/AddMissingProperties.php +++ /dev/null @@ -1,115 +0,0 @@ - - */ - public function transform(SourceCode $code): Promise - { - return call(function () use ($code) { - $rootNode = $this->parser->get($code); - $wrDiagnostics = yield $this->reflector->diagnostics($code); - $sourceBuilder = SourceCodeBuilder::create(); - - /** @var AssignmentToMissingPropertyDiagnostic $diagnostic */ - foreach ($wrDiagnostics->byClass(AssignmentToMissingPropertyDiagnostic::class) as $diagnostic) { - $class = $this->reflector->reflectClassLike($diagnostic->classType()); - $classBuilder = $this->resolveClassBuilder($sourceBuilder, $class); - $type = $diagnostic->propertyType(); - - $propertyBuilder = $classBuilder - ->property($diagnostic->propertyName()) - ->visibility('private'); - - if ($type->isDefined()) { - foreach ($type->allTypes()->classLike() as $importClass) { - $sourceBuilder->use($importClass->name()->__toString()); - } - $type = $type->toLocalType($class->scope()); - $propertyBuilder->type($type->toPhpString(), $type); - $propertyBuilder->docType((string)$type->generalize()); - - if ($diagnostic->isSubscriptAssignment()) { - $propertyBuilder->defaultValue([]); - } - } - } - - if (isset($class)) { - $sourceBuilder->namespace($class->name()->namespace()); - } - - return $this->updater->textEditsFor( - $sourceBuilder->build(), - $code - ); - }); - } - - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - return call(function () use ($code) { - $wrDiagnostics = yield $this->reflector->diagnostics($code); - $diagnostics = []; - - /** @var AssignmentToMissingPropertyDiagnostic $diagnostic */ - foreach ($wrDiagnostics->byClass(AssignmentToMissingPropertyDiagnostic::class) as $diagnostic) { - $diagnostics[] = new Diagnostic( - $diagnostic->range(), - $diagnostic->message(), - Diagnostic::WARNING - ); - } - - return new Diagnostics($diagnostics); - }); - } - - /** - * @return TraitBuilder|ClassBuilder - */ - private function resolveClassBuilder(SourceCodeBuilder $sourceBuilder, ReflectionClassLike $class): ClassLikeBuilder - { - $name = $class->name()->short(); - - if ($class instanceof ReflectionTrait) { - return $sourceBuilder->trait($name); - } - - return $sourceBuilder->class($name); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/AddOverrideAttributeTransformer.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/AddOverrideAttributeTransformer.php deleted file mode 100644 index 51dd052135..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/AddOverrideAttributeTransformer.php +++ /dev/null @@ -1,177 +0,0 @@ - - */ - public function transform(SourceCode $code): Promise - { - return new Success((function () use ($code) { - $edits = []; - foreach ($this->methodsNeedingAttribute($code) as [$method, $node]) { - $edits[] = $this->createEdit($node, $code->__toString()); - } - - return TextEdits::fromTextEdits($edits); - })()); - } - - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - return new Success((function () use ($code) { - $diagnostics = []; - foreach ($this->methodsNeedingAttribute($code) as [$method, $node]) { - $diagnostics[] = new Diagnostic( - $method->nameRange(), - sprintf( - 'Method "%s" overrides a parent method but has no #[\Override] attribute', - $method->name(), - ), - Diagnostic::HINT - ); - } - - /** @phpstan-ignore-next-line */ - return Diagnostics::fromArray($diagnostics); - })()); - } - - /** - * @return list - */ - private function methodsNeedingAttribute(SourceCode $code): array - { - if (version_compare($this->phpVersion, '8.3', '<')) { - return []; - } - - $methodNodes = $this->methodNodesByNameOffset($code); - $methods = []; - - foreach ($this->reflector->reflectClassesIn($code)->classes() as $class) { - foreach ($class->methods()->belongingTo($class->name()) as $method) { - $node = $methodNodes[$method->nameRange()->start()->toInt()] ?? null; - - if (null === $node) { - continue; - } - - if ($this->hasOverrideAttribute($node)) { - continue; - } - - if (!$this->overridesMethod($class, $method->name())) { - continue; - } - - $methods[] = [$method, $node]; - } - } - - return $methods; - } - - private function overridesMethod(ReflectionClass $class, string $methodName): bool - { - // the engine does not consider a parent constructor or a private - // parent method to be overridden, but an interface constructor is - $parent = $class->parent(); - if ( - $parent && '__construct' !== $methodName - && $parent->methods()->has($methodName) - && !$parent->methods()->get($methodName)->visibility()->isPrivate() - ) { - return true; - } - - foreach ($class->interfaces() as $interface) { - if ($interface->methods()->has($methodName)) { - return true; - } - } - - return false; - } - - private function hasOverrideAttribute(MethodDeclaration $node): bool - { - foreach ($node->attributes ?? [] as $attributeGroup) { - foreach ($attributeGroup->attributes->getElements() as $attribute) { - if (!$attribute instanceof Attribute) { - continue; - } - $name = $attribute->name; - $name = $name instanceof Node ? $name->getText() : $name->getText($node->getFileContents()); - - if (strtolower(ltrim((string)$name, '\\')) === 'override') { - return true; - } - } - } - - return false; - } - - private function createEdit(MethodDeclaration $node, string $source): TextEdit - { - $start = $node->getStartPosition(); - $indent = ''; - $offset = $start; - while ($offset > 0 && in_array($source[$offset - 1], [' ', "\t"], true)) { - $indent = $source[$offset - 1] . $indent; - $offset--; - } - - if ($offset !== 0 && $source[$offset - 1] !== "\n") { - return TextEdit::create($start, 0, '#[\Override] '); - } - - return TextEdit::create($start, 0, sprintf("#[\Override]\n%s", $indent)); - } - - /** - * @return array - */ - private function methodNodesByNameOffset(SourceCode $code): array - { - $nodes = []; - foreach ($this->astProvider->get($code)->getDescendantNodes() as $node) { - if (!$node instanceof MethodDeclaration || null === $node->name) { - continue; - } - $nodes[$node->name->getStartPosition()] = $node; - } - - return $nodes; - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/CompleteConstructor.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/CompleteConstructor.php deleted file mode 100644 index 32ab1fabfe..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/CompleteConstructor.php +++ /dev/null @@ -1,191 +0,0 @@ - - */ - public function transform(SourceCode $source): Promise - { - if (false === $this->promote) { - return new Success($this->transformAssign($source)); - } - - return new Success($this->transformPromote($source)); - } - - - /** - * @return Promise - */ - public function diagnostics(SourceCode $source): Promise - { - $diagnostics = []; - foreach ($this->candidateClasses($source) as $class) { - $constructMethod = $class->methods()->belongingTo($class->name())->get('__construct'); - assert($constructMethod instanceof ReflectionMethod); - foreach ($constructMethod->parameters()->notPromoted() as $parameter) { - assert($parameter instanceof ReflectionParameter); - $frame = $constructMethod->frame(); - - $isUsed = $frame->locals()->byName($parameter->name())->count() > 0; - $hasProperty = $class->properties()->has($parameter->name()); - - if ($isUsed && $hasProperty) { - continue; - } - - $diagnostics[] = new Diagnostic( - ByteOffsetRange::fromInts( - $parameter->position()->start()->toInt(), - $parameter->position()->end()->toInt() + 5 + strlen($class->name()->__toString()) - ), - sprintf( - 'Parameter "%s" may not have been assigned', - $parameter->name() - ), - Diagnostic::WARNING - ); - } - } - - return new Success(new Diagnostics($diagnostics)); - } - - private function transformAssign(SourceCode $source): TextEdits - { - $edits = []; - $sourceCodeBuilder = SourceCodeBuilder::create(); - - foreach ($this->candidateClasses($source) as $class) { - $classBuilder = $sourceCodeBuilder->class($class->name()->short()); - $methodBuilder = $classBuilder->method('__construct'); - $constructMethod = $class->methods()->get('__construct'); - $methodBody = (string) $constructMethod->body(); - - // Filtering out parameters from the parent class - $nonPromotedParameterNames = $this->getParentClassParamaterNames($class); - $parametersToHandle = array_filter( - iterator_to_array($constructMethod->parameters()->notPromoted()), - fn (ReflectionParameter $parameter) => !in_array($parameter->name(), $nonPromotedParameterNames) - ); - - foreach ($parametersToHandle as $parameter) { - if (preg_match('{this\s*->' . $parameter->name() . '}', $methodBody)) { - continue; - } - $methodBuilder->body()->line('$this->' . $parameter->name() . ' = $' . $parameter->name() .';'); - } - - foreach ($parametersToHandle as $parameter) { - if ($parameter->isPromoted()) { - continue; - } - - assert($parameter instanceof ReflectionParameter); - if (true === $class->properties()->has($parameter->name())) { - continue; - } - - $propertyBuilder = $classBuilder->property($parameter->name()); - $propertyBuilder->visibility($this->visibility); - $parameterType = $parameter->inferredType(); - if ($parameterType->isDefined()) { - $parameterType = $parameterType->toLocalType($class->scope()); - $propertyBuilder->type($parameterType->toPhpString(), $parameterType); - $propertyBuilder->docType((string)$parameterType); - } - } - } - - return $this->updater->textEditsFor($sourceCodeBuilder->build(), $source); - } - - private function transformPromote(SourceCode $source): TextEdits - { - $edits = []; - - foreach ($this->candidateClasses($source) as $class) { - $constructMethod = $class->methods()->get('__construct'); - $nonPromotedParameterNames = $this->getParentClassParamaterNames($class); - foreach ($constructMethod->parameters()->notPromoted() as $parameter) { - if (in_array($parameter->name(), $nonPromotedParameterNames)) { - continue; - } - $edits[] = TextEdit::create($parameter->position()->start()->toInt(), 0, sprintf('%s ', $this->visibility)); - } - } - - return TextEdits::fromTextEdits($edits); - } - - /** - * Get the names of the parent constructor to know which parameters should not be promoted. - * @return array - */ - private function getParentClassParamaterNames(ReflectionClass $class): array - { - $ancestor = $class->parent(); - if ($ancestor === null) { - return []; - } - - if (!$ancestor->methods()->has('__construct')) { - return []; - } - - $parameters = []; - foreach ($ancestor->methods()->get('__construct')->parameters() as $parameter) { - $parameters[] = $parameter->name(); - } - - return $parameters; - } - - /** - * @return Generator - */ - private function candidateClasses(SourceCode $source): Generator - { - $classes = $this->reflector->reflectClassesIn($source)->classes(); - foreach ($classes as $class) { - if ($class instanceof ReflectionInterface) { - continue; - } - - if (!$class->methods()->belongingTo($class->name())->has('__construct')) { - continue; - } - - yield $class; - } - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/ImplementContracts.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/ImplementContracts.php deleted file mode 100644 index 246e06c4d8..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/ImplementContracts.php +++ /dev/null @@ -1,142 +0,0 @@ - - */ - public function diagnostics(SourceCode $source): Promise - { - return new Success((function () use ($source) { - $diagnostics = []; - $classes = $this->reflector->reflectClassesIn($source); - foreach ($classes->concrete() as $class) { - assert($class instanceof ReflectionClass); - $missingMethods = $this->missingClassMethods($class); - if (0 === count($missingMethods)) { - continue; - } - $diagnostics[] = new Diagnostic( - ByteOffsetRange::fromInts( - $class->position()->start()->toInt(), - $class->position()->start()->toInt() + 5 + strlen($class->name()->__toString()) - ), - sprintf( - 'Missing methods "%s"', - implode('", "', array_map(function (ReflectionMethod $method) { - return $method->name(); - }, $missingMethods)) - ), - Diagnostic::ERROR - ); - } - - return new Diagnostics($diagnostics); - })()); - } - - /** - * @return Promise - */ - public function transform(SourceCode $source): Promise - { - return new Success((function () use ($source) { - $classes = $this->reflector->reflectClassesIn($source); - $edits = []; - $sourceCodeBuilder = SourceCodeBuilder::create(); - - /** @var ReflectionClass $class */ - foreach ($classes->concrete() as $class) { - $classBuilder = $sourceCodeBuilder->class($class->name()->short()); - $missingMethods = $this->missingClassMethods($class); - - if ($missingMethods === []) { - continue; - } - - /** @var ReflectionMethod $missingMethod */ - foreach ($missingMethods as $missingMethod) { - $builder = $this->factory->fromSource($missingMethod->declaringClass()->sourceCode()); - $methodBuilder = $builder->classLike( - $missingMethod->declaringClass()->name()->short() - )->method($missingMethod->name()); - - $missingMethodReturnType = $missingMethod->returnType(); - foreach ($missingMethodReturnType->allTypes()->classLike() as $type) { - $sourceCodeBuilder->use($type->name()); - } - - foreach ($missingMethod->parameters() as $parameter) { - $parameterType = $parameter->type(); - foreach ($parameterType->allTypes()->classLike() as $classType) { - if ($classType->name()->namespace() != $class->name()->namespace()) { - $sourceCodeBuilder->use($classType->name()); - } - } - } - - $classBuilder->add($methodBuilder); - } - } - - return $this->updater->textEditsFor($sourceCodeBuilder->build(), $source); - })()); - } - - private function missingClassMethods(ReflectionClass $class): array - { - $methods = []; - $reflectionMethods = $class->methods(); - foreach ($class->interfaces() as $interface) { - foreach ($interface->methods() as $method) { - if ($reflectionMethods->has($method->name())) { - continue; - } - - $methods[] = $method; - } - } - - foreach ($class->methods()->abstract() as $method) { - assert($method instanceof ReflectionMethod); - if ($method->declaringClass()->name() == $class->name()) { - continue; - } - - foreach ($class->traits() as $trait) { - if ($trait->methods()->has($method->name())) { - continue 2; - } - } - - - $methods[] = $method; - } - - return $methods; - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php deleted file mode 100644 index 7b6f58e256..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php +++ /dev/null @@ -1,136 +0,0 @@ - - */ - private array $fixed = []; - - public function __construct( - private Reflector $reflector, - private AstProvider $parser - ) { - } - - /** - * @return Promise - */ - public function transform(SourceCode $code): Promise - { - return call(function () use ($code) { - $rootNode = $this->parser->get($code); - $edits = []; - - foreach ((yield $this->reflector->diagnostics($code))->byClass(UnusedImportDiagnostic::class) as $unusedImport) { - $importNode = $rootNode->getDescendantNodeAtPosition($unusedImport->range()->start()->toInt()); - - if (!$importNode instanceof QualifiedName) { - continue; - } - - $list = $importNode->getFirstAncestor(NamespaceUseClause::class); - - if (!$list instanceof NamespaceUseClause) { - continue; - } - - if ($list->groupClauses) { - if ($edit = $this->forGroupClause($importNode, $list)) { - $edits[] = $edit; - } - continue; - } - - // there is exactly one element - $declaration = $importNode->getFirstAncestor(NamespaceUseDeclaration::class); - if (null === $declaration) { - continue; - } - $length = $declaration->getEndPosition() - $declaration->getStartPosition(); - - if (substr($code->__toString(), $declaration->getEndPosition(), 1) === "\n") { - $length++; - } - - $edits[] = TextEdit::create( - $declaration->getStartPosition(), - $length, - '' - ); - } - - return TextEdits::fromTextEdits($edits); - }); - } - - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - return call(function () use ($code) { - $diagnostics = []; - foreach ((yield $this->reflector->diagnostics($code))->byClass(UnusedImportDiagnostic::class) as $unusedClass) { - $diagnostics[] = new Diagnostic( - $unusedClass->range(), - $unusedClass->message(), - Diagnostic::WARNING - ); - } - - return new Diagnostics($diagnostics); - }); - } - - private function forGroupClause(QualifiedName $importNode, NamespaceUseClause $list): ?TextEdit - { - $fixed = spl_object_id($list); - if (isset($this->fixed[$fixed])) { - return null; - } - $this->fixed[$fixed] = true; - - $names = []; - foreach ($list->groupClauses?->children ?: [] as $groupClause) { - if (!$groupClause instanceof NamespaceUseGroupClause) { - continue; - } - - if ($groupClause->namespaceName->__toString() === $importNode->__toString()) { - continue; - } - $names[] = $groupClause->__toString(); - } - - $groupClauses = $list->groupClauses; - - if (null === $groupClauses) { - return null; - } - - return TextEdit::create( - $groupClauses->getStartPosition(), - $groupClauses->getEndPosition() - $groupClauses->getStartPosition(), - implode(', ', $names) - ); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php deleted file mode 100644 index 2e19d5c297..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php +++ /dev/null @@ -1,103 +0,0 @@ - - */ - public function transform(SourceCode $code): Promise - { - return call(function () use ($code) { - $diagnostics = yield $this->wrDiagnostics($code); - $builder = $this->builderFactory->fromSource($code); - - $class = null; - $docblocks = []; - foreach ($diagnostics as $diagnostic) { - /** @var DocblockMissingClassGenericDiagnostic $diagnostic */ - $class = $this->reflector->reflectClassLike($diagnostic->className()); - - $classBuilder = $builder->classLike($class->name()->short()); - - foreach ($diagnostic->missingGenericType()->allTypes()->classLike() as $classType) { - $builder->use($classType->name()->__toString()); - } - - $tag = match($diagnostic->isExtends()) { - true => new ExtendsTagPrototype( - $diagnostic->missingGenericType(), - ), - false => new ImplementsTagPrototype( - $diagnostic->missingGenericType(), - ), - }; - $classBuilder->docblock( - $this->docblockUpdater->set( - $classBuilder->getDocblock() ? $classBuilder->getDocblock()->__toString() : $class->docblock()->raw(), - $tag - ) - ); - } - - return $this->updater->textEditsFor($builder->build(), $code); - }); - } - - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - return call(function () use ($code) { - $diagnostics = []; - - $missings = yield $this->wrDiagnostics($code); - - foreach ($missings as $missing) { - $diagnostics[] = new Diagnostic( - $missing->range(), - $missing->message(), - Diagnostic::WARNING - ); - } - - /** @phpstan-ignore-next-line */ - return Diagnostics::fromArray($diagnostics); - }); - } - - /** - * @return Promise - */ - private function wrDiagnostics(SourceCode $code): Promise - { - return call(function () use ($code) { - return (yield $this->reflector->diagnostics($code))->byClass(DocblockMissingClassGenericDiagnostic::class); - }); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php deleted file mode 100644 index 4745d32a7b..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php +++ /dev/null @@ -1,108 +0,0 @@ - - */ - public function transform(SourceCode $code): Promise - { - return call(function () use ($code) { - $diagnostics = yield $this->methodsThatNeedFixing($code); - $builder = $this->builderFactory->fromSource($code); - - $class = null; - $docblocks = []; - foreach ($diagnostics as $diagnostic) { - $class = $this->reflector->reflectClassLike($diagnostic->classType()); - $method = $class->methods()->get($diagnostic->methodName()); - - $classBuilder = $builder->classLike($method->class()->name()->short()); - $methodBuilder = $classBuilder->method($method->name()); - - foreach ($diagnostic->paramType()->allTypes()->classLike() as $classType) { - $builder->use($classType->name()->__toString()); - } - - $methodBuilder->docblock( - $this->docblockUpdater->set( - $methodBuilder->getDocblock() ? $methodBuilder->getDocblock()->__toString() : $method->docblock()->raw(), - new ParamTagPrototype( - $diagnostic->paramName(), - $diagnostic->paramType()->toLocalType($method->scope()) - ) - ) - ); - } - - return $this->updater->textEditsFor($builder->build(), $code); - }); - } - - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - return call(function () use ($code) { - $diagnostics = []; - - $missings = yield $this->methodsThatNeedFixing($code); - - foreach ($missings as $missing) { - $diagnostics[] = new Diagnostic( - $missing->range(), - sprintf( - 'Missing @param %s', - $missing->paramName(), - ), - Diagnostic::WARNING - ); - } - - /** @phpstan-ignore-next-line */ - return Diagnostics::fromArray($diagnostics); - }); - } - - /** - * @return Promise - */ - private function methodsThatNeedFixing(SourceCode $code): Promise - { - return call(function () use ($code) { - $missings = []; - $diagnostics = (yield $this->reflector->diagnostics($code))->byClass(DocblockMissingParamDiagnostic::class); - - foreach ($diagnostics as $diagnostic) { - $missings[] = $diagnostic; - } - - return $missings; - }); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php deleted file mode 100644 index f21d45b8af..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php +++ /dev/null @@ -1,111 +0,0 @@ - - */ - public function transform(SourceCode $code): Promise - { - return call(function () use ($code) { - $diagnostics = yield $this->methodsThatNeedFixing($code); - $builder = $this->builderFactory->fromSource($code); - - $class = null; - foreach ($diagnostics as $diagnostic) { - $class = $this->reflector->reflectClassLike($diagnostic->classType()); - $method = $class->methods()->get($diagnostic->methodName()); - - $classBuilder = $builder->classLike($method->class()->name()->short()); - $methodBuilder = $classBuilder->method($method->name()); - $replacement = $method->frame()->returnType(); - $localReplacement = $replacement->toLocalType($method->scope())->generalize(); - - foreach ($replacement->allTypes()->classLike() as $classType) { - $builder->use($classType->toPhpString()); - } - - $methodBuilder->docblock( - $this->docblockUpdater->set( - $methodBuilder->getDocblock() ? $methodBuilder->getDocblock()->__toString() : $method->docblock()->raw(), - new ReturnTagPrototype( - $localReplacement - ) - ) - ); - } - - return $this->updater->textEditsFor($builder->build(), $code); - }); - } - - /** - * @return Diagnostics - */ - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - return call(function () use ($code) { - $diagnostics = []; - - $missingDocblocks = yield $this->methodsThatNeedFixing($code); - - foreach ($missingDocblocks as $missingDocblock) { - $diagnostics[] = new Diagnostic( - $missingDocblock->range(), - sprintf( - 'Missing @return %s', - $missingDocblock->actualReturnType(), - ), - Diagnostic::WARNING - ); - } - - /** @phpstan-ignore-next-line */ - return Diagnostics::fromArray($diagnostics); - }); - } - - /** - * @return Promise - */ - private function methodsThatNeedFixing(SourceCode $code): Promise - { - return call(function () use ($code) { - $missingMethods = []; - $diagnostics = (yield $this->reflector->diagnostics($code))->byClasses(DocblockMissingReturnTypeDiagnostic::class); - - foreach ($diagnostics as $diagnostic) { - $missingMethods[] = $diagnostic; - } - - return $missingMethods; - }); - } -} diff --git a/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php b/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php deleted file mode 100644 index f61e942b8c..0000000000 --- a/lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php +++ /dev/null @@ -1,110 +0,0 @@ - - */ - public function transform(SourceCode $code): Promise - { - return call(function () use ($code) { - $methods = yield $this->methodsThatNeedFixing($code); - $builder = $this->builderFactory->fromSource($code); - - $class = null; - foreach ($methods as $method) { - $classBuilder = $builder->class($method->class()->name()->short()); - $methodBuilder = $classBuilder->method($method->name()); - $replacement = $this->returnType($method); - $localReplacement = $replacement->toLocalType($method->scope()); - $notNullReplacement = $replacement->stripNullable(); - - foreach ($replacement->allTypes()->classLike() as $classType) { - $builder->use($classType->name()); - } - - $methodBuilder->returnType($localReplacement->reduce()->toPhpString(), $localReplacement->reduce()); - } - - return $this->updater->textEditsFor($builder->build(), $code); - }); - } - - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - return call(function () use ($code) { - $wrDiagnostics = (yield $this->reflector->diagnostics($code))->byClass(MissingReturnTypeDiagnostic::class); - $diagnostics = []; - - /** @var MissingReturnTypeDiagnostic $diagnostic */ - foreach ($wrDiagnostics as $diagnostic) { - if (!$diagnostic->returnType()->isDefined()) { - continue; - } - - $diagnostics[] = new Diagnostic( - $diagnostic->range(), - $diagnostic->message(), - Diagnostic::WARNING, - ); - } - - /** @phpstan-ignore-next-line */ - return Diagnostics::fromArray($diagnostics); - }); - } - - /** - * @return Promise> - */ - private function methodsThatNeedFixing(SourceCode $code): Promise - { - return call(function () use ($code) { - $diagnostics = (yield $this->reflector->diagnostics($code))->byClass(MissingReturnTypeDiagnostic::class); - $methods = []; - /** @var MissingReturnTypeDiagnostic $diagnostic */ - foreach ($diagnostics as $diagnostic) { - if (!$diagnostic->returnType()->isDefined()) { - continue; - } - - $class = $this->reflector->reflectClassLike($diagnostic->classType()); - $methods[] = $class->methods()->get($diagnostic->methodName()); - } - - return $methods; - }); - } - - private function returnType(ReflectionMethod $method): Type - { - $returnType = $method->frame()->returnType(); - return $returnType; - } -} diff --git a/lib/CodeTransform/CodeTransform.php b/lib/CodeTransform/CodeTransform.php deleted file mode 100644 index 9cb7ce5113..0000000000 --- a/lib/CodeTransform/CodeTransform.php +++ /dev/null @@ -1,35 +0,0 @@ -transformers; - } - - /** - * @param mixed $code - */ - public function transform($code, array $transformations): SourceCode - { - $code = SourceCode::fromUnknown($code); - $transformers = $this->transformers->in($transformations); - - return wait($transformers->applyTo($code)); - } -} diff --git a/lib/CodeTransform/Domain/AbstractCollection.php b/lib/CodeTransform/Domain/AbstractCollection.php deleted file mode 100644 index 2aededc945..0000000000 --- a/lib/CodeTransform/Domain/AbstractCollection.php +++ /dev/null @@ -1,80 +0,0 @@ - - */ -abstract class AbstractCollection implements IteratorAggregate, Countable -{ - /** - * @var array - */ - private array $elements = []; - - /** - * @param T[] $elements - */ - final public function __construct(array $elements) - { - foreach ($elements as $name => $element) { - $type = $this->type(); - if (false === $element instanceof $type) { - throw new InvalidArgumentException(sprintf( - 'Collection element must be instanceof "%s"', - $type - )); - } - $this->elements[(string)$name] = $element; - } - } - - /** - * @return static - * @param T[] $elements - */ - public static function fromArray(array $elements) - { - return new static($elements); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->elements); - } - - public function names(): array - { - return array_keys($this->elements); - } - - /** - * @return T - */ - public function get(string $name) - { - if (!isset($this->elements[$name])) { - throw new InvalidArgumentException(sprintf( - 'Generator "%s" not known, known elements: "%s"', - $name, - implode('", "', array_keys($this->elements)) - )); - } - - return $this->elements[$name]; - } - - public function count(): int - { - return count($this->elements); - } - - abstract protected function type(): string; -} diff --git a/lib/CodeTransform/Domain/ClassName.php b/lib/CodeTransform/Domain/ClassName.php deleted file mode 100644 index fd7db243f8..0000000000 --- a/lib/CodeTransform/Domain/ClassName.php +++ /dev/null @@ -1,48 +0,0 @@ -name = $name; - } - - public function __toString() - { - return $this->name; - } - - public static function fromString(string $name): self - { - return new self($name); - } - - public function namespace(): string - { - if (false === $pos = strrpos($this->name, '\\')) { - return ''; - } - - return substr($this->name, 0, $pos ?: 0); - } - - public function short(): string - { - if (false === $pos = strrpos($this->name, '\\')) { - return $this->name; - } - - return substr($this->name, $pos + 1); - } -} diff --git a/lib/CodeTransform/Domain/Diagnostic.php b/lib/CodeTransform/Domain/Diagnostic.php deleted file mode 100644 index 6ae7eb8a2c..0000000000 --- a/lib/CodeTransform/Domain/Diagnostic.php +++ /dev/null @@ -1,35 +0,0 @@ -range; - } - - public function severity(): int - { - return $this->severity; - } - - public function message(): string - { - return $this->message; - } -} diff --git a/lib/CodeTransform/Domain/Diagnostics.php b/lib/CodeTransform/Domain/Diagnostics.php deleted file mode 100644 index 215cdb9419..0000000000 --- a/lib/CodeTransform/Domain/Diagnostics.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -class Diagnostics extends AbstractCollection -{ - public static function none(): self - { - return new self([]); - } - - protected function type(): string - { - return Diagnostic::class; - } -} diff --git a/lib/CodeTransform/Domain/DocBlockUpdater.php b/lib/CodeTransform/Domain/DocBlockUpdater.php deleted file mode 100644 index b0e3c6df93..0000000000 --- a/lib/CodeTransform/Domain/DocBlockUpdater.php +++ /dev/null @@ -1,10 +0,0 @@ -end(); - } -} diff --git a/lib/CodeTransform/Domain/DocBlockUpdater/ImplementsTagPrototype.php b/lib/CodeTransform/Domain/DocBlockUpdater/ImplementsTagPrototype.php deleted file mode 100644 index 27fb71e157..0000000000 --- a/lib/CodeTransform/Domain/DocBlockUpdater/ImplementsTagPrototype.php +++ /dev/null @@ -1,26 +0,0 @@ -toString() === $this->type->__toString(); - } - - public function endOffsetFor(TagNode $tag): int - { - assert($tag instanceof ExtendsTag); - return $tag->end(); - } -} diff --git a/lib/CodeTransform/Domain/DocBlockUpdater/ParamTagPrototype.php b/lib/CodeTransform/Domain/DocBlockUpdater/ParamTagPrototype.php deleted file mode 100644 index 4454db5929..0000000000 --- a/lib/CodeTransform/Domain/DocBlockUpdater/ParamTagPrototype.php +++ /dev/null @@ -1,27 +0,0 @@ -paramName(), '$') === $this->name; - } - - public function endOffsetFor(TagNode $tag): int - { - assert($tag instanceof ParamTag); - return $tag->variable ? $tag->variable->end() : $tag->end(); - } -} diff --git a/lib/CodeTransform/Domain/DocBlockUpdater/ReturnTagPrototype.php b/lib/CodeTransform/Domain/DocBlockUpdater/ReturnTagPrototype.php deleted file mode 100644 index 38f1265cdf..0000000000 --- a/lib/CodeTransform/Domain/DocBlockUpdater/ReturnTagPrototype.php +++ /dev/null @@ -1,25 +0,0 @@ -type() ? $tag->type()->end() : $tag->end(); - } -} diff --git a/lib/CodeTransform/Domain/DocBlockUpdater/TagPrototype.php b/lib/CodeTransform/Domain/DocBlockUpdater/TagPrototype.php deleted file mode 100644 index f960ab85d1..0000000000 --- a/lib/CodeTransform/Domain/DocBlockUpdater/TagPrototype.php +++ /dev/null @@ -1,12 +0,0 @@ - - */ -final class Generators extends AbstractCollection -{ - protected function type(): string - { - return Generator::class; - } -} diff --git a/lib/CodeTransform/Domain/Helper/InterestingOffsetFinder.php b/lib/CodeTransform/Domain/Helper/InterestingOffsetFinder.php deleted file mode 100644 index 3b334b6451..0000000000 --- a/lib/CodeTransform/Domain/Helper/InterestingOffsetFinder.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - public function find(TextDocument $sourceCode): Promise; -} diff --git a/lib/CodeTransform/Domain/Helper/MissingMemberFinder/MissingMember.php b/lib/CodeTransform/Domain/Helper/MissingMemberFinder/MissingMember.php deleted file mode 100644 index 0032ce9907..0000000000 --- a/lib/CodeTransform/Domain/Helper/MissingMemberFinder/MissingMember.php +++ /dev/null @@ -1,30 +0,0 @@ -range; - } - - public function name(): string - { - return $this->name; - } - - public function memberType(): string - { - return $this->memberType; - } -} diff --git a/lib/CodeTransform/Domain/NameWithByteOffset.php b/lib/CodeTransform/Domain/NameWithByteOffset.php deleted file mode 100644 index f859d7acfd..0000000000 --- a/lib/CodeTransform/Domain/NameWithByteOffset.php +++ /dev/null @@ -1,49 +0,0 @@ -type = $type; - } - - public function byteOffset(): ByteOffset - { - return $this->byteOffset; - } - - public function name(): Name - { - return $this->name; - } - - public function type(): string - { - return $this->type; - } -} diff --git a/lib/CodeTransform/Domain/NameWithByteOffsets.php b/lib/CodeTransform/Domain/NameWithByteOffsets.php deleted file mode 100644 index 3c14ca4a45..0000000000 --- a/lib/CodeTransform/Domain/NameWithByteOffsets.php +++ /dev/null @@ -1,38 +0,0 @@ - - */ -class NameWithByteOffsets implements IteratorAggregate -{ - private $nameWithByteOffsets; - - public function __construct(NameWithByteOffset ...$nameWithByteOffsets) - { - $this->nameWithByteOffsets = $nameWithByteOffsets; - } - - public function getIterator(): Iterator - { - return new ArrayIterator($this->nameWithByteOffsets); - } - - public function onlyUniqueNames(): self - { - $seen = []; - return new self(...array_filter($this->nameWithByteOffsets, function (NameWithByteOffset $byteOffset) use (&$seen) { - $name = $byteOffset->name()->__toString(); - if (in_array($name, $seen)) { - return false; - } - $seen[] = $name; - return true; - })); - } -} diff --git a/lib/CodeTransform/Domain/Refactor/ByteOffsetRefactor.php b/lib/CodeTransform/Domain/Refactor/ByteOffsetRefactor.php deleted file mode 100644 index 901f4391d4..0000000000 --- a/lib/CodeTransform/Domain/Refactor/ByteOffsetRefactor.php +++ /dev/null @@ -1,12 +0,0 @@ -type()), - $nameImport->alias() - )); - - $this->name = $nameImport->name()->head()->__toString(); - } - - public function name(): string - { - return $this->name; - } -} diff --git a/lib/CodeTransform/Domain/Refactor/ImportClass/ClassIsCurrentClassException.php b/lib/CodeTransform/Domain/Refactor/ImportClass/ClassIsCurrentClassException.php deleted file mode 100644 index 718643e424..0000000000 --- a/lib/CodeTransform/Domain/Refactor/ImportClass/ClassIsCurrentClassException.php +++ /dev/null @@ -1,26 +0,0 @@ -type()), - $nameImport->name()->head() - )); - - $this->name = $nameImport->name()->head()->__toString(); - } - - public function name(): string - { - return $this->name; - } -} diff --git a/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyImportedException.php b/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyImportedException.php deleted file mode 100644 index 82e1a53dbd..0000000000 --- a/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyImportedException.php +++ /dev/null @@ -1,37 +0,0 @@ -type()), - $nameImport->name()->head() - )); - - $this->name = $nameImport->name()->head()->__toString(); - } - - public function name(): string - { - return $this->name; - } - - public function existingName(): string - { - return $this->existingName; - } - - public function existingFQN(): string - { - return $this->existingFQN; - } -} diff --git a/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyInNamespaceException.php b/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyInNamespaceException.php deleted file mode 100644 index 3acee05fd4..0000000000 --- a/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyInNamespaceException.php +++ /dev/null @@ -1,26 +0,0 @@ -type()), - $nameImport->name()->head() - )); - - $this->name = $nameImport->name()->head()->__toString(); - } - - public function name(): string - { - return $this->name; - } -} diff --git a/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyUsedException.php b/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyUsedException.php deleted file mode 100644 index 0d4291e01a..0000000000 --- a/lib/CodeTransform/Domain/Refactor/ImportClass/NameAlreadyUsedException.php +++ /dev/null @@ -1,9 +0,0 @@ -alias; - } - - public function name(): FullyQualifiedName - { - return $this->name; - } - - public function isFunction(): bool - { - return $this->type === self::TYPE_FUNCTION; - } - - public function isClass(): bool - { - return $this->type === self::TYPE_CLASS; - } - - public function type(): string - { - return $this->type; - } -} diff --git a/lib/CodeTransform/Domain/Refactor/ImportName.php b/lib/CodeTransform/Domain/Refactor/ImportName.php deleted file mode 100644 index 4c091178a8..0000000000 --- a/lib/CodeTransform/Domain/Refactor/ImportName.php +++ /dev/null @@ -1,18 +0,0 @@ -code; - } - - public static function fromString(string $code): SourceCode - { - return new self($code, TextDocumentUri::fromString('untitled:Untitled')); - } - - public static function fromStringAndPath(string $code, ?string $path = null): SourceCode - { - return new self($code, TextDocumentUri::fromString($path)); - } - - public function withSource(string $code): SourceCode - { - return new self($code, $this->uri); - } - - public function withPath(string $path): SourceCode - { - return new self($this->code, TextDocumentUri::fromString($path)); - } - - public function path(): string - { - return $this->uri->path(); - } - - public function extractSelection(int $offsetStart, int $offsetEnd): string - { - return substr($this->code, $offsetStart, $offsetEnd - $offsetStart); - } - - public function replaceSelection(string $replacement, int $offsetStart, int $offsetEnd): SourceCode - { - $start = substr($this->code, 0, $offsetStart); - $end = substr($this->code, $offsetEnd); - - return self::withSource($start . $replacement . $end); - } - - /** - * @param mixed $code - */ - public static function fromUnknown($code): SourceCode - { - if ($code instanceof SourceCode) { - return $code; - } - - if (is_string($code)) { - return self::fromString($code); - } - - throw new RuntimeException(sprintf( - 'Do not know how to create source code object from "%s"', - gettype($code) - )); - } - - public function uri(): TextDocumentUri - { - return $this->uri; - } - - public function language(): TextDocumentLanguage - { - return TextDocumentLanguage::fromString('php'); - } - - /** - * Create a SourceCode class from the standard TextDocument. In the long - * term this package should be updated to work with this TextDocument - * interface and not depend on it's own representation. - */ - public static function fromTextDocument(TextDocument $textDocument): self - { - if (null === $textDocument->uri()) { - throw new RuntimeException( - 'Cannot create source code from text document with no URI' - ); - } - return new self($textDocument->__toString(), $textDocument->uri()); - } - - public function uriOrThrow(): TextDocumentUri - { - return $this->uri; - } -} diff --git a/lib/CodeTransform/Domain/Transformer.php b/lib/CodeTransform/Domain/Transformer.php deleted file mode 100644 index ea5b5760f3..0000000000 --- a/lib/CodeTransform/Domain/Transformer.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function transform(SourceCode $code): Promise; - - /** - * Return the issues that this transform will fix. - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise; -} diff --git a/lib/CodeTransform/Domain/Transformers.php b/lib/CodeTransform/Domain/Transformers.php deleted file mode 100644 index f3f699e03b..0000000000 --- a/lib/CodeTransform/Domain/Transformers.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ -final class Transformers extends AbstractCollection -{ - /** - * @return Promise - */ - public function applyTo(SourceCode $code): Promise - { - return call(function () use ($code) { - foreach ($this as $transformer) { - assert($transformer instanceof Transformer); - $code = SourceCode::fromStringAndPath( - (yield $transformer->transform($code))->apply($code), - $code->uri()->__toString() - ); - } - - return $code; - }); - } - - public function in(array $transformerNames): self - { - $transformers = []; - - foreach ($transformerNames as $transformerName) { - $transformers[] = $this->get($transformerName); - } - - return new self($transformers); - } - - protected function type(): string - { - return Transformer::class; - } -} diff --git a/lib/CodeTransform/Domain/Utils/TextUtils.php b/lib/CodeTransform/Domain/Utils/TextUtils.php deleted file mode 100644 index 7990b4d546..0000000000 --- a/lib/CodeTransform/Domain/Utils/TextUtils.php +++ /dev/null @@ -1,58 +0,0 @@ - $line) { - if ($line === '') { - continue; - } - - preg_match('{^(\s+).*$}', $line, $matches); - - if (false === isset($matches[1])) { - $indentation = 0; - break; - } - - $count = mb_strlen($matches[1]); - - if (null === $indentation || $count < $indentation) { - $indentation = $count; - } - } - - if (null === $indentation) { - $indentation = 0; - } - - foreach ($lines as &$line) { - $line = substr($line, $indentation); - } - - return trim(implode("\n", $lines), "\n"); - } - - public static function stringIndentation(string $string): int - { - $lines = explode("\n", $string); - - if (empty($lines)) { - return 0; - } - - preg_match('{^(\s+).*$}m', $lines[0], $matches); - - if (false === isset($matches[1])) { - return 0; - } - - return mb_strlen($matches[1]); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/AdapterTestCase.php b/lib/CodeTransform/Tests/Adapter/AdapterTestCase.php deleted file mode 100644 index 8f1f140434..0000000000 --- a/lib/CodeTransform/Tests/Adapter/AdapterTestCase.php +++ /dev/null @@ -1,51 +0,0 @@ -renderer()); - } - - protected function workspace(): Workspace - { - return Workspace::create(__DIR__ . '/../Workspace'); - } - - protected function sourceExpected($manifestPath) - { - $workspace = $this->workspace(); - $workspace->reset(); - - if (!file_exists($manifestPath)) { - touch($manifestPath); - } - - $workspace->loadManifest(file_get_contents($manifestPath)); - $source = $workspace->getContents('source'); - $expected = $workspace->getContents('expected'); - - return [ $source, $expected ]; - } - - protected function sourceExpectedAndOffset($manifestPath) - { - [$source, $expected] = $this->sourceExpected($manifestPath); - [$source, $offsetStart, $offsetEnd] = ExtractOffset::fromSource($source); - - return [ $source, $expected, $offsetStart, $offsetEnd ]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/DocblockParser/ParserDocblockUpdaterTest.php b/lib/CodeTransform/Tests/Adapter/DocblockParser/ParserDocblockUpdaterTest.php deleted file mode 100644 index 49eb603be8..0000000000 --- a/lib/CodeTransform/Tests/Adapter/DocblockParser/ParserDocblockUpdaterTest.php +++ /dev/null @@ -1,145 +0,0 @@ -createUpdater()->set( - '/** @return Foobar */', - new ReturnTagPrototype(TypeFactory::string()) - )); - } - - public function testUpdateParam(): void - { - self::assertEquals('/** @param string $foo */', $this->createUpdater()->set( - '/** @param Foobar $foo */', - new ParamTagPrototype('foo', TypeFactory::string()) - )); - } - - public function testUpdateParamWithReturnType(): void - { - self::assertEquals( - <<<'EOT' - /** - * @return array - * @param string $foo - */ - EOT, - $this->createUpdater()->set( - <<<'EOT' - /** - * @return array - */ - EOT, - new ParamTagPrototype('foo', TypeFactory::string()) - ) - ); - } - - public function testUpdateReturnTypeWithMultipleTags(): void - { - self::assertEquals( - <<<'EOT' - /** - * This is some text - * @param Foobar - * @return string - * @return string - */ - EOT, - $this->createUpdater()->set( - <<<'EOT' - /** - * This is some text - * @param Foobar - * @return Bazboo - * @return Foobar - */ - EOT, - new ReturnTagPrototype(TypeFactory::string()) - ) - ); - } - - public function testAddIfNotExisting(): void - { - self::assertEquals('/** @return string */', $this->createUpdater()->set( - '/** */', - new ReturnTagPrototype(TypeFactory::string()) - )); - } - - public function testAddIfNotExistingMultiline0(): void - { - self::assertEquals( - <<<'EOT' - /** - * @return string - */ - EOT, - $this->createUpdater()->set( - <<<'EOT' - /** - */ - EOT, - new ReturnTagPrototype(TypeFactory::string()) - ) - ); - } - - public function testAddIfNotExistingMultiline(): void - { - self::assertEquals( - <<<'EOT' - /** - * - * @return string - */ - EOT, - $this->createUpdater()->set( - <<<'EOT' - /** - * - */ - EOT, - new ReturnTagPrototype(TypeFactory::string()) - ) - ); - } - - public function testAddDocblock(): void - { - self::assertEquals( - <<<'EOT' - - /** - * @return string - */ - - EOT, - $this->createUpdater()->set( - <<<'EOT' - EOT, - new ReturnTagPrototype(TypeFactory::string()) - ) - ); - } - - - private function createUpdater(): ParserDocblockUpdater - { - return (new ParserDocblockUpdater(DocblockParser::create(), new TextFormat())); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/Native/GenerateNew/ClassGeneratorTest.php b/lib/CodeTransform/Tests/Adapter/Native/GenerateNew/ClassGeneratorTest.php deleted file mode 100644 index 2718161179..0000000000 --- a/lib/CodeTransform/Tests/Adapter/Native/GenerateNew/ClassGeneratorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -renderer()); - $code = $generator->generateNew($className); - - $this->assertEquals(<<<'EOT' - workspace(); - $workspace->reset(); - $workspace->loadManifest((string)file_get_contents(__DIR__ . '/fixtures/' . $test)); - $expected = $workspace->getContents('expected'); - - $transformer = $this->createTransformer($workspace); - - $source = SourceCode::fromStringAndPath( - $workspace->getContents($filePath), - $this->workspace()->path($filePath) - ); - - $diagnostics = wait($transformer->diagnostics($source)); - $this->assertCount($diagnosticCount, $diagnostics); - $transformed = wait($transformer->transform($source)); - - $this->assertEquals(trim($expected), trim($transformed->apply($source))); - } - - /** - * @return Generator - */ - public static function provideFixClassName(): Generator - { - yield 'no op' => [ - 'FileOne.php', - 'fixNamespace0.test', - 0 - ]; - yield 'fix file with missing namespace' => [ - 'PathTo/FileOne.php', - 'fixNamespace1.test', - 1 - ]; - yield 'fix file with namespace' => [ - 'PathTo/FileOne.php', - 'fixNamespace2.test', - 1 - ]; - yield 'fix class name' => [ - 'FileOne.php', - 'fixNamespace3.test', - 1 - ]; - yield 'fix class name with same line bracket' => [ - 'FileOne.php', - 'fixNamespace4.test', - 1 - ]; - yield 'fix class name and namespace' => [ - 'Phpactor/Test/Foobar/FileOne.php', - 'fixNamespace5.test', - 2 - ]; - } - - public function testThrowsExceptionIfSourceCodeHasNoPath(): void - { - $this->expectException(TransformException::class); - $this->expectExceptionMessage('Source is not a file'); - $transformer = $this->createTransformer($this->workspace()); - $transformed = wait($transformer->transform(SourceCode::fromString('hello'))); - } - - public function testOnEmptyFile(): void - { - $workspace = $this->workspace(); - $workspace->reset(); - $workspace->loadManifest(file_get_contents(__DIR__ . '/fixtures/fixNamespace1.test')); - $source = $workspace->getContents('PathTo/FileOne.php'); - $expected = $workspace->getContents('expected'); - $transformer = $this->createTransformer($workspace); - $source = SourceCode::fromStringAndPath('', $this->workspace()->path('/PathTo/FileOne.php')); - $transformed = wait($transformer->transform($source)); - $this->assertEquals(<<<'EOT' - apply($source)); - } - - private function initComposer(Workspace $workspace) - { - if (self::$composerAutoload) { - return self::$composerAutoload; - } - - $composer = <<<'EOT' - { - "autoload": { - "psr-4": { - "": "" - } - } - } - EOT - ; - file_put_contents($workspace->path('/composer.json'), $composer); - $cwd = getcwd(); - chdir($workspace->path('/')); - exec('composer dumpautoload'); - chdir($cwd); - self::$composerAutoload = require_once($workspace->path('/vendor/autoload.php')); - - return $this->initComposer($workspace); - } - - private function createTransformer(Workspace $workspace): ClassNameFixerTransformer - { - $autoload = $this->initComposer($workspace); - $fileToClass = new ComposerFileToClass($autoload); - $transformer = new ClassNameFixerTransformer($fileToClass); - return $transformer; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/ClassToFile/Transformer/fixtures/fixNamespace0.test b/lib/CodeTransform/Tests/Adapter/TolerantParser/ClassToFile/Transformer/fixtures/fixNamespace0.test deleted file mode 100644 index 022f2f27f3..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/ClassToFile/Transformer/fixtures/fixNamespace0.test +++ /dev/null @@ -1,18 +0,0 @@ -// File: FileOne.php -importNameFromTestFile('class', $test, $name, $alias); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - abstract public static function provideImportClass(): Generator; - - public function testThrowsExceptionIfClassAlreadyImported(): void - { - $this->expectException(NameAlreadyImportedException::class); - $this->expectExceptionMessage('Class "DateTime" is already imported'); - $this->importNameFromTestFile('class', 'importClass1.test', 'DateTime'); - } - - public function testThrowsExceptionIfImportedClassIsTheCurrentClass1(): void - { - $this->expectException(ClassIsCurrentClassException::class); - $this->expectExceptionMessage('Class "Foobar" is the current class'); - $this->importName('expectException(ClassIsCurrentClassException::class); - $this->expectExceptionMessage('Class "Foobar" is the current class'); - $this->importName('expectException(ClassIsCurrentClassException::class); - $this->expectExceptionMessage('Class "Foobar" is the current class'); - $this->importName('expectException(AliasAlreadyUsedException::class); - $this->expectExceptionMessage('Class alias "DateTime" is already used'); - $this->importNameFromTestFile('class', 'importClass1.test', 'Foobar', 'DateTime'); - } - - public function testThrowsNameAlreadyImportedExistingAliasName(): void - { - try { - $this->importName( - 'getMessage()); - self::assertSame('Bar', $error->name()); - self::assertSame('Foo2Bar', $error->existingName()); - self::assertSame('Foo2\Bar', $error->existingFQN()); - } - } - - public function testThrowsNameAlreadyImportedNameInUse(): void - { - try { - $this->importName( - 'getMessage()); - self::assertSame('Bar', $error->name()); - self::assertSame('Bar', $error->existingName()); - self::assertSame('Foo1\Bar', $error->existingFQN()); - } - } - - public function testThrowsNameAlreadyImportedOnlyAliasName(): void - { - try { - $this->importName( - 'getMessage()); - self::assertSame('Bar', $error->name()); - self::assertSame('Foo2Bar', $error->existingName()); - self::assertSame('Foo2\Bar', $error->existingFQN()); - } - } - - public function testThrowsNameAlreadyImportedFunction(): void - { - try { - $this->importName( - 'getMessage()); - self::assertSame('in_array', $error->name()); - self::assertSame('in_array', $error->existingName()); - self::assertSame('in_array', $error->existingFQN()); - } - } - - public function testThrowsNameAlreadyImportedFunctionAlias(): void - { - try { - $this->importName( - 'getMessage()); - self::assertSame('in_array', $error->name()); - self::assertSame('foo_in_array', $error->existingName()); - self::assertSame('in_array', $error->existingFQN()); - } - } - - public function testThrowsExceptionIfImportedClassHasSameNameAsCurrentClassName(): void - { - try { - $this->importName( - 'getMessage()); - self::assertSame('Foobar', $error->name()); - self::assertSame('Foobar', $error->existingName()); - self::assertSame('Barfoo\Foobar', $error->existingFQN()); - } - } - - public function testThrowsExceptionIfImportedClassHasSameNameAsCurrentInterfaceName(): void - { - $this->expectException(NameAlreadyImportedException::class); - $this->importName( - 'expectException(NameAlreadyInNamespaceException::class); - $this->expectExceptionMessage('Class "Barfoo" is in the same namespace as current class'); - $source = <<<'EOT' - importName($source, 64, NameImport::forClass('Barfoo\Barfoo')); - } - - #[DataProvider('provideImportFunction')] - public function testImportFunction(string $test, string $name, ?string $alias = null): void - { - [$expected, $transformed] = $this->importNameFromTestFile('function', $test, $name, $alias); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - abstract public static function provideImportFunction(): Generator; - - abstract protected function importName(string $source, int $offset, NameImport $nameImport, bool $importGlobals = true): TextEdits; - - /** - * @return array{string,string} - */ - private function importNameFromTestFile(string $type, string $test, string $name, ?string $alias = null): array - { - [$source, $expected, $offset] = $this->sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - $edits = TextEdits::none(); - - if ($type === 'class') { - $edits = $this->importName($source, $offset, NameImport::forClass($name, $alias)); - } - - if ($type === 'function') { - $edits = $this->importName($source, $offset, NameImport::forFunction($name, $alias)); - } - - return [$expected, $edits->apply($source)]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantChangeVisiblityTest.php b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantChangeVisiblityTest.php deleted file mode 100644 index 8aa2f14d2c..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantChangeVisiblityTest.php +++ /dev/null @@ -1,35 +0,0 @@ -sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - - $extractMethod = new TolerantChangeVisiblity(); - $transformed = $extractMethod->changeVisiblity(SourceCode::fromString($source), $offsetStart); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator - */ - public static function provideChangeVisibility(): Generator - { - yield 'no op' => [ 'changeVisibility1.test' ]; - yield 'method: from public to protected' => [ 'changeVisibility2.test' ]; - yield 'property: from protected to private' => [ 'changeVisibility3.test' ]; - yield 'constant: from public to protected' => [ 'changeVisibility4.test' ]; - yield 'property: on keyword' => [ 'changeVisibility5.test' ]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php deleted file mode 100644 index c3669e419d..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php +++ /dev/null @@ -1,154 +0,0 @@ -sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - - if ($expectedExceptionMessage) { - $this->expectException(Exception::class); - $this->expectExceptionMessage($expectedExceptionMessage); - } - - $extractMethod = new TolerantExtractExpression(); - - $textEdits = $extractMethod->extractExpression(SourceCode::fromString($source), $offsetStart, $offsetEnd, $name); - $transformed = $textEdits->apply($source); - $this->assertEquals(trim($expected), trim($transformed)); - } - - public static function provideExtractExpression(): Generator - { - yield 'no op' => [ - 'extractExpression1.test', - 'foobar', - ]; - - yield 'extract string literal' => [ - 'extractExpression2.test', - 'foobar', - ]; - - yield 'extract on end position semi-colon: object creation' => [ - 'extractExpression3.test', - 'foobar', - ]; - - yield 'single node' => [ - 'extractExpression4.test', - 'foobar', - ]; - - yield 'single array expression' => [ - 'extractExpression5.test', - 'foobar', - ]; - - yield 'stand-alone expression: whole expression' => [ - 'extractExpression6.test', - 'foobar', - ]; - - yield 'stand-alone expression: partial expression' => [ - 'extractExpression6A.test', - 'foobar', - ]; - - yield 'string concatenation' => [ - 'extractExpression7.test', - 'foobar', - ]; - - yield 'preserve statement indentation: spaces' => [ - 'extractExpression8.test', - 'foobar', - ]; - - yield 'preserve statement indentation: tabs' => [ - 'extractExpression8A.test', - 'foobar', - ]; - - yield 'preserve statement indentation: tabs and comments' => [ - 'extractExpression8B.test', - 'foobar', - ]; - - yield 'extract element in array' => [ - 'extractExpression9.test', - 'foobar', - ]; - - yield 'should not: start on method definition' => [ - 'extractExpression10.test', - 'foobar', - ]; - - yield 'should not: start and end in different methods' => [ - 'extractExpression11.test', - 'foobar', - ]; - - yield 'should not: start and end in different expressions' => [ - 'extractExpression12.test', - 'foobar', - ]; - - yield 'multiline expression' => [ - 'extractExpression13.test', - 'foobar', - ]; - - yield 'should not: inside class member list' => [ - 'extractExpression14.test', - 'foobar', - ]; - - yield 'should not: class declaration' => [ - 'extractExpression15.test', - 'foobar', - ]; - - yield 'should not: on function declaration' => [ - 'extractExpression16.test', - 'foobar', - ]; - - yield 'single assignment expression: method call without semi-colon' => [ - 'extractExpression17.test', - 'foobar', - ]; - - yield 'single assignment expression: method call with semi-colon' => [ - 'extractExpression17A.test', - 'foobar', - ]; - } - - public function testWillNotExtractExpressionIfNoRange(): void - { - $extractMethod = new TolerantExtractExpression(); - - self::assertFalse($extractMethod->canExtractExpression( - SourceCode::fromString('canExtractExpression( - SourceCode::fromString(' [ - 'importClass1.test', - 'Barfoo\Foobar', - ]; - - yield 'with namespace' => [ - 'importClass2.test', - 'Barfoo\Foobar', - ]; - - yield 'with no namespace declaration or use statements' => [ - 'importClass3.test', - 'Barfoo\Foobar', - ]; - - yield 'with alias' => [ - 'importOnlyClass4.test', - 'Barfoo\Foobar', - 'Barfoo', - ]; - - yield 'with static alias' => [ - 'importOnlyClass5.test', - 'Barfoo\Foobar', - 'Barfoo', - ]; - - yield 'with multiple aliases' => [ - 'importOnlyClass6.test', - 'Barfoo\Foobar', - 'Barfoo', - ]; - - yield 'with alias and existing name' => [ - 'importOnlyClass7.test', - 'Barfoo\Foobar', - 'Barfoo', - ]; - - yield 'with class in root namespace' => [ - 'importClass8.test', - 'Foobar', - ]; - - yield 'from phpdoc' => [ - 'importClass9.test', - 'Barfoo\Foobar', - ]; - - yield 'from phpdoc (resolved in a SourceFileNode)' => [ - 'importClass10.test', - 'Barfoo\Foobar', - ]; - } - - public static function provideImportFunction(): Generator - { - yield 'import function' => [ - 'importFunction1.test', - 'Acme\foobar', - ]; - } - - protected function importName($source, int $offset, NameImport $nameImport, bool $importGlobals = true): TextEdits - { - $importClass = (new TolerantImportName($this->updater(), $this->parser(), $importGlobals)); - return $importClass->importNameOnly(SourceCode::fromString($source), ByteOffset::fromInt($offset), $nameImport); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantImportNameTest.php b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantImportNameTest.php deleted file mode 100644 index c2dbc4c0de..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantImportNameTest.php +++ /dev/null @@ -1,110 +0,0 @@ - [ - 'importClass1.test', - 'Barfoo\Foobar', - ]; - - yield 'with namespace' => [ - 'importClass2.test', - 'Barfoo\Foobar', - ]; - - yield 'with no namespace declaration or use statements' => [ - 'importClass3.test', - 'Barfoo\Foobar', - ]; - - yield 'with alias' => [ - 'importClass4.test', - 'Barfoo\Foobar', - 'Barfoo', - ]; - - yield 'with static alias' => [ - 'importClass5.test', - 'Barfoo\Foobar', - 'Barfoo', - ]; - - yield 'with multiple aliases' => [ - 'importClass6.test', - 'Barfoo\Foobar', - 'Barfoo', - ]; - - yield 'with alias and existing name' => [ - 'importClass7.test', - 'Barfoo\Foobar', - 'Barfoo', - ]; - - yield 'with class in root namespace' => [ - 'importClass8.test', - 'Foobar', - ]; - - yield 'from phpdoc' => [ - 'importClass9.test', - 'Barfoo\Foobar', - ]; - - yield 'from phpdoc (resolved in a SourceFileNode)' => [ - 'importClass10.test', - 'Barfoo\Foobar', - ]; - - yield 'with declare only' => [ - 'importClass_with_strict_types.test', - 'Barfoo\Foobar', - ]; - } - - public static function provideImportFunction(): Generator - { - yield 'import function' => [ - 'importFunction1.test', - 'Acme\foobar', - ]; - } - - public function testImportsGlobal(): void - { - $source = 'importName($source, 10, NameImport::forFunction('array_map', null), true); - self::assertStringContainsString('array_map', $edits->apply($source)); - } - - public function testNotImportGlobalWhenDisabled(): void - { - $source = 'importName($source, 10, NameImport::forFunction('array_map', null), false); - self::assertStringNotContainsString('array_map', $edits->apply($source)); - } - - public function testImportNotGlobalWhenDisabled(): void - { - $source = 'importName($source, 10, NameImport::forFunction('Bar\array_map', null), false); - self::assertStringContainsString('Bar\array_map', $edits->apply($source)); - } - - protected function importName(string $source, int $offset, NameImport $nameImport, bool $importGlobals = true): TextEdits - { - $importClass = (new TolerantImportName($this->updater(), $this->parser(), $importGlobals)); - return $importClass->importName(SourceCode::fromString($source), ByteOffset::fromInt($offset), $nameImport); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php deleted file mode 100644 index 089cf703ac..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php +++ /dev/null @@ -1,65 +0,0 @@ -sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - - $renameVariable = new TolerantRenameVariable($this->parser()); - $transformed = $renameVariable->renameVariable(SourceCode::fromString($source), $offset, $name, $scope); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - public static function provideRenameMethod(): Generator - { - yield 'one instance no context' => [ - 'renameVariable1.test', - 'newName' - ]; - yield 'two instances no context' => [ - 'renameVariable2.test', - 'newName' - ]; - yield 'local scope' => [ - 'renameVariable3.test', - 'newName', - RenameVariable::SCOPE_LOCAL - ]; - yield 'parameters from declaration' => [ - 'renameVariable4.test', - 'newName' - ]; - yield 'local parameter from body' => [ - 'renameVariable4.test', - 'newName', - RenameVariable::SCOPE_LOCAL - ]; - yield 'typed parameter' => [ - 'renameVariable5.test', - 'newName', - RenameVariable::SCOPE_LOCAL - ]; - yield 'anonymous function use' => [ - 'renameVariable6.test', - 'newName', - RenameVariable::SCOPE_LOCAL - ]; - yield 'anonymous function use within' => [ - 'renameVariable7.test', - 'newName', - RenameVariable::SCOPE_LOCAL - ]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/changeVisibility1.test b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/changeVisibility1.test deleted file mode 100644 index 2153a74670..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/changeVisibility1.test +++ /dev/null @@ -1,8 +0,0 @@ -// File: source -llo'; -// File: expected -blic function hello() {} -} -// File: expected -ted $hello; -} -// File: expected -te const hello = 'bar'; -} -// File: expected -lo; -} -// File: expected -<> -// File: expected -lo() - { - if ('hello' . $bar === null) { - } - } -} -// File: expected -llo' . $bar === null) { - } - } - - public function bye() - { - <> - } -} -// File: expected -es'; - $bar = 'hel<>lo' . $bar; - } -} -// File: expected -o ? - '1' : - '2'<>). - ' times'; - } -} -// File: expected - - public function hello() - { - if ('hello' . $bar === null) { - } - } -} -// File: expected -s Foobar -{ - public function hello() - { - if ('hello' . $bar === null) { - } - } -} -// File: expected -(){ - -} -// File: expected -$someclass->someMethod()<>; - } -} -// File: expected -someMethod(); - - } -} \ No newline at end of file diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression17A.test b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression17A.test deleted file mode 100644 index 2eceec0189..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression17A.test +++ /dev/null @@ -1,8 +0,0 @@ -// File: source -$obj->method();<> -// File: expected -method(); diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression2.test b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression2.test deleted file mode 100644 index 63ad5eadf5..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression2.test +++ /dev/null @@ -1,11 +0,0 @@ -// File: source -'hello'<> === null) { -} -// File: expected -new stdClass($a);<> -// File: expected -ew stdClass($a); -// File: expected -[ 'one' => 'two', 'three' => 'four' ]); -// File: expected - 'two', 'three' => 'four' ]; -Assert::assertEquals($result, $foobar); diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression6.test b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression6.test deleted file mode 100644 index 5d8159e1d8..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression6.test +++ /dev/null @@ -1,8 +0,0 @@ -// File: source -[ 'one' => 'two', 'three' => 'four' ]; -// File: expected - 'two', 'three' => 'four' ]; diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression6A.test b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression6A.test deleted file mode 100644 index ea5ca31644..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression6A.test +++ /dev/null @@ -1,9 +0,0 @@ -// File: source -$obj<>->method(); -// File: expected -method(); diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression7.test b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression7.test deleted file mode 100644 index 8ef18d7c76..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/extractExpression7.test +++ /dev/null @@ -1,11 +0,0 @@ -// File: source -ello' . $bar<> === null) { -} -// File: expected -ello' . $bar<> === null) { - } - } -} -// File: expected -ello' . $bar<> === null) { - } - } -} -// File: expected -ello' . $bar<> === null) { - } - } -} -// File: expected -new SplFileInfo('path/one'), - new SplFileInfo('path/two'), -]); -// File: expected -bar(); -// File: expected -Bar $foobar */ -$foobar = $container->get('fooboar'); -// File: expected -get('fooboar'); diff --git a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/importClass2.test b/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/importClass2.test deleted file mode 100644 index 0f2fef1135..0000000000 --- a/lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/fixtures/importClass2.test +++ /dev/null @@ -1,14 +0,0 @@ -// File: source -arbar; - -new Foobar(); -// File: expected -oobar(); -// File: expected -oobar(); -// File: expected -oobar::create(); -// File: expected -oobar::create(); -} -// File: expected -oobar::create(); -} -// File: expected -oobar::create(); -} -// File: expected -Bar $foobar - */ - private $foobar; -} -// File: expected -bar(); -// File: expected -ar(''); -// File: expected -ar(''); -// File: expected -oobar(); -// File: expected -oobar::create(); -// File: expected -oobar::create(); -} -// File: expected -oobar::create(); -} -// File: expected -foo; -// File: expected -o; -$foo; -// File: expected -oo = some_thing(); - - if ($foo == $bar) { - return $bar; - } - - return $foo; - } - - public function thisIsNothing() - { - $foo; - } -} -// File: expected -oo = some_thing(); - } -} -// File: expected -oo = some_thing(); - } -} -// File: expected -oo = some_thing(); - function () use ($foo) { - } - } -} -// File: expected -oo) { - } - } -} -// File: expected -reflectorForWorkspace($source); - $generator = new InterfaceFromExistingGenerator($reflector, $this->renderer()); - $source = $generator->generateFromExisting(ClassName::fromString($className), ClassName::fromString($targetName)); - $this->assertEquals($expected, (string) $source); - } - - /** - * @return Generator - */ - public static function provideGenerateInterface(): Generator - { - yield 'Generates interface' => [ - 'Music\Beat', - 'Music\BeatInterface', - <<<'EOT' - foobar = $foobar; - } - - /** - * This is some documentation. - */ - public function play(Snare $snare = null, int $bar = "boo") - { - $snare->hit(); - } - - public function empty() - { - } - - private function something() - { - } - - protected function somethingElse() - { - } - } - EOT - , <<<'EOT' - [ - 'Music\Beat', - 'Music\BeatInterface', - <<<'EOT' - [ - 'Music\Beat', - 'Music\BeatInterface', - <<<'EOT' - addSource('build(); - $default = (new EmptyValueRenderer())->render(TypeFactory::reflectedClass($reflector, 'Borders')); - self::assertEquals('Borders::ALL', $default); - } - public function testEnumNoCases(): void - { - $reflector = ReflectorBuilder::create()->addSource('build(); - $default = (new EmptyValueRenderer())->render(TypeFactory::reflectedClass($reflector, 'Borders')); - self::assertEquals('/** enum `Borders` has no cases */', $default); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Helper/WorseInterestingOffsetFinderTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Helper/WorseInterestingOffsetFinderTest.php deleted file mode 100644 index c12e4b15a2..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Helper/WorseInterestingOffsetFinderTest.php +++ /dev/null @@ -1,142 +0,0 @@ -reflectorForWorkspace($source); - $document = TextDocumentBuilder::create($source)->build(); - $offset = ByteOffset::fromInt($offset); - - $newOffset = (new WorseInterestingOffsetFinder($reflector))->find($document, $offset); - $reflectionOffset = $reflector->reflectOffset($document, $newOffset); - - $this->assertEquals($expectedSymbolType, $reflectionOffset->nodeContext()->symbol()->symbolType()); - } - - public static function provideFindSomethingInterestingWhen() - { - yield 'offset in empty file' => [ - <<<'EOT' - - EOT - , Symbol::UNKNOWN, - ]; - - yield 'offset over target class' => [ - <<<'EOT' - oobar - { - } - EOT - , Symbol::CLASS_, - ]; - - yield 'offset in whitespace in target class' => [ - <<<'EOT' - - } - EOT - , Symbol::CLASS_, - ]; - - yield 'offset on method' => [ - <<<'EOT' - methodOne() - { - } - } - EOT - , Symbol::METHOD, - ]; - - yield 'offset in method' => [ - <<<'EOT' - - } - } - EOT - , Symbol::METHOD, - ]; - - yield 'offset in method call' => [ - <<<'EOT' - ba<>r(); - } - - private function bar() - { - } - } - EOT - , Symbol::METHOD, - ]; - - - yield 'offset on var' => [ - <<<'EOT' - o; - } - } - EOT - , Symbol::VARIABLE, - ]; - - yield 'offset on expression' => [ - <<<'EOT' - foo; - } - } - EOT - , Symbol::VARIABLE, - ]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Helper/WorseMissingMemberFinderTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Helper/WorseMissingMemberFinderTest.php deleted file mode 100644 index a7d042e40d..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Helper/WorseMissingMemberFinderTest.php +++ /dev/null @@ -1,136 +0,0 @@ -reflectorForWorkspace($source); - $document = TextDocumentBuilder::create($source)->uri('file:///test')->build(); - - $methods = wait((new WorseMissingMemberFinder($reflector))->find($document)); - self::assertCount($expectedCount, $methods); - } - - /** - * @return Generator - */ - public static function provideFindMissingMethods(): Generator - { - yield 'no methods' => [ - <<<'EOT' - [ - <<<'EOT' - foo(); } } - EOT - , 0 - ]; - yield '1 missing method' => [ - <<<'EOT' - foo(); } } - EOT - , 1 - ]; - yield 'missing static method' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - bof(); - EOT - , 1 - ]; - yield 'call foreign class present' => [ - <<<'EOT' - zed(); - EOT - , 0 - ]; - yield 'methods from trait' => [ - <<<'EOT' - boo(); - EOT - , 0 - ]; - yield 'methods from trait with virtual method' => [ - <<<'EOT' - boo(); - EOT - , 0 - ]; - yield 'methods from generic' => [ - <<<'EOT' - - */ - function foo(){} - $new = foo(); - $new->boo(); - EOT - , 1 - ]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php deleted file mode 100644 index 0648f3a17e..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php +++ /dev/null @@ -1,46 +0,0 @@ -sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - - $replaceQualifierWithImport = new WorseReplaceQualifierWithImport( - $this->reflectorForWorkspace($source), - new WorseBuilderFactory($this->reflectorForWorkspace($source)), - $this->updater() - ); - - $textDocumentEdits = $replaceQualifierWithImport->getTextEdits( - SourceCode::fromStringAndPath($source, 'file:///source'), - $offset - ); - $sourceCode = SourceCode::fromStringAndPath($source, 'file:///source'); - $transformed = SourceCode::fromStringAndPath( - (string) $textDocumentEdits->textEdits()->apply($sourceCode), - $textDocumentEdits->uri()->path() - ); - - self::assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator> - */ - public static function dataFQNToImport(): Generator - { - yield 'in an expression' => [ 'replaceQualifierWithImport1.test' ]; - yield 'in a parameter' => [ 'replaceQualifierWithImport2.test' ]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php deleted file mode 100644 index 2232efb658..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php +++ /dev/null @@ -1,79 +0,0 @@ -sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - - $extractConstant = new WorseExtractConstant($this->reflectorForWorkspace($source), $this->updater()); - $textDocumentEdits = $extractConstant->extractConstant(SourceCode::fromStringAndPath($source, 'file:///source'), $offset, $name); - $sourceCode = SourceCode::fromStringAndPath($source, 'file:///source'); - $transformed = SourceCode::fromStringAndPath( - (string) $textDocumentEdits->textEdits()->apply($sourceCode), - $textDocumentEdits->uri()->path() - ); - - $this->assertEquals(trim($expected), trim($transformed)); - } - /** - * @return Generator> - */ - public static function provideExtractMethod(): Generator - { - yield 'string' => [ 'extractConstant1.test', 'HELLO_WORLD' ]; - yield 'numeric' => [ 'extractConstant2.test', 'HELLO_WORLD' ]; - yield 'array_key' => [ 'extractConstant3.test', 'HELLO_WORLD' ]; - yield 'namespaced' => [ 'extractConstant4.test', 'HELLO_WORLD' ]; - yield 'replace all' => [ 'extractConstant5.test', 'HELLO_WORLD' ]; - yield 'replace all numeric' => [ 'extractConstant6.test', 'HOUR' ]; - yield 'replace heredoc' => [ 'extractConstant7.test', 'HELLO_WORLD' ]; - } - - public function testNoClass(): void - { - $this->expectException(TransformException::class); - $this->expectExceptionMessage('Node does not belong to a class'); - - $code = <<<'EOT' - reflectorForWorkspace($code), $this->updater()); - $extractConstant->extractConstant(SourceCode::fromString($code), 8, 'asd'); - } - - public function testNoOverwritingOfExistingConstants(): void - { - $this->expectException(TransformException::class); - $this->expectExceptionMessage('Constant with name TEXT already exists on class Test'); - - $code = <<<'EOT' - ext'; - } - } - EOT; - - [$source, $offset] = ExtractOffset::fromSource($code); - $extractConstant = new WorseExtractConstant($this->reflectorForWorkspace($source), $this->updater()); - $extractConstant->extractConstant(SourceCode::fromString($source), $offset, 'TEXT'); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php deleted file mode 100644 index 4dea6c623e..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php +++ /dev/null @@ -1,100 +0,0 @@ -sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - - $worseSourceCode = TextDocumentBuilder::fromPathAndString('file:///source', $source); - $reflector = $this->reflectorForWorkspace($worseSourceCode); - - $factory = new WorseBuilderFactory($reflector); - $extractMethod = new WorseExtractMethod($reflector, $factory, $this->updater()); - - $sourceCode = SourceCode::fromStringAndPath($source, 'file:///source'); - $textDocumentEdits = $extractMethod->extractMethod($sourceCode, $offsetStart, $offsetEnd, 'newMethod'); - - $transformed = SourceCode::fromStringAndPath( - (string) $textDocumentEdits->textEdits()->apply($sourceCode), - $textDocumentEdits->uri()->path() - ); - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** @return Generator> */ - public static function provideExtractMethod(): Generator - { - yield 'no free variables' => ['extractMethod1.test']; - yield 'free variable' => ['extractMethod2.test']; - yield 'free variables' => ['extractMethod3.test']; - yield 'namespaced' => ['extractMethod4.test']; - yield 'duplicated vars' => ['extractMethod5.test']; - yield 'return value and assignment' => ['extractMethod6.test']; - yield 'multiple return value and assignment' => ['extractMethod7.test']; - yield 'multiple return value with incoming variables' => ['extractMethod8.test']; - yield 'multiple return value boundaries' => ['extractMethod10.test']; - yield 'tail variables are taken from scope' => ['extractMethod11.test']; - yield 'replacement indentation is preserved' => ['extractMethod12.test']; - yield 'only considers selection content for return vars' => ['extractMethod13.test']; - yield 'return mutated primative' => ['extractMethod14.test']; - yield 'imports classes' => ['extractMethod15.test']; - yield 'adds return type for scalar' => ['extractMethod16.test']; - yield 'adds return type for nullable scalar' => ['extractMethod16A.test']; - yield 'adds return type and import for nullable class' => ['extractMethod16B.test']; - yield 'adds return type for class' => ['extractMethod17.test']; - yield 'extracts expression to method' => ['extractMethod18.test']; - yield 'extracts assignment expression to method' => ['extractMethod19.test']; - yield 'extracts assignment expression with unknown return type' => ['extractMethod20.test']; - yield 'extract expression and adds short return type for class' => ['extractMethod21.test']; - yield 'return if extracted code has a return' => ['extractMethod22.test']; - yield 'adds method to declaring class' => ['extractMethod23.test']; - yield 'empty text selection' => ['extractMethod27.test']; - yield 'nullable argument' => ['extractMethod29.test']; - yield 'ignore scoped variables: catch clause' => ['extractMethod30.test']; - yield 'ignore scoped variables: anonymous function' => ['extractMethod31.test']; - yield 'union argument' => ['extractMethod32.test']; - yield 'extract method from trait' => ['extractMethod33.test']; - yield 'extract static method' => ['extractMethod34.test']; - } - - #[DataProvider('provideExtractMethodFromDifferentScopes')] - public function testExtractingMethodsFromDifferentScopes(string $test): void - { - $this->expectException(Exception::class); - $this->expectExceptionMessage('Cannot extract method. Check if start and end statements are in different scopes.'); - - $this->testExtractMethod($test); - } - - /** @return Generator> */ - public static function provideExtractMethodFromDifferentScopes(): Generator - { - yield['extractMethod24.test']; - yield['extractMethod25.test']; - yield['extractMethod26.test']; - yield 'empty selection 2' => [ - 'extractMethod28.test', - ]; - } - - public function testExtractMethodThatExists(): void - { - $this->expectException(Exception::class); - $this->expectExceptionMessage('Class "extractMethod" already has method "newMethod"'); - - $this->testExtractMethod('extractMethod_methodExists.test'); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php deleted file mode 100644 index 546453a7b7..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php +++ /dev/null @@ -1,53 +0,0 @@ -sourceExpectedAndOffset($path); - - $fill = $this->createRefactor($source); - $transformed = $fill->refactor( - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt($offset) - )->apply($source); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator - */ - public static function provideFill(): Generator - { - foreach ((new GlobIterator(__DIR__ . '/fixtures/fillMatchArms*.test')) as $fileInfo) { - assert($fileInfo instanceof SplFileInfo); - yield $fileInfo->getBasename() => [ - $fileInfo->getPathname() - ]; - } - } - - private function createRefactor(string $source): WorseFillMatchArms - { - $fill = new WorseFillMatchArms( - $this->reflectorForWorkspace($source), - new TolerantAstProvider(), - ); - return $fill; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php deleted file mode 100644 index e61ef1c57b..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php +++ /dev/null @@ -1,82 +0,0 @@ -sourceExpectedAndOffset($path); - - $fill = $this->createFillObject($source, true, false); - $transformed = $fill->refactor( - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt($offset) - )->apply($source); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - #[DataProvider('provideFill')] - public function testFillNotNamed( - string $path - ): void { - [$source, $expected, $offset] = $this->sourceExpectedAndOffset($path); - $expected = $this->workspace()->getContents('nonamed'); - - $fill = $this->createFillObject($source, false, true); - $transformed = $fill->refactor( - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt($offset), - )->apply($source); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - public function testOffsetNotObject(): void - { - $fill = $this->createFillObject(''); - $edits = $fill->refactor( - TextDocumentBuilder::create('build(), - ByteOffset::fromInt(10) - ); - self::assertCount(0, $edits); - } - - /** - * @return Generator - */ - public static function provideFill(): Generator - { - foreach ((new GlobIterator(__DIR__ . '/fixtures/fillObject*.test')) as $fileInfo) { - assert($fileInfo instanceof SplFileInfo); - yield $fileInfo->getBasename() => [ - $fileInfo->getPathname() - ]; - } - } - - private function createFillObject(string $source, bool $named = true, bool $hint = false): WorseFillObject - { - $fill = new WorseFillObject( - $this->reflectorForWorkspace($source), - new TolerantAstProvider(), - $this->updater(), - $named, - $hint - ); - return $fill; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php deleted file mode 100644 index 6297d50285..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php +++ /dev/null @@ -1,92 +0,0 @@ -sourceExpectedAndOffset( - __DIR__ . '/fixtures/' . $test - ); - - $generateAccessor = new WorseGenerateAccessor( - $this->reflectorForWorkspace($source), - $this->updater(), - $prefix, - $upperCaseFirst - ); - $transformed = $generateAccessor->generate( - SourceCode::fromString($source), - [$propertyName], - $offset - )->apply($source); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - public static function provideExtractAccessor(): Generator - { - $propertyName = 'method'; - - yield'property' => [ - 'generateAccessor1.test', - $propertyName, - ]; - yield'prefix and ucfirst' => [ - 'generateAccessor2.test', - $propertyName, - 'get', - true, - ]; - yield 'return type' => [ - 'generateAccessor3.test', - $propertyName, - ]; - yield 'namespaced' => [ - 'generateAccessor4.test', - $propertyName, - ]; - yield 'pseudo-type' => [ - 'generateAccessor5.test', - $propertyName, - ]; - yield 'multiple-classes' => [ - 'generateAccessor6.test', - $propertyName, - ]; - yield 'prefix but ucfirst by default' => [ - 'generateAccessor7.test', - $propertyName, - 'get', - ]; - yield 'prefix but ucfirst to false' => [ - 'generateAccessor8.test', - $propertyName, - 'get', - false, - ]; - } - - public function testNonProperty(): void - { - $this->expectException(ItemNotFound::class); - $this->expectExceptionMessage('Unknown item "bar", known items: "foo"'); - $source = 'reflectorForWorkspace(''), $this->updater()); - $generateAccessor->generate(SourceCode::fromString($source), ['bar'], 0); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php deleted file mode 100644 index 71f53317b6..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php +++ /dev/null @@ -1,70 +0,0 @@ -sourceExpectedAndOffset($path); - - $create = $this->generator($source, true, false); - $textDocumentEditsCollection = $create->generateMethod( - TextDocumentBuilder::create($source)->uri('file:///foo')->build(), - ByteOffset::fromInt($offset) - ); - - $transformed = $source; - foreach ($textDocumentEditsCollection as $textDocumentEdits) { - $transformed = $textDocumentEdits->textEdits()->apply($transformed); - } - - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator - */ - public static function provideCreate(): Generator - { - foreach ((new GlobIterator(__DIR__ . '/fixtures/generateConstructor*.test')) as $fileInfo) { - assert($fileInfo instanceof SplFileInfo); - yield $fileInfo->getBasename() => [ - $fileInfo->getPathname() - ]; - } - } - - public function testOffsetNotObject(): void - { - $create = $this->generator(''); - $edits = $create->generateMethod( - TextDocumentBuilder::create('uri('file:///foo')->build(), - ByteOffset::fromInt(10) - ); - self::assertCount(0, $edits); - } - - private function generator(string $source, bool $named = true, bool $hint = false): WorseGenerateConstructor - { - $reflector = $this->reflectorForWorkspace($source); - return new WorseGenerateConstructor( - $reflector, - $this->builderFactory($reflector), - $this->updater(), - new TolerantAstProvider() - ); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateDecoratorTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateDecoratorTest.php deleted file mode 100644 index efeca22fa5..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateDecoratorTest.php +++ /dev/null @@ -1,42 +0,0 @@ -sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - $sourceCode = SourceCode::fromStringAndPath($source, 'file:///source'); - - $generateDecorator = new WorseGenerateDecorator($this->reflectorForWorkspace($source), $this->updater()); - $textDocumentEdits = $generateDecorator->getTextEdits($sourceCode, 'Phpactor\\SomethingToDecorate'); - - $transformed = SourceCode::fromStringAndPath( - (string) $textDocumentEdits->apply($sourceCode), - 'file:///source' - ); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator - */ - public static function provideGenerateDecorator(): Generator - { - yield 'decorating untyped method' => [ 'generateDecorator1.test']; - yield 'decorating method with parameters' => [ 'generateDecorator2.test']; - yield 'decorating method with return type' => [ 'generateDecorator3.test']; - yield 'decorating method with default values' => [ 'generateDecorator4.test']; - yield 'decorating method with void' => [ 'generateDecorator5.test']; - yield 'decorating multiple methods' => [ 'generateDecorator6.test']; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMemberTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMemberTest.php deleted file mode 100644 index 7ea95ad299..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMemberTest.php +++ /dev/null @@ -1,106 +0,0 @@ -sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - - $transformed = $this->generateMember($source, $offset, $name); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator> - */ - public static function provideGenerateMember(): Generator - { - yield 'string' => [ 'generateMember1.test' ]; - yield 'parameter' => [ 'generateMember2.test' ]; - yield 'named parameters' => ['generateMember_namedParams.test']; - yield 'typed parameter' => [ 'generateMember3.test' ]; - yield 'undeclared parameter' => [ 'generateMember4.test' ]; - yield 'expression' => [ 'generateMember5.test' ]; - yield 'public accessor in another class' => [ 'generateMember6.test' ]; - yield 'public accessor on interface' => [ 'generateMember7.test' ]; - yield 'public accessor on interface with namespace' => [ 'generateMember8.test' ]; - yield 'imports classes' => [ 'generateMember9.test' ]; - yield 'static private method' => [ 'generateMember10.test' ]; - yield 'static public method' => [ 'generateMember11.test' ]; - yield 'add return type' => [ 'generateMember12.test' ]; - yield 'add return type with docblock' => [ 'generateMember13.test' ]; - yield 'add parameter type multiple literals' => [ 'generateMember14.test' ]; - yield 'nullable parameter inference' => [ 'generateMember15.test' ]; - yield 'generic parameter inference' => [ 'generateMember16.test' ]; - yield 'union false' => [ 'generateMember17.test' ]; - yield 'duplicated type guesses' => [ 'generateMember_duplicateNameGuesses.test' ]; - yield 'docblock for complex type' => [ 'generateMember_complexTypeDocblock.test' ]; - yield 'enum' => [ 'generateMember_enumParams.test' ]; - yield 'backed_enum' => [ 'generateMember_backedEnumParams.test' ]; - yield 'public method on enum' => [ 'generateMember_enum.test', 'play']; - yield 'case on enum' => [ 'generateMember_enumCase.test', 'Foo']; - yield 'private constant on class' => [ 'generateMember_constant.test', 'FOO']; - yield 'public constant on class' => [ 'generateMember_constantPublic.test', 'FOO']; - } - - public function testGenerateOnTraitException(): void - { - $this->expectException(TransformException::class); - $this->expectExceptionMessage('Can only generate methods on classes'); - $source = <<<'EOT' - hello->asd(); - } - } - EOT - ; - - $this->generateMember($source, 152, 'test_name'); - } - - private function generateMember(string $source, int $start, ?string $name): string - { - $worseSourceCode = TextDocumentBuilder::fromPathAndString('file:///source', $source); - $reflector = $this->reflectorForWorkspace($worseSourceCode); - - $generateMember = new WorseGenerateMember( - $reflector, - new WorseBuilderFactory($reflector), - $this->updater() - ); - $sourceCode = SourceCode::fromStringAndPath($source, 'file:///source'); - $textDocumentEdits = $generateMember->generateMember($sourceCode, $start, $name); - - $transformed = SourceCode::fromStringAndPath( - (string) $textDocumentEdits->textEdits()->apply($sourceCode), - $textDocumentEdits->uri()->path() - ); - return $transformed; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php deleted file mode 100644 index 76a224acdc..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php +++ /dev/null @@ -1,104 +0,0 @@ -sourceExpectedAndOffset( - __DIR__ . '/fixtures/' . $test - ); - - $generateMutator = new WorseGenerateMutator( - $this->reflectorForWorkspace($source), - $this->updater(), - $prefix, - $upperCaseFirst, - $fluent, - ); - $transformed = $generateMutator->generate( - SourceCode::fromString($source), - [$propertyName], - $offset - )->apply($source); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator> - */ - public static function provideExtractMutator(): Generator - { - $propertyName = 'method'; - - yield 'property' => [ - 'generateMutator1.test', - $propertyName, - ]; - yield 'prefix and ucfirst' => [ - 'generateMutator2.test', - $propertyName, - 'set', - true, - ]; - yield 'return type' => [ - 'generateMutator3.test', - $propertyName, - ]; - yield 'namespaced' => [ - 'generateMutator4.test', - $propertyName, - ]; - yield 'pseudo-type' => [ - 'generateMutator5.test', - $propertyName, - ]; - yield 'multiple-classes' => [ - 'generateMutator6.test', - $propertyName, - ]; - yield 'prefix but ucfirst by default' => [ - 'generateMutator7.test', - $propertyName, - 'set', - ]; - yield 'prefix but ucfirst to false' => [ - 'generateMutator8.test', - $propertyName, - 'set', - false, - ]; - yield 'fluent' => [ - 'generateMutator9.test', - $propertyName, - '', - false, - true, - ]; - } - - public function testNonProperty(): void - { - $this->expectException(ItemNotFound::class); - $this->expectExceptionMessage('Unknown item "bar", known items: "foo"'); - $source = 'reflectorForWorkspace(''), $this->updater()); - $generateMutator->generate(SourceCode::fromString($source), ['bar'], 0); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php deleted file mode 100644 index 3522b13e00..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php +++ /dev/null @@ -1,70 +0,0 @@ -sourceExpected(__DIR__ . '/fixtures/' . $test); - - $transformed = $this->overrideMethod($source, $className, $methodName); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator - */ - public static function provideExtractMethod(): Generator - { - yield 'root type as param and return type' => [ - 'overrideMethod1.test', - 'ChildClass', - 'barbar' - ]; - yield 'no params or return type' => [ - 'overrideMethod2.test', - 'ChildClass', - 'barbar' - ]; - yield 'scalar type as param and return type' => [ - 'overrideMethod3.test', - 'ChildClass', - 'barbar' - ]; - yield 'default value' => [ - 'overrideMethod4.test', - 'ChildClass', - 'barbar' - ]; - yield 'parent class with > 1 method' => [ - 'overrideMethod5.test', - 'ChildClass', - 'barbar' - ]; - } - - public function testClassNoParent(): void - { - $this->expectException(TransformException::class); - $this->overrideMethod('reflectorForWorkspace($source); - $factory = new WorseBuilderFactory($reflector); - $override = new WorseOverrideMethod($reflector, $factory, $this->updater(), '8.5'); - return $override->overrideMethod(SourceCode::fromString($source), $className, $methodName)->apply($source); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractConstant1.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractConstant1.test deleted file mode 100644 index 9c6c7fca64..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractConstant1.test +++ /dev/null @@ -1,22 +0,0 @@ -// File: source -hello_world'; - } -} -// File: expected -4; - } -} -// File: expected -34']; - } -} -// File: expected -lo'; - } -} -// File: expected -ello'; - 'hello'; - } - - public function smallMethod() - { - if ($foo == 'hello' and $bar == 'goodbye') { - } - } -} - -'hello'; -// File: expected -00; - } - - public function smallMethod() - { - 36001; - 3600; - } -} -// File: expected -I am a HEREDOC -EOT; - } -} -// File: expected -$foobar = 'hello';<> - } -} -// File: expected -newMethod(); - } - - private function newMethod() - { - $foobar = 'hello'; - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod10.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod10.test deleted file mode 100644 index 1f2883d72f..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod10.test +++ /dev/null @@ -1,48 +0,0 @@ -// File: source -$new = true; - if ($foobar !== 'hello') { - $new = false; - $bar = 'hahaha'; - }<> - - $new = $bar; - - return [ $new, $bar ]; - } -} -// File: expected -newMethod($foobar); - - $new = $bar; - - return [ $new, $bar ]; - } - - private function newMethod(string $foobar): array - { - $new = true; - if ($foobar !== 'hello') { - $new = false; - $bar = 'hahaha'; - } - return [$new, $bar]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod11.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod11.test deleted file mode 100644 index 70d1d282e3..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod11.test +++ /dev/null @@ -1,39 +0,0 @@ -// File: source -$new = true;<> - } - - public function no() - { - $new = 1234; - } -} -// File: expected -newMethod(); - } - - public function no() - { - $new = 1234; - } - - private function newMethod() - { - $new = true; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod12.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod12.test deleted file mode 100644 index f9950e5b08..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod12.test +++ /dev/null @@ -1,28 +0,0 @@ -// File: source - $new = true; - echo 'foooo'; -<> - } -} -// File: expected -newMethod(); - } - - private function newMethod() - { - $new = true; - echo 'foooo'; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod13.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod13.test deleted file mode 100644 index 312d03adc6..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod13.test +++ /dev/null @@ -1,33 +0,0 @@ -// File: source -echo 'hello';<> - - return $bar; - } -} -// File: expected -newMethod(); - - return $bar; - } - - private function newMethod() - { - echo 'hello'; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod14.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod14.test deleted file mode 100644 index 7631773982..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod14.test +++ /dev/null @@ -1,35 +0,0 @@ -// File: source - $bar = 'goodbye'; - <> - - return $bar; - } -} -// File: expected -newMethod($bar); - - return $bar; - } - - private function newMethod(string $bar): string - { - $bar = 'goodbye'; - return $bar; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod15.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod15.test deleted file mode 100644 index 4fd015d1a8..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod15.test +++ /dev/null @@ -1,53 +0,0 @@ -// File: Carcar.php -carcar(); - - <>$car;<> - - return $bar; - } -} -// File: expected -carcar(); - - $this->newMethod($car); - - return $bar; - } - - private function newMethod(Carcar $car) - { - $car; - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16.test deleted file mode 100644 index 14a820ce12..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source -$car = 'hello';<> - - return $car; - } -} -// File: expected -newMethod(); - - return $car; - } - - private function newMethod(): string - { - $car = 'hello'; - return $car; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16A.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16A.test deleted file mode 100644 index cbc53681fc..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16A.test +++ /dev/null @@ -1,36 +0,0 @@ -// File: source -$car = $this->something();<> - - return $car; - } -} -// File: expected -newMethod(); - - return $car; - } - - private function newMethod(): ?string - { - $car = $this->something(); - return $car; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16B.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16B.test deleted file mode 100644 index 06c40099d6..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod16B.test +++ /dev/null @@ -1,47 +0,0 @@ -// File: Foo.php -$car = $this->foo()->bar();<> - - return $car; - } -} -// File: expected -newMethod(); - - return $car; - } - - private function newMethod(): ?Bar - { - $car = $this->foo()->bar(); - return $car; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod17.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod17.test deleted file mode 100644 index 5febd2ebe5..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod17.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source -$car = new stdClass();<> - - return $car; - } -} -// File: expected -newMethod(); - - return $car; - } - - private function newMethod(): stdClass - { - $car = new stdClass(); - return $car; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod18.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod18.test deleted file mode 100644 index a6fb4ed6ba..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod18.test +++ /dev/null @@ -1,21 +0,0 @@ -// File: source -new Barfoo()<>); - } -} -// File: expected -newMethod()); - } - - private function newMethod(): Barfoo - { - return new Barfoo(); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod19.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod19.test deleted file mode 100644 index 0f32cd0c8d..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod19.test +++ /dev/null @@ -1,21 +0,0 @@ -// File: source -$foobar = new Barfoo()<>); - } -} -// File: expected -newMethod()); - } - - private function newMethod(): Barfoo - { - return $foobar = new Barfoo(); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod2.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod2.test deleted file mode 100644 index c7b030e67c..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod2.test +++ /dev/null @@ -1,41 +0,0 @@ -// File: source - - if ($foobar) { - return 'yes'; - } - - return 'no'; - <> - - } -} -// File: expected -newMethod($foobar); - - } - - private function newMethod(string $foobar) - { - if ($foobar) { - return 'yes'; - } - - return 'no'; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod20.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod20.test deleted file mode 100644 index 8031857c29..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod20.test +++ /dev/null @@ -1,21 +0,0 @@ -// File: source -$foo = $bar ? $hello : $bar<>); - } -} -// File: expected -newMethod()); - } - - private function newMethod() - { - return $foo = $bar ? $hello : $bar; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod21.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod21.test deleted file mode 100644 index 79bb7981d8..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod21.test +++ /dev/null @@ -1,23 +0,0 @@ -// File: source -$foobar = new \Foobar\Barfoo()<>); - } -} -// File: expected -newMethod()); - } - - private function newMethod(): Barfoo - { - return $foobar = new \Foobar\Barfoo(); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod22.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod22.test deleted file mode 100644 index 3d037a2a47..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod22.test +++ /dev/null @@ -1,21 +0,0 @@ -// File: source -return new Foobar($foobar = new \Foobar\Barfoo());<> - } -} -// File: expected -newMethod(); - } - - private function newMethod() - { - return new Foobar($foobar = new \Foobar\Barfoo()); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod23.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod23.test deleted file mode 100644 index 3d037a2a47..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod23.test +++ /dev/null @@ -1,21 +0,0 @@ -// File: source -return new Foobar($foobar = new \Foobar\Barfoo());<> - } -} -// File: expected -newMethod(); - } - - private function newMethod() - { - return new Foobar($foobar = new \Foobar\Barfoo()); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod24.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod24.test deleted file mode 100644 index 7dd12b10e7..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod24.test +++ /dev/null @@ -1,12 +0,0 @@ -// File: source -$expression1 = 1; - }; - $expression2 = 2;<> - } -} -// File: expected diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod25.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod25.test deleted file mode 100644 index 44871b8e79..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod25.test +++ /dev/null @@ -1,12 +0,0 @@ -// File: source -$b = 4; - } - $c = 5;<> - } -} -// File: expected diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod26.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod26.test deleted file mode 100644 index ee681c7478..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod26.test +++ /dev/null @@ -1,18 +0,0 @@ -// File: source - - $b = 22; - } - $c = 33; - <> - $d = 44; - } - } -} -// File: expected diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod27.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod27.test deleted file mode 100644 index de048b6313..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod27.test +++ /dev/null @@ -1,32 +0,0 @@ -// File: source - - <> - } - } -} -// File: expected -newMethod(); - } - } - - private function newMethod() - { - - } -} \ No newline at end of file diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod28.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod28.test deleted file mode 100644 index 3c9efffc82..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod28.test +++ /dev/null @@ -1,14 +0,0 @@ -// File: source -<> - } - } -} -// File: expected diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod29.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod29.test deleted file mode 100644 index 5d6e82f55e..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod29.test +++ /dev/null @@ -1,24 +0,0 @@ -// File: source -$car = $foobar->getCar();<> - return $car; - } -} -// File: expected -newMethod($foobar); - return $car; - } - - private function newMethod(?Foobar $foobar) - { - $car = $foobar->getCar(); - return $car; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod3.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod3.test deleted file mode 100644 index db84bf56b8..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod3.test +++ /dev/null @@ -1,43 +0,0 @@ -// File: source - - if ($foobar) { - return $barfoo; - } - - return 'no'; - <> - } -} -// File: expected -newMethod($foobar, $barfoo); - } - - private function newMethod(Foobar $foobar, string $barfoo) - { - if ($foobar) { - return $barfoo; - } - - return 'no'; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod30.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod30.test deleted file mode 100644 index 7c829529f4..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod30.test +++ /dev/null @@ -1,52 +0,0 @@ -// File: source - - try { - $someOne = true; - } catch (Exception $e) { - throw new Exception("inner", 0, $e); - $a = 5; - } - <> - - try { - $someTwo = true; - } catch (Exception $e) { - throw new Exception("inner", 0, $e); - $a = 6; - } - - return $a; - } -} -// File: expected -newMethod(); - - try { - $someTwo = true; - } catch (Exception $e) { - throw new Exception("inner", 0, $e); - $a = 6; - } - - return $a; - } - - private function newMethod(): int - { - try { - $someOne = true; - } catch (Exception $e) { - throw new Exception("inner", 0, $e); - $a = 5; - } - return $a; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod31.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod31.test deleted file mode 100644 index 464d61ba3b..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod31.test +++ /dev/null @@ -1,33 +0,0 @@ -// File: source - - $f = function() { - $e = 3; - return $e; - } - <> - $e = 2; - return $e; - } -} -// File: expected -newMethod(); - $e = 2; - return $e; - } - - private function newMethod() - { - $f = function() { - $e = 3; - return $e; - } - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod32.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod32.test deleted file mode 100644 index f9dddb55ca..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod32.test +++ /dev/null @@ -1,36 +0,0 @@ -// File: union.php -$car = $union;<> - return $car; - } -} -// File: expected -newMethod($union); - return $car; - } - - private function newMethod(string|Foo|Bar $union): string|Foo|Bar - { - $car = $union; - return $car; - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod33.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod33.test deleted file mode 100644 index f34a3db916..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod33.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source -$car = $union;<> - return $car; - } -} -// File: expected -newMethod($union); - return $car; - } - - private function newMethod(?string $union): ?string - { - $car = $union; - return $car; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod34.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod34.test deleted file mode 100644 index 3db778e7a6..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod34.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source -$car = $union;<> - return $car; - } -} -// File: expected -newMethod($union); - return $car; - } - - private static function newMethod(?string $union): ?string - { - $car = $union; - return $car; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod4.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod4.test deleted file mode 100644 index 6687a378c7..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod4.test +++ /dev/null @@ -1,37 +0,0 @@ -// File: source -$this->foobar();<> - } - - private function foobar() - { - } -} -// File: expected -newMethod(); - } - - private function foobar() - { - } - - private function newMethod() - { - $this->foobar(); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod5.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod5.test deleted file mode 100644 index fbac84e845..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod5.test +++ /dev/null @@ -1,41 +0,0 @@ -// File: source - - if ($foobar) { - return 'yes'; - } - - return $foobar; - <> - - } -} -// File: expected -newMethod($foobar); - - } - - private function newMethod(string $foobar) - { - if ($foobar) { - return 'yes'; - } - - return $foobar; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod6.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod6.test deleted file mode 100644 index 024d78b514..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod6.test +++ /dev/null @@ -1,38 +0,0 @@ -// File: source - - $new = false; - if (true) { - $new = true; - } - <> - - return $new; - } -} -// File: expected -newMethod(); - - return $new; - } - - private function newMethod(): bool - { - $new = false; - if (true) { - $new = true; - } - return $new; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod7.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod7.test deleted file mode 100644 index 6f57cff7a9..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod7.test +++ /dev/null @@ -1,48 +0,0 @@ -// File: source - - $new = false; - if (true) { - $new = true; - } - $foo = 'hello'; - <> - - if ($foo) { - return $new; - } - - return $new; - } -} -// File: expected -newMethod(); - - if ($foo) { - return $new; - } - - return $new; - } - - private function newMethod(): array - { - $new = false; - if (true) { - $new = true; - } - $foo = 'hello'; - return [$foo, $new]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod8.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod8.test deleted file mode 100644 index 34e8a11ca0..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod8.test +++ /dev/null @@ -1,54 +0,0 @@ -// File: source - - $new = false; - if ($hello) { - $new = true; - } - $foo = 'hello'; - <> - - if ($foo) { - return $new; - } - - echo $hello; - $this->foobar; - - return $new; - } -} -// File: expected -newMethod($hello); - - if ($foo) { - return $new; - } - - echo $hello; - $this->foobar; - - return $new; - } - - private function newMethod(string $hello): array - { - $new = false; - if ($hello) { - $new = true; - } - $foo = 'hello'; - return [$foo, $new]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod_methodExists.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod_methodExists.test deleted file mode 100644 index d99985435c..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractMethod_methodExists.test +++ /dev/null @@ -1,17 +0,0 @@ -// File: source - - $foo = 'hello'; - <> - } - - public function newMethod() - { - } -} -// File: expected diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_existingCases.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_existingCases.test deleted file mode 100644 index 7d109158f5..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_existingCases.test +++ /dev/null @@ -1,39 +0,0 @@ -// File: source -{ - Page::Blog => null, - }; -} -// File: expected - null, - Page::AboutUs => null, - Page::Websites => null, - - }; -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_fill.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_fill.test deleted file mode 100644 index 22f15a4073..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_fill.test +++ /dev/null @@ -1,36 +0,0 @@ -// File: source -{}; -} -// File: expected - null, - Page::Blog => null, - Page::Websites => null, - }; -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_noStartBrace.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_noStartBrace.test deleted file mode 100644 index 22c84ffbd7..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_noStartBrace.test +++ /dev/null @@ -1,37 +0,0 @@ -// File: source -}; -} -// File: expected - null, - Page::Blog => null, - Page::Websites => null, - }; -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_notEnum.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_notEnum.test deleted file mode 100644 index c114f652ec..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillMatchArms_notEnum.test +++ /dev/null @@ -1,16 +0,0 @@ -// File: source -}; -} -// File: expected -{}; -} -// File: expected - null, - Page::Blog => null, - Page::Websites => null, - }; -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillObject_attributes.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillObject_attributes.test deleted file mode 100644 index b057b338e6..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/fillObject_attributes.test +++ /dev/null @@ -1,47 +0,0 @@ -// File: source -ute()] -class Foo { -} - -// File: expected -O(); -// File: Barfoo.php -O(); -// File: Barfoo.php -O; -// File: expected -O(); -// File: expected -ivate $method; -} -// File: expected -method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor2.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor2.test deleted file mode 100644 index 32e751704d..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor2.test +++ /dev/null @@ -1,20 +0,0 @@ -// File: source - -} -// File: expected -method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor3.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor3.test deleted file mode 100644 index 5ef9378f80..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor3.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source - - - /** - * @var Type1 - */ - private $method; -} -// File: expected -method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor4.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor4.test deleted file mode 100644 index 894b2f3ed5..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor4.test +++ /dev/null @@ -1,30 +0,0 @@ -// File: source - -} -// File: expected -method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor5.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor5.test deleted file mode 100644 index 95f2bd413b..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor5.test +++ /dev/null @@ -1,30 +0,0 @@ -// File: source - -} -// File: expected -method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor6.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor6.test deleted file mode 100644 index 9d4534ed75..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor6.test +++ /dev/null @@ -1,38 +0,0 @@ -// File: source - -} - -class Bar -{ -} -// File: expected -method; - } -} - -class Bar -{ -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor7.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor7.test deleted file mode 100644 index 7f33ae50f5..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor7.test +++ /dev/null @@ -1,19 +0,0 @@ -// File: source -ivate $method; -} -// File: expected -method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor8.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor8.test deleted file mode 100644 index 65543438cd..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateAccessor8.test +++ /dev/null @@ -1,19 +0,0 @@ -// File: source -ivate $method; -} -// File: expected -method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateConstructor.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateConstructor.test deleted file mode 100644 index 7ad1704746..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateConstructor.test +++ /dev/null @@ -1,25 +0,0 @@ -// File: source -rfoo('foobar', 123); -// File: expected -rfoo(12); -// File: expected -rfoo('foobar', 123)] -class Foobar {} - -// File: expected -rfoo(); -// File: expected -rfoo('foobar', 1, 'barfoo', 'arg', 4); -// File: expected -rfoo(12); -// File: expected -foo implements SomethingToDecorate -{ -} - -// File: expected -inner = $inner; - } - - public function doSomething() - { - return $this->inner->doSomething(); - } -} - - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator2.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator2.test deleted file mode 100644 index 102ebaccef..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator2.test +++ /dev/null @@ -1,41 +0,0 @@ -// File: source -foo implements SomethingToDecorate -{ -} - -// File: expected -inner = $inner; - } - - public function doSomething(string $a, int $b) - { - return $this->inner->doSomething($a, $b); - } -} - - - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator3.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator3.test deleted file mode 100644 index 853b3243b1..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator3.test +++ /dev/null @@ -1,41 +0,0 @@ -// File: source -foo implements SomethingToDecorate -{ -} - -// File: expected -inner = $inner; - } - - public function doSomething(string $a, int $b): int - { - return $this->inner->doSomething($a, $b); - } -} - - - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator4.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator4.test deleted file mode 100644 index 1b45258fb9..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator4.test +++ /dev/null @@ -1,41 +0,0 @@ -// File: source -foo implements SomethingToDecorate -{ -} - -// File: expected -inner = $inner; - } - - public function doSomething(string $a, int $b = 3): int - { - return $this->inner->doSomething($a, $b); - } -} - - - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator5.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator5.test deleted file mode 100644 index 8252b81a7f..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator5.test +++ /dev/null @@ -1,42 +0,0 @@ -// File: source -foo implements SomethingToDecorate -{ -} - -// File: expected -inner = $inner; - } - - public function doSomething(): void - { - $this->inner->doSomething(); - } -} - - - - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator6.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator6.test deleted file mode 100644 index 69f3b55046..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateDecorator6.test +++ /dev/null @@ -1,50 +0,0 @@ -// File: source -foo implements SomethingToDecorate -{ -} - -// File: expected -inner = $inner; - } - - public function doSomething(): void - { - $this->inner->doSomething(); - } - - public function doSomethingElse(): int - { - return $this->inner->doSomethingElse(); - } -} - - - - - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember1.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember1.test deleted file mode 100644 index 684517eb0f..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember1.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source - -f<>oobar(); - } -} -// File: expected -foobar(); - } - - private function foobar() - { - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember10.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember10.test deleted file mode 100644 index cefb8a4366..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember10.test +++ /dev/null @@ -1,30 +0,0 @@ -// File: source - -obar(); - } -} -// File: expected -meMethod('foobar'); - } -} -// File: expected -<>convertParenthesized<>($type, $scope); - } - - return new MissingType(); - } -} -// File: expected -convertParenthesized($type, $scope); - } - - return new MissingType(); - } - - private function convertParenthesized(TypeNode&ParenthesizedType $type, ?ReflectionScope $scope): Type - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember13.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember13.test deleted file mode 100644 index 9b1035b068..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember13.test +++ /dev/null @@ -1,42 +0,0 @@ -// File: source - - */ - public function convert(?TypeNode $type, ?ReflectionScope $scope = null): array - { - if ($type instanceof ParenthesizedType) { - return $this-><>convertParenthesized<>($type, $scope); - } - - return new MissingType(); - } -} -// File: expected - - */ - public function convert(?TypeNode $type, ?ReflectionScope $scope = null): array - { - if ($type instanceof ParenthesizedType) { - return $this->convertParenthesized($type, $scope); - } - - return new MissingType(); - } - - private function convertParenthesized(TypeNode&ParenthesizedType $type, ?ReflectionScope $scope): array - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember14.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember14.test deleted file mode 100644 index 2cebb790cc..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember14.test +++ /dev/null @@ -1,37 +0,0 @@ -// File: source -<>generate<>($foo); - } -} -// File: expected -generate($foo); - } - - /** - * @param "foo"|"bar"|"baz" $foo - */ - private function generate(string $foo): void - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember15.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember15.test deleted file mode 100644 index 085b455ddb..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember15.test +++ /dev/null @@ -1,32 +0,0 @@ -// File: source -<>generate<>($this->ba()); - } - - public function ba(): ?string {} -} -// File: expected -generate($this->ba()); - } - - public function ba(): ?string {} - - private function generate(?string $string): void - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember16.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember16.test deleted file mode 100644 index c8746eaca9..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember16.test +++ /dev/null @@ -1,37 +0,0 @@ -// File: source - $foo - */ -function convert($foo): void -{ - return $foo-><>generate<>(); -} -// File: expected - $foo - */ -function convert($foo): void -{ - return $foo->generate(); -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember17.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember17.test deleted file mode 100644 index 45cbac4082..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember17.test +++ /dev/null @@ -1,36 +0,0 @@ -// File: source -<>generate<>(stringOrFalse()); - } -} -// File: expected -generate(stringOrFalse()); - } - - private function generate(string|false $stringFalse): void - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember18.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember18.test deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember2.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember2.test deleted file mode 100644 index 7dcaedf3df..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember2.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source - -f<>oobar($one); - } -} -// File: expected -foobar($one); - } - - private function foobar($one) - { - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember3.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember3.test deleted file mode 100644 index fcf7e2e8fe..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember3.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source - -f<>oobar($one); - } -} -// File: expected -foobar($one); - } - - private function foobar(Animal $one) - { - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember4.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember4.test deleted file mode 100644 index b2b2706138..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember4.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source - -f<>oobar($one); - } -} -// File: expected -foobar($one); - } - - private function foobar($one) - { - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember5.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember5.test deleted file mode 100644 index 7046d0ec06..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember5.test +++ /dev/null @@ -1,49 +0,0 @@ -// File: source - -b<>arfoo($this->anotherClass->someMethod()); - } -} -// File: expected -barfoo($this->anotherClass->someMethod()); - } - - private function barfoo(string $string) - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember6.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember6.test deleted file mode 100644 index 0b76fb1633..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember6.test +++ /dev/null @@ -1,42 +0,0 @@ -// File: source - -anotherClass->so<>meMethod('foobar'); - } -} -// File: expected -anotherClass->someMethod('foobar'); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember7.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember7.test deleted file mode 100644 index bd77d801bb..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember7.test +++ /dev/null @@ -1,40 +0,0 @@ -// File: source - -anotherClass->so<>meMethod('foobar'); - } -} -// File: expected -anotherClass->someMethod('foobar'); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember8.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember8.test deleted file mode 100644 index 05599b00e4..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember8.test +++ /dev/null @@ -1,30 +0,0 @@ -// File: source - -fo<>obar(); - } -} -// File: expected -foobar(); - } - - private function foobar() - { - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember9.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember9.test deleted file mode 100644 index 700e2b9b3c..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember9.test +++ /dev/null @@ -1,46 +0,0 @@ -// File: Carcar.php -carcar(); - $this->fo<>obar($bar); - } -} -// File: expected -carcar(); - $this->foobar($bar); - } - - private function foobar(Carcar $bar) - { - } -} - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_backedEnumParams.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_backedEnumParams.test deleted file mode 100644 index a6a2c6b1b7..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_backedEnumParams.test +++ /dev/null @@ -1,36 +0,0 @@ -// File: source -b<>arfoo(Alignment::Left); - } -} -// File: expected -barfoo(Alignment::Left); - } - - private function barfoo(Alignment $alignment) - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_complexTypeDocblock.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_complexTypeDocblock.test deleted file mode 100644 index cfa4c2fc50..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_complexTypeDocblock.test +++ /dev/null @@ -1,35 +0,0 @@ -// File: source - - $one - */ - public function name(array $one) - { - $this->f<>oobar($one); - } -} -// File: expected - - $one - */ - public function name(array $one) - { - $this->foobar($one); - } - - /** - * @param array $one - */ - private function foobar(array $one) - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_constant.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_constant.test deleted file mode 100644 index 24de6f98da..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_constant.test +++ /dev/null @@ -1,23 +0,0 @@ -// File: source - -; - } -} -// File: expected -; -// File: expected -f<>oobar('string', 'foo'); - } -} -// File: expected -foobar('string', 'foo'); - } - - private function foobar(string $string, string $string2) - { - } -} - - diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_enum.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_enum.test deleted file mode 100644 index 2e777865d0..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_enum.test +++ /dev/null @@ -1,28 +0,0 @@ -// File: source -ay(); - -// File: expected -; - } -} -// File: expected -b<>arfoo(Alignment::Left); - } -} -// File: expected -barfoo(Alignment::Left); - } - - private function barfoo(Alignment $alignment) - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_namedParams.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_namedParams.test deleted file mode 100644 index fa926a8e5f..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMember_namedParams.test +++ /dev/null @@ -1,23 +0,0 @@ -// File: source - -b<>arfoo(testing: true, foo: 10); - } -} -// File: expected -barfoo(testing: true, foo: 10); - } - - private function barfoo(bool $testing, int $foo) - { - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator1.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator1.test deleted file mode 100644 index 53bdacbfea..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator1.test +++ /dev/null @@ -1,20 +0,0 @@ -// File: source - -ivate $method; -} -// File: expected -method = $method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator2.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator2.test deleted file mode 100644 index 9357fb50bc..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator2.test +++ /dev/null @@ -1,20 +0,0 @@ -// File: source - -} -// File: expected -method = $method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator3.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator3.test deleted file mode 100644 index 81292613ec..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator3.test +++ /dev/null @@ -1,26 +0,0 @@ -// File: source - - - /** - * @var Type1 - */ - private $method; -} -// File: expected -method = $method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator4.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator4.test deleted file mode 100644 index 29151f45ba..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator4.test +++ /dev/null @@ -1,30 +0,0 @@ -// File: source - -} -// File: expected -method = $method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator5.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator5.test deleted file mode 100644 index f77ad5cdd3..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator5.test +++ /dev/null @@ -1,30 +0,0 @@ -// File: source - -} -// File: expected -method = $method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator6.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator6.test deleted file mode 100644 index a007873490..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator6.test +++ /dev/null @@ -1,38 +0,0 @@ -// File: source - -} - -class Bar -{ -} -// File: expected -method = $method; - } -} - -class Bar -{ -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator7.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator7.test deleted file mode 100644 index d507312507..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator7.test +++ /dev/null @@ -1,19 +0,0 @@ -// File: source -ivate $method; -} -// File: expected -method = $method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator8.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator8.test deleted file mode 100644 index a080d5db5c..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator8.test +++ /dev/null @@ -1,19 +0,0 @@ -// File: source -ivate $method; -} -// File: expected -method = $method; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator9.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator9.test deleted file mode 100644 index 00a8aa4982..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/generateMutator9.test +++ /dev/null @@ -1,21 +0,0 @@ -// File: source - -ivate $method; -} -// File: expected -method = $method; - return $this; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/heredoc_convert_to_escaped_string.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/heredoc_convert_to_escaped_string.test deleted file mode 100644 index 6eb6de02a1..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/heredoc_convert_to_escaped_string.test +++ /dev/null @@ -1,8 +0,0 @@ -// File: source - he said: "You can see the source"! -TA; -// File: expected -ring -TA; -// File: expected -ting"; -// File: expected -o"; -// File: expected -oobar(); -// File: expected -oobar(); -// File: expected -obar(); -// File: expected -oo::class; - -// File: expected -oo $foo) { } - -// File: expected -workspace()->put('Bag.php', 'workspace()->put('Boo.php', 'reflectorForWorkspace($example), $this->updater()); - $transformed = wait($transformer->transform(SourceCode::fromString($source))); - $this->assertEquals((string) $expected, (string) $transformed->apply($source)); - } - - /** - * @return Generator - */ - public static function provideCompleteConstructor(): Generator - { - yield 'It does nothing on source with no classes' => [ - <<<'EOT' - [ - <<<'EOT' - hello = 'Hello'; - } - } - EOT - , - <<<'EOT' - hello = 'Hello'; - } - } - EOT - - ]; - - yield 'It adds missing properties with documented type' => [ - <<<'EOT' - $bar - */ - public function hello(array $bar) - { - $this->hello = $bar; - } - } - EOT - , - <<<'EOT' - - */ - private $hello; - - /** - * @param array $bar - */ - public function hello(array $bar) - { - $this->hello = $bar; - } - } - EOT - - ]; - - yield 'It ignores existing properties' => [ - <<<'EOT' - hello = 'Hello'; - } - } - EOT - , - <<<'EOT' - hello = 'Hello'; - } - } - EOT - - ]; - - yield 'It ignores existing properties of a different visibility' => [ - <<<'EOT' - hello = 'Hello'; - } - } - EOT - , - <<<'EOT' - hello = 'Hello'; - } - } - EOT - ]; - - yield 'It appends new properties' => [ - <<<'EOT' - foobar = 1234; - } - } - EOT - , - <<<'EOT' - foobar = 1234; - } - } - EOT - ]; - - yield 'It appends new properties in a namespaced class' => [ - <<<'EOT' - foobar = 1234; - } - } - EOT - , - <<<'EOT' - foobar = 1234; - } - } - EOT - ]; - - yield 'Properties should only be taken from current class' => [ - <<<'EOT' - dodo = 'string'; - } - } - - class Foobar extends Dodo - { - public function hello() - { - $this->foobar = 1234; - } - } - EOT - , - <<<'EOT' - dodo = 'string'; - } - } - - class Foobar extends Dodo - { - /** - * @var int - */ - private $foobar; - - public function hello() - { - $this->foobar = 1234; - } - } - EOT - ]; - - yield 'It adds missing properties using the imported type' => [ - <<<'EOT' - hello = new Hello(); - } - } - EOT - , - <<<'EOT' - hello = new Hello(); - } - } - EOT - - ]; - - yield 'It missing properties with an untyped parameter' => [ - <<<'EOT' - hello = $string; - } - } - EOT - , - <<<'EOT' - hello = $string; - } - } - EOT - - ]; - - yield 'It adds missing trait properties within the Trait' => [ - <<<'EOT' - hello = 'goodbye'; - } - } - EOT - , - <<<'EOT' - hello = 'goodbye'; - } - } - EOT - ]; - - yield 'It adds missing property from call expression' => [ - <<<'EOT' - bar = $this->bar(); - } - - public function bar(): string - { - } - } - EOT - , - <<<'EOT' - bar = $this->bar(); - } - - public function bar(): string - { - } - } - EOT - ]; - - yield 'It adds missing property from array assignment' => [ - <<<'EOT' - bar['foo'] = $this->bar(); - } - - public function bar(): string - { - } - } - EOT - , - <<<'EOT' - - */ - private $bar = []; - - public function hello() - { - $this->bar['foo'] = $this->bar(); - } - - public function bar(): string - { - } - } - EOT - ]; - - yield 'It adds missing property from array add' => [ - <<<'EOT' - bar[] = $this->bar(); - } - - public function bar(): string - { - } - } - EOT - , - <<<'EOT' - bar[] = $this->bar(); - } - - public function bar(): string - { - } - } - EOT - ]; - - yield 'It imports classes' => [ - <<<'EOT' - foo = $foo->bar(); - } - } - EOT - , - <<<'EOT' - foo = $foo->bar(); - } - } - EOT - - ]; - } - - #[DataProvider('provideDiagnostics')] - public function testDiagnostics(string $example, int $diagnosticsCount): void - { - $source = SourceCode::fromString($example); - $transformer = new AddMissingProperties($this->reflectorForWorkspace($example), $this->updater()); - $diagnostics = wait($transformer->diagnostics($source)); - $this->assertCount($diagnosticsCount, $diagnostics); - } - - /** - * @return Generator - */ - public static function provideDiagnostics(): Generator - { - yield 'empty' => [ - ' [ - 'bar = "foo"; } }', - 1 - ]; - - yield 'not missing properties' => [ - 'bar = "foo"; } }', - 0 - ]; - - yield 'ignores property from another class' => [ - <<<'EOT' - doesNotMatter instanceof SecretImplementation); - - $anotherClass->doesNotMatter = 'test'; - } - } - EOT - , 0 - ]; - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/AddOverrideAttributeTransformerTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/AddOverrideAttributeTransformerTest.php deleted file mode 100644 index 775097123b..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/AddOverrideAttributeTransformerTest.php +++ /dev/null @@ -1,682 +0,0 @@ -workspace()->put('Bag.php', 'workspace()->put('Boo.php', 'reflectorForWorkspace($example), '8.3'); - $transformed = wait($transformer->transform($source)); - $this->assertEquals($expected, (string) $transformed->apply($source)); - } - - /** - * @return Generator - */ - public static function provideAddOverrideAttribute(): Generator - { - yield 'It does nothing on source with no classes' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - reflectorForWorkspace($example), '8.2'); - $transformed = wait($transformer->transform($source)); - $this->assertEquals($example, (string) $transformed->apply($source)); - $this->assertCount(0, wait($transformer->diagnostics($source))); - } - - public function testDiagnostics(): void - { - $example = <<<'EOT' - reflectorForWorkspace($example), '8.3'); - $diagnostics = iterator_to_array(wait($transformer->diagnostics($source))); - $this->assertCount(1, $diagnostics); - $this->assertEquals( - 'Method "foo" overrides a parent method but has no #[\Override] attribute', - reset($diagnostics)->message() - ); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/CompleteConstructorTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/CompleteConstructorTest.php deleted file mode 100644 index 85a0b407b9..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/CompleteConstructorTest.php +++ /dev/null @@ -1,777 +0,0 @@ -reflectorForWorkspace($example), $this->updater(), 'private', promote: false); - $this->assertCount($expectedCount, wait($transformer->diagnostics($source))); - } - /** - * @return Generator - */ - public static function provideDiagnostics(): Generator - { - yield 'empty' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - string = $string; } } - EOT - , 1 - ]; - } - - #[DataProvider('provideCompleteConstructor')] - public function testCompleteConstructor(string $example, string $expected): void - { - $source = SourceCode::fromString($example); - $transformer = new CompleteConstructor($this->reflectorForWorkspace($example), $this->updater(), 'private'); - $transformed = wait($transformer->transform($source)); - $this->assertEquals((string) $expected, (string) $transformed->apply($source)); - } - /** - * @return Generator - */ - public static function provideCompleteConstructor(): Generator - { - yield 'It does nothing on source with no classes' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - bar = $bar; - } - } - - class Foobar extends Barfoo - { - } - EOT - , - <<<'EOT' - bar = $bar; - } - } - - class Foobar extends Barfoo - { - } - EOT - ]; - - yield 'It does nothing with no constructor' => [ - <<<'EOT' - [ - <<<'EOT' - foo = $foo; - $this->bar = $bar; - } - } - EOT - - ]; - - yield 'adds assignations and properties on abstract class' => [ - <<<'EOT' - foo = $foo; - $this->bar = $bar; - } - } - EOT - - ]; - - yield 'it does not add parameters of inherited classes' => [ - <<<'EOT' - [ - <<<'EOT' - foo = $foo; - $this->bar = $bar; - } - } - EOT - - ]; - - yield 'It does adds nullable type docblocks' => [ - <<<'EOT' - foo = $foo; - } - } - EOT - - ]; - - yield 'Adds documented types' => [ - <<<'EOT' - $foo - */ - public function __construct(array $foo) - { - } - } - EOT - , - <<<'EOT' - - */ - private $foo; - - /** - * @param Foo $foo - */ - public function __construct(array $foo) - { - $this->foo = $foo; - } - } - EOT - - ]; - - yield 'It is idempotent' => [ - <<<'EOT' - foo = $foo; - } - } - EOT - , - <<<'EOT' - foo = $foo; - } - } - EOT - - ]; - - yield 'It is updates missing' => [ - <<<'EOT' - foo = $foo; - } - } - EOT - , - <<<'EOT' - foo = $foo; - $this->acme = $acme; - } - } - EOT - - ]; - - yield 'It does not redeclare' => [ - <<<'EOT' - foo = $foo ?: null; - } - } - EOT - , - <<<'EOT' - foo = $foo ?: null; - } - } - EOT - - ]; - - yield 'Existing property with assignment' => [ - <<<'EOT' - bar = $bar; - } - } - EOT - - ]; - - yield 'Aliased import' => [ - <<<'EOT' - bar = $bar; - } - } - EOT - - ]; - - yield 'Aliased relative import' => [ - <<<'EOT' - bar = $bar; - } - } - EOT - - ]; - - yield 'Ignores promoted properties' => [ - <<<'EOT' - foo = $foo; - } - } - EOT - - ]; - - yield 'Importing property before constants' => [ - <<<'EOT' - bar = $bar; - } - } - EOT - - ]; - } - - #[DataProvider('provideCompleteConstructorPromote')] - public function testCompleteConstructorPromote(string $example, string $expected): void - { - $source = SourceCode::fromString($example); - $transformer = new CompleteConstructor($this->reflectorForWorkspace($example), $this->updater(), 'private', true); - $transformed = wait($transformer->transform($source)); - $this->assertEquals((string) $expected, (string) $transformed->apply($source)); - } - - /** - * @return Generator - */ - public static function provideCompleteConstructorPromote(): Generator - { - yield 'It does adds assignations and properties' => [ - <<<'EOT' - [ - <<<'EOT' - reflectorForWorkspace($example), $this->updater(), 'private', true); - $transformed = wait($transformer->transform($source)); - $this->assertEquals((string) $expected, (string) $transformed->apply($source)); - } - - /** - * @return Generator - */ - public static function provideCompleteConstructorWithParentClass(): Generator - { - yield 'Do not promote constructor arguments if a parent class already has the same argument' => [ - <<<'EOT' - [ - <<<'EOT' - class B { - public function __construct(private string $a) {} - } - class A extends B {} - - class Foo extends A { - public function __construct(string $a) {parent::__construct($a);} - } - EOT, - <<<'EOT' - class B { - public function __construct(private string $a) {} - } - class A extends B {} - - class Foo extends A { - public function __construct(string $a) {parent::__construct($a);} - } - EOT, - ]; - - } - -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/ImplementContractsTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/ImplementContractsTest.php deleted file mode 100644 index 1e65130be8..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/ImplementContractsTest.php +++ /dev/null @@ -1,576 +0,0 @@ -reflectorForWorkspace($example); - $transformer = new ImplementContracts($reflector, $this->updater(), $this->builderFactory($reflector)); - $transformed = wait($transformer->transform($source)); - $this->assertEquals((string) $expected, (string) $transformed->apply($source)); - } - - /** - * @return Generator> - */ - public static function provideCompleteConstructor(): Generator - { - yield 'It does nothing on source with no classes' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - reflectorForWorkspace($example), - $this->updater(), - $this->builderFactory($this->reflectorForWorkspace($example)) - ); - $this->assertCount($expectedCount, wait($transformer->diagnostics($source))); - } - - /** - * @return Generator - */ - public static function provideDiagnostics(): Generator - { - yield 'empty' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - reflectorForWorkspace($example), - new TolerantAstProvider() - ); - $transformed = wait($transformer->transform(SourceCode::fromString($source))); - $this->assertEquals((string) $expected, (string) $transformed->apply($source)); - } - - /** - * @return Generator - */ - public static function provideRemoveUnusedImports(): Generator - { - yield 'It does nothing on source with no classes' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - workspace()->put( - 'Example1.php', - 'workspace()->put( - 'Example2.php', - 'workspace()->put( - 'Example3.php', - 'workspace()->put( - 'Example4.php', - 'reflectorForWorkspace($example); - $transformer = $this->createTransformer($reflector); - $transformed = wait($transformer->transform($source))->apply($source); - self::assertEquals($expected, $transformed); - } - - /** - * @return Generator - */ - public static function provideTransform(): Generator - { - yield 'add missing extends' => [ - <<<'EOT' - - */ - class Foobar extends Generic { - } - EOT - ]; - yield 'updates missing extends' => [ - <<<'EOT' - - */ - class Foobar extends Generic { - } - EOT - ]; - yield 'ignores valid extends' => [ - <<<'EOT' - - */ - class Foobar extends Generic { - } - EOT - , - <<<'EOT' - - */ - class Foobar extends Generic { - } - EOT - ]; - yield 'adds extends' => [ - <<<'EOT' - - */ - class Foobar extends Generic { - } - EOT - , - <<<'EOT' - - */ - class Foobar extends Generic { - } - EOT - ]; - yield 'ignores compatible object' => [ - <<<'EOT' - - */ - class Foobar extends NeedsObject { - } - EOT - , - <<<'EOT' - - */ - class Foobar extends NeedsObject { - } - EOT - ]; - yield 'does not fix incompatible object' => [ - <<<'EOT' - - */ - class Foobar extends NeedsObject { - } - EOT - , - <<<'EOT' - - */ - class Foobar extends NeedsObject { - } - EOT - ]; - yield 'implements' => [ - <<<'EOT' - - */ - class Foobar implements GenericInterface { - } - EOT - ]; - yield 'implements of' => [ - <<<'EOT' - - */ - class Foobar implements NeedsObjectInterface { - } - EOT - ]; - } - - private function createTransformer(Reflector $reflector): UpdateDocblockGenericTransformer - { - return new UpdateDocblockGenericTransformer( - $reflector, - $this->updater(), - $this->builderFactory($reflector), - new ParserDocblockUpdater(DocblockParser::create(), new TextFormat()) - ); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformerTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformerTest.php deleted file mode 100644 index 6a27033b0e..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformerTest.php +++ /dev/null @@ -1,163 +0,0 @@ -workspace()->put( - 'Example.php', - 'workspace()->put( - 'Example1.php', - 'reflectorForWorkspace($example); - $transformer = $this->createTransformer($reflector); - $transformed = wait($transformer->transform($source))->apply($source); - self::assertEquals($expected, $transformed); - } - - /** - * @return Generator - */ - public static function provideTransform(): Generator - { - yield 'add missing docblock and param' => [ - <<<'EOT' - $param - */ - public function baz(array $param): array - { - } - } - EOT - ]; - yield 'add missing param' => [ - <<<'EOT' - $param - */ - public function baz(array $param): array - { - } - } - EOT - ]; - yield 'add multiple missing param' => [ - <<<'EOT' - $param - * @param array $baz - */ - public function baz(array $param, array $baz): array - { - } - } - EOT - ]; - yield 'imports class' => [ - <<<'EOT' - $gen - */ - public function baz(Generic $gen): array - { - } - } - EOT - ]; - } - - private function createTransformer(Reflector $reflector): UpdateDocblockParamsTransformer - { - return new UpdateDocblockParamsTransformer( - $reflector, - $this->updater(), - $this->builderFactory($reflector), - new ParserDocblockUpdater(DocblockParser::create(), new TextFormat()) - ); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformerTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformerTest.php deleted file mode 100644 index e02ec3546d..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformerTest.php +++ /dev/null @@ -1,922 +0,0 @@ -workspace()->put( - 'Example.php', - 'workspace()->put( - 'Example2.php', - 'reflectorForWorkspace($example); - $transformer = $this->createTransformer($reflector); - $transformed = wait($transformer->transform($source))->apply($source); - self::assertEquals($expected, $transformed); - } - - /** - * @return Generator - */ - public static function provideUpdateReturn(): Generator - { - yield 'add missing docblock' => [ - <<<'EOT' - array(); - } - - /** @return array */ - private function array(): array - { - return ['string' => new Baz']; - } - } - EOT - , - <<<'EOT' - - */ - public function baz(): array - { - return $this->array(); - } - - /** @return array */ - private function array(): array - { - return ['string' => new Baz']; - } - } - EOT - ]; - - yield 'add array literal' => [ - <<<'EOT' - 'bar', - 'baz' => 'boo', - ]; - } - } - EOT - , - <<<'EOT' - - */ - public function baz(): array - { - return [ - 'foo' => 'bar', - 'baz' => 'boo', - ]; - } - } - EOT - ]; - - yield 'add union of array literals' => [ - <<<'EOT' - 'bar', - ]; - } - - return [ - 'foo' => 'bar', - 'baz' => 'boo', - ]; - } - } - EOT - , - <<<'EOT' - - */ - public function baz(): array - { - if ($foo) { - return [ - 'baz' => 'bar', - ]; - } - - return [ - 'foo' => 'bar', - 'baz' => 'boo', - ]; - } - } - EOT - ]; - - yield 'permit wider return types' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - */ - $foo; - return $foo; - } - } - EOT - , - <<<'EOT' - - */ - public function baz(): Foo - { - /** @var ConcreteFoo */ - $foo; - return $foo; - } - } - EOT - ]; - - yield 'add void return to interfaces by default' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - - */ - public function baz() - { - yield 'foo'; - } - } - EOT - ]; - - yield 'generator with unioned namespaced classes' => [ - <<<'EOT' - - */ - public function baz(): Generator - { - yield ['1', new NSTest()]; - yield ['2', new NSTest2()]; - } - } - EOT - ]; - - yield 'add generator with array value' => [ - <<<'EOT' - [ - 'val', - new stdClass(), - ]; - yield 'bar' => [ - 'lav', - new stdClass(), - ]; - } - } - EOT - , - <<<'EOT' - - */ - public function t5() - { - yield 'foo' => [ - 'val', - new stdClass(), - ]; - yield 'bar' => [ - 'lav', - new stdClass(), - ]; - } - } - EOT - ]; - - yield 'adds docblock for array' => [ - <<<'EOT' - null, []); - } - } - EOT - , - <<<'EOT' - null, []); - } - } - EOT - ]; - - yield 'add docblock for iterables' => [ - <<<'EOT' - baz(); - } - - /** - * @return iterable - */ - public function baz(): iterable - { - yield 22; - } - } - EOT - , - <<<'EOT' - - */ - public function bar(): iterable - { - return $this->baz(); - } - - /** - * @return iterable - */ - public function baz(): iterable - { - yield 22; - } - } - EOT - ]; - - yield 'does not add non-array return type when array return is given' => [ - <<<'EOT' - foo(); - } - - /** - * @return mixed - */ - private function foo() {} - } - EOT - , - <<<'EOT' - foo(); - } - - /** - * @return mixed - */ - private function foo() {} - } - EOT - ]; - - yield 'adds docblock for closure' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - bazes(); - } - } - EOT - , - <<<'EOT' - bazes(); - } - } - EOT - ]; - - yield 'inherited type' => [ - <<<'EOT' - [ - <<<'EOT' - - */ - public function baz(): array - { - yield [ - 'foobar', - function (Bar $b): string { - } - ]; - } - } - EOT - ]; - - yield 'trait' => [ - <<<'EOT' - - */ - public function baz(): array - { - yield [ - 'foobar', - function (Bar $b): string { - } - ]; - } - } - EOT - ]; - - yield 'updates existing docblock' => [ - <<<'EOT' - - */ - public function baz(): array - { - yield [ - 'foobar', - function (Bar $b): string { - } - ]; - } - } - EOT - ]; - - yield 'updates existing docblock with other tags' => [ - <<<'EOT' - - */ - public function baz(): array - { - yield [ - 'foobar', - function (Bar $b): string { - } - ]; - } - } - EOT - ]; - } - - /** - * @param string[] $expected - */ - #[DataProvider('provideDiagnostics')] - public function testDiagnostics(string $example, array $expected): void - { - $source = SourceCode::fromString($example); - $reflector = $this->reflectorForWorkspace($example); - $transformer = $this->createTransformer($reflector); - $diagnostics = array_map(fn (Diagnostic $d) => $d->message(), iterator_to_array(wait($transformer->diagnostics($source)))); - self::assertEquals($expected, $diagnostics); - } - - /** - * @return Generator - */ - public static function provideDiagnostics(): Generator - { - yield 'no methods' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - array(); - } - - /** @return array */ - private function array(): array - { - return ['string' => new Baz']; - } - } - EOT - , - [ - 'Missing @return array', - ] - ]; - } - - private function createTransformer(Reflector $reflector): UpdateDocblockReturnTransformer - { - return new UpdateDocblockReturnTransformer( - $reflector, - $this->updater(), - $this->builderFactory($reflector), - new ParserDocblockUpdater(DocblockParser::create(), new TextFormat()) - ); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformerTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformerTest.php deleted file mode 100644 index 1cc93edf25..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformerTest.php +++ /dev/null @@ -1,475 +0,0 @@ -workspace()->put( - 'Example.php', - 'reflectorForWorkspace($example); - $transformer = $this->createTransformer($reflector); - $transformed = wait($transformer->transform($source))->apply($source); - self::assertEquals($expected, $transformed); - } - - /** - * @return Generator - */ - public static function provideTransform(): Generator - { - yield 'add missing return type' => [ - <<<'EOT' - new Baz']; - } - } - EOT - , - <<<'EOT' - new Baz']; - } - } - EOT - ]; - - yield 'add generator return type' => [ - <<<'EOT' - [ - <<<'EOT' - new Baz']; - } - } - EOT - , - <<<'EOT' - new Baz']; - } - } - EOT - ]; - - yield 'update nullable type' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - baz(); - } - - private function baz() - { - } - } - EOT - , - <<<'EOT' - baz(); - } - - private function baz(): void - { - } - } - EOT - ]; - yield 'adds false type' => [ - <<<'EOT' - baz(); - } - - private function baz(): string|false - { - } - } - EOT - , - <<<'EOT' - baz(); - } - - private function baz(): string|false - { - } - } - EOT - ]; - yield 'adds array shape type' => [ - <<<'EOT' - baz(); - } - - /** - * @return array{string,string} - */ - private function baz(): array - { - } - } - EOT - , - <<<'EOT' - baz(); - } - - /** - * @return array{string,string} - */ - private function baz(): array - { - } - } - EOT - ]; - } - - /** - * @param string[] $expected - */ - #[DataProvider('provideDiagnostics')] - public function testDiagnostics(string $example, array $expected): void - { - $source = SourceCode::fromString($example); - $reflector = $this->reflectorForWorkspace($example); - $transformer = $this->createTransformer($reflector); - $diagnostics = array_map(fn (Diagnostic $d) => $d->message(), iterator_to_array(wait($transformer->diagnostics($source)))); - self::assertEquals($expected, $diagnostics); - } - - /** - * @return Generator - */ - public static function provideDiagnostics(): Generator - { - yield 'no methods' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - array(); - } - - /** @return array */ - private function array(): array - { - return ['string' => new Baz']; - } - } - EOT - , - [ - 'Missing return type `array`', - ] - ]; - - yield 'ignores constructor' => [ - <<<'EOT' - [ - <<<'EOT' - [ - <<<'EOT' - item; - } - } - EOT - , - [ - ] - ]; - - yield 'never on interface' => [ - <<<'EOT' - [ - <<<'EOT' - updater(), - $this->builderFactory($reflector) - ); - } -} diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/WorseTestCase.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/WorseTestCase.php deleted file mode 100644 index 56a208066e..0000000000 --- a/lib/CodeTransform/Tests/Adapter/WorseReflection/WorseTestCase.php +++ /dev/null @@ -1,58 +0,0 @@ -addMemberProvider(new DocblockMemberProvider()); - $builder->addDiagnosticProvider(new MissingMemberProvider()); - $builder->addDiagnosticProvider(new DocblockMissingReturnTypeProvider()); - $builder->addDiagnosticProvider(new AssignmentToMissingPropertyProvider()); - $builder->addDiagnosticProvider(new MissingReturnTypeProvider()); - $builder->addDiagnosticProvider(new UnusedImportProvider()); - $builder->addDiagnosticProvider(new DocblockMissingParamProvider()); - $builder->addDiagnosticProvider(new DocblockMissingExtendsTagProvider()); - $builder->addDiagnosticProvider(new DocblockMissingImplementsTagProvider()); - - foreach ((array)glob($this->workspace()->path('/*.php')) as $file) { - if ($file === false) { - continue; - } - - $locator = new TemporarySourceLocator(ReflectorBuilder::create()->build(), true); - $locator->pushSourceCode(TextDocumentBuilder::fromUri($file)->build()); - $builder->addLocator($locator); - } - - if ($source !== null) { - $builder->addSource(TextDocumentBuilder::create($source)->uri('/foo')->build()); - } - - return $builder->build(); - } - - public function builderFactory(Reflector $reflector): BuilderFactory - { - return new WorseBuilderFactory($reflector); - } -} diff --git a/lib/CodeTransform/Tests/Benchmark/Adapter/WorseReflection/Util/class/empty b/lib/CodeTransform/Tests/Benchmark/Adapter/WorseReflection/Util/class/empty deleted file mode 100644 index b3d9bbc7f3..0000000000 --- a/lib/CodeTransform/Tests/Benchmark/Adapter/WorseReflection/Util/class/empty +++ /dev/null @@ -1 +0,0 @@ -prophesize(Transformer::class); - $trans1->transform(Argument::type(SourceCode::class))->willReturn(new Success(TextEdits::one( - TextEdit::create(ByteOffset::fromInt(5), 0, ' goodbye') - ))); - - $code = $this->create([ - 'one' => $trans1->reveal() - ])->transform('hello', [ 'one' ]); - - $this->assertEquals($expectedCode, $code); - } - - public function testAcceptsSourceCodeAsParameter(): void - { - $expectedCode = SourceCode::fromStringAndPath('hello goodbye', '/path/to'); - - $trans1 = $this->prophesize(Transformer::class); - $trans1->transform($expectedCode)->willReturn(new Success(TextEdits::none())); - - $code = $this->create([ - 'one' => $trans1->reveal() - ])->transform($expectedCode, [ 'one' ]); - - $this->assertEquals($expectedCode, $code); - } - - - public function create(array $transformers): CodeTransform - { - /** @phpstan-ignore-next-line */ - return CodeTransform::fromTransformers(Transformers::fromArray($transformers)); - } -} diff --git a/lib/CodeTransform/Tests/Unit/Domain/ClassNameTest.php b/lib/CodeTransform/Tests/Unit/Domain/ClassNameTest.php deleted file mode 100644 index 6bccbb56a6..0000000000 --- a/lib/CodeTransform/Tests/Unit/Domain/ClassNameTest.php +++ /dev/null @@ -1,49 +0,0 @@ -assertEquals('This\\Is\\A\\Namespace', $class->namespace()); - } - - /** - * It returns empty strsing if no namespace - */ - public function testNamespaceNone(): void - { - $class = ClassName::fromString('ClassName'); - $this->assertEquals('', $class->namespace()); - } - - #[TestDox('It returns the class short name')] - public function testShort(): void - { - $class = ClassName::fromString('Namespace\\ClassName'); - $this->assertEquals('ClassName', $class->short()); - } - - #[TestDox('It returns the class short name with no namespace')] - public function testShortNoNamespace(): void - { - $class = ClassName::fromString('ClassName'); - $this->assertEquals('ClassName', $class->short()); - } - - #[TestDox('It throws exception if classname is empty.')] - public function testEmpty(): void - { - $this->expectExceptionMessage('Class name cannot be empty'); - ClassName::fromString(''); - } -} diff --git a/lib/CodeTransform/Tests/Unit/Domain/GeneratorsTest.php b/lib/CodeTransform/Tests/Unit/Domain/GeneratorsTest.php deleted file mode 100644 index cdc93e5fee..0000000000 --- a/lib/CodeTransform/Tests/Unit/Domain/GeneratorsTest.php +++ /dev/null @@ -1,33 +0,0 @@ -prophesize(Generator::class); - $generator2 = $this->prophesize(Generator::class); - - $generators = Generators::fromArray([ - 'one' => $generator1->reveal(), - 'two' => $generator2->reveal(), - ]); - - $this->assertSame($generator1->reveal(), $generators->get('one')); - $this->assertCount(2, $generators); - $this->assertSame([ - 'one' => $generator1->reveal(), - 'two' => $generator2->reveal(), - ], iterator_to_array($generators)); - } -} diff --git a/lib/CodeTransform/Tests/Unit/Domain/NameWithByteOffsetsTest.php b/lib/CodeTransform/Tests/Unit/Domain/NameWithByteOffsetsTest.php deleted file mode 100644 index 37c5139d3f..0000000000 --- a/lib/CodeTransform/Tests/Unit/Domain/NameWithByteOffsetsTest.php +++ /dev/null @@ -1,44 +0,0 @@ -onlyUniqueNames() - ); - } -} diff --git a/lib/CodeTransform/Tests/Unit/Domain/SourceCodeTest.php b/lib/CodeTransform/Tests/Unit/Domain/SourceCodeTest.php deleted file mode 100644 index e8b98ed8e2..0000000000 --- a/lib/CodeTransform/Tests/Unit/Domain/SourceCodeTest.php +++ /dev/null @@ -1,88 +0,0 @@ -assertEquals(self::PATH, $source->uri()->path()); - } - - public function testFromUnknownReturnsSourceCodeIfPassedSourceCode(): void - { - $source1 = SourceCode::fromStringAndPath(self::SOURCE, self::PATH); - $source2 = SourceCode::fromUnknown($source1); - - $this->assertSame($source1, $source2); - } - - public function testFromUnknownReturnsSourceCodeIfPassedString(): void - { - $source1 = 'hello'; - $source2 = SourceCode::fromUnknown($source1); - - $this->assertEquals(SourceCode::fromString($source1), $source2); - } - - public function testFromUnknownThrowsExceptionIfTypeIsInvalid(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Do not know'); - $source2 = SourceCode::fromUnknown(1234); - } - - public function testWithSource(): void - { - $source1 = SourceCode::fromStringAndPath(self::SOURCE, self::PATH); - $source2 = $source1->withSource(self::OTHER_SOURCE); - - $this->assertEquals(self::OTHER_SOURCE, $source2->__toString()); - $this->assertNotSame($source1, $source2); - } - - public function testWithPath(): void - { - $source1 = SourceCode::fromStringAndPath(self::SOURCE, self::PATH); - $source2 = $source1->withPath(self::OTHER_PATH); - - $this->assertEquals(self::OTHER_PATH, $source2->uri()->path()); - $this->assertNotSame($source1, $source2); - } - - public function testNonAbsolutePath(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('must be absolute'); - SourceCode::fromStringAndPath('asdf', 'path'); - } - - public function testCanonicalizePath(): void - { - $sourceCode = SourceCode::fromStringAndPath('asd', '/path/to/here/../'); - $this->assertEquals('/path/to', $sourceCode->uri()->path()); - } - - public function testExtractSelection(): void - { - $sourceCode = SourceCode::fromString('12345678'); - $this->assertEquals('34', $sourceCode->extractSelection(2, 4)); - } - - public function testReplaceSelection(): void - { - $sourceCode = SourceCode::fromString('12345678'); - $this->assertEquals('12HE5678', (string) $sourceCode->replaceSelection('HE', 2, 4)); - } -} diff --git a/lib/CodeTransform/Tests/Unit/Domain/Utils/TextUtilsTest.php b/lib/CodeTransform/Tests/Unit/Domain/Utils/TextUtilsTest.php deleted file mode 100644 index f95b5b2b4b..0000000000 --- a/lib/CodeTransform/Tests/Unit/Domain/Utils/TextUtilsTest.php +++ /dev/null @@ -1,58 +0,0 @@ -assertEquals($expected, TextUtils::removeIndentation($code)); - } - - public static function provideRemoveIndentation() - { - yield [ - ' hello', - 'hello' - ]; - - yield [ - <<<'EOT' - hello - world - hello - world - EOT - , - <<<'EOT' - hello - world - hello - world - EOT - ]; - - yield [ - <<<'EOT' - hello - hello - world - hello - world - EOT - , - <<<'EOT' - hello - hello - world - hello - world - EOT - ]; - } -} diff --git a/lib/Completion/Bridge/ObjectRenderer/ItemDocumentation.php b/lib/Completion/Bridge/ObjectRenderer/ItemDocumentation.php deleted file mode 100644 index 92d143c3a2..0000000000 --- a/lib/Completion/Bridge/ObjectRenderer/ItemDocumentation.php +++ /dev/null @@ -1,28 +0,0 @@ -docs)); - } - - public function name(): string - { - return $this->name; - } - - public function object(): object - { - return $this->object; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/ChainTolerantCompletor.php b/lib/Completion/Bridge/TolerantParser/ChainTolerantCompletor.php deleted file mode 100644 index 6b2d62e895..0000000000 --- a/lib/Completion/Bridge/TolerantParser/ChainTolerantCompletor.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ - public function complete(TextDocument $source, ByteOffset $byteOffset): Generator - { - $node = $this->provider->get($source, $byteOffset); - $isComplete = true; - - foreach ($this->tolerantCompletors as $tolerantCompletor) { - $start = microtime(true); - $completionNode = $node; - - if ($tolerantCompletor instanceof TolerantQualifiable) { - $completionNode = $tolerantCompletor->qualifier()->couldComplete($node); - } - - if (!$completionNode) { - $this->logger->timeTaken($tolerantCompletor, microtime(true) - $start); - continue; - } - - $suggestions = $tolerantCompletor->complete($completionNode, $source, $byteOffset); - - yield from $suggestions; - - $this->logger->timeTaken($tolerantCompletor, microtime(true) - $start); - - $isComplete = $isComplete && $suggestions->getReturn(); - } - - return $isComplete; - } - - private function filterNonQualifyingClasses(Node $node): array - { - return array_filter($this->tolerantCompletors, function (TolerantCompletor $completor) use ($node) { - if (!$completor instanceof TolerantQualifiable) { - return true; - } - - return null !== $completor->qualifier()->couldComplete($node); - }); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/CompletionContext.php b/lib/Completion/Bridge/TolerantParser/CompletionContext.php deleted file mode 100644 index 5ceb7fc2b1..0000000000 --- a/lib/Completion/Bridge/TolerantParser/CompletionContext.php +++ /dev/null @@ -1,410 +0,0 @@ -parent; - - if (null === $parent) { - return false; - } - - if ( - $parent instanceof BinaryExpression - && $parent->operator->kind === TokenKind::LessThanToken - && str_starts_with(ltrim($parent->__toString()), '<<<') - ) { - return false; - } - - if ($parent instanceof ArgumentExpression) { - return true; - } - - if (self::classMembersBody($node)) { - return false; - } - $previous = NodeUtil::previousSibling($node->parent); - - if ($previous instanceof InlineHtml) { - $phpTag = $previous->scriptSectionStartTag?->getText($previous->getFileContents()); - - if ($phpTag === 'getStartPosition() === $previous->getEndPosition()) { - return false; - } - } - - return - $parent instanceof Expression || - $parent instanceof StatementNode || - $parent instanceof ConstElement || - $parent instanceof MatchArmConditionList || - $parent instanceof MatchArm || - $parent instanceof ArrayElement // yield; - ; - } - - public static function attribute(?Node $node): bool - { - if (null === $node) { - return false; - } - - return - $node instanceof AttributeGroup || - $node instanceof Attribute || - $node->parent instanceof Attribute; - } - - public static function useImport(?Node $node): bool - { - if (null === $node) { - return false; - } - return $node->parent instanceof NamespaceUseClause; - } - - public static function classLike(?Node $node): bool - { - if (null === $node) { - return false; - } - $parent = $node->parent; - if (null === $parent) { - return false; - } - if ($parent->parent) { - if (self::isClassClause($parent->parent)) { - return true; - } - } - - return self::isClassClause($parent); - } - - public static function type(?Node $node): bool - { - if (null === $node) { - return false; - } - - if (null === $node->parent) { - return false; - } - - // no type-completion clauses (extends, implements, use) - // as these are class-like only - if ($node->parent->parent) { - if ( - self::isClassClause($node->parent->parent) - ) { - return false; - } - } - - if ( - $node->parent instanceof Parameter || - $node->parent instanceof QualifiedNameList - ) { - return true; - } - - return false; - } - - public static function nodeOrParentIs(?Node $node, string $type): bool - { - if (null === $node) { - return false; - } - if ($node instanceof $type) { - return true; - } - - if ($node->parent instanceof $type) { - return true; - } - return false; - } - - public static function classMembersBody(?Node $node): bool - { - if (null === $node) { - return false; - } - - if ($node instanceof ClassMembersNode) { - return true; - } - - if (null === $node->parent) { - return false; - } - - if ($node->parent instanceof ConstElement) { - return false; - } - - if ($node instanceof Variable) { - return false; - } - - if ( - $node->parent instanceof MethodDeclaration - && $node instanceof CompoundStatementNode - && $node->openBrace instanceof MissingToken - ) { - return false; - } - - $nodeBeforeOffset = NodeUtil::firstDescendantNodeBeforeOffset($node->getRoot(), $node->parent->getStartPosition()); - - if ($nodeBeforeOffset instanceof ClassMembersNode) { - return true; - } - - $classLike = $nodeBeforeOffset->getFirstAncestor(ClassLike::class); - if (!$classLike) { - return false; - } - if ($classLike->getEndPosition() < $node->getStartPosition()) { - if ($classLike instanceof ClassDeclaration) { - if (!$classLike->classMembers->closeBrace instanceof MissingToken) { - return false; - } - } - if ($classLike instanceof InterfaceDeclaration) { - if (!$classLike->interfaceMembers->closeBrace instanceof MissingToken) { - return false; - } - } - if ($classLike instanceof TraitDeclaration) { - if (!$classLike->traitMembers->closeBrace instanceof MissingToken) { - return false; - } - } - if ($classLike instanceof EnumDeclaration) { - if (!$classLike->enumMembers->closeBrace instanceof MissingToken) { - return false; - } - } - } - - if ($nodeBeforeOffset instanceof CompoundStatementNode && $node->getStartPosition() < $nodeBeforeOffset->getEndPosition()) { - return false; - } - - $memberDeclaration = $nodeBeforeOffset->getFirstAncestor(MethodDeclaration::class, ConstElement::class); - - if (!$memberDeclaration) { - return true; - } - - if ($memberDeclaration->getEndPosition() < $node->getStartPosition()) { - return true; - } - - return false; - } - - public static function classClause(?Node $node, ByteOffset $offset): bool - { - if (null === $node) { - return false; - } - - $prefix = substr($node->getFileContents(), 0, $offset->toInt()); - if (preg_match('{(class|interface|trait)\s+[^\s]+\s*[^\s\{]*$}', $prefix)) { - return true; - } - if (preg_match('{(class|interface|trait)\s+[^\s]+\s*(implements|extends)\s+([^,\s\{]+[,\s]*)*$}', $prefix)) { - return true; - } - - return false; - } - - public static function anonymousUse(Node $node): bool - { - if (!$node->parent) { - return false; - } - $compound = $node->parent->parent; - if (!$compound instanceof CompoundStatementNode) { - return false; - } - $anonymous = $compound->parent; - if (!$anonymous instanceof AnonymousFunctionCreationExpression) { - return false; - } - if (!$compound->openBrace instanceof MissingToken) { - return false; - } - if (!$anonymous->anonymousFunctionUseClause) { - return false; - } - - return true; - } - - public static function methodName(Node $node): bool - { - // If the body (as the current node) is empty, the parent is MethodDeclaration - if ($node instanceof CompoundStatementNode && !$node->openBrace instanceof MissingToken) { - return false; - } - - if (!$node->parent instanceof MethodDeclaration) { - return false; - } - - return $node->parent->openParen instanceof MissingToken; - } - - public static function declaration(Node $node, ByteOffset $offset): bool - { - if (!$node->parent) { - return false; - } - if (!$node->parent->parent) { - return false; - } - if (!$node instanceof MicrosoftQualifiedName) { - return false; - } - if (!$node->parent->parent instanceof SourceFileNode) { - return false; - } - - if ($node->parent->getText() !== $node->getText()) { - return false; - } - - $previous = NodeUtil::previousSibling($node->parent); - - // for some reason `class Foobar { func<>` will result in `func` being an sibling to `class Foobar` instead of - // within the members node. - // To fix this ensure that if the previous is a declaration then make sure that it doesn't have a missing closed token - if ($previous instanceof ClassDeclaration) { - if ($previous->classMembers->closeBrace instanceof MissingToken) { - return false; - } - } - if ($previous instanceof TraitDeclaration) { - if ($previous->traitMembers->closeBrace instanceof MissingToken) { - return false; - } - } - if ($previous instanceof InterfaceDeclaration) { - if ($previous->interfaceMembers->closeBrace instanceof MissingToken) { - return false; - } - } - if ($previous instanceof EnumDeclaration) { - if ($previous->enumMembers->closeBrace instanceof MissingToken) { - return false; - } - } - if ($node->getEndPosition() < $previous->getEndPosition()) { - return false; - } - - return true; - } - - public static function promotedPropertyVisibility(Node $node): bool - { - $methodDeclaration = $node->getFirstAncestor(MethodDeclaration::class); - if (!$methodDeclaration instanceof MethodDeclaration) { - return false; - } - if ($methodDeclaration->getName() !== '__construct') { - return false; - } - if ($node instanceof CompoundStatementNode) { - return true; - } - $parameter = $node->getFirstAncestor(Parameter::class); - if (!$parameter instanceof Parameter) { - return false; - } - if (NodeUtil::nullOrMissing($parameter->variableName)) { - return true; - } - - return false; - } - - public static function conditionInfix(Node $node): bool - { - // If the current node is not a variable we're at the beginning of the condition like "if (<>" - if ($node->getText() === '') { - return false; - } - - $parent = $node->parent; - if (!$parent instanceof ExpressionStatement) { - return false; - } - - $grandParent = $parent->parent; - return $grandParent instanceof IfStatementNode || $grandParent instanceof WhileStatement; - } - - private static function isClassClause(?Node $node): bool - { - if (null === $node) { - return false; - } - return - $node instanceof InterfaceBaseClause || - $node instanceof ClassInterfaceClause || - $node instanceof TraitUseClause || - $node instanceof ClassBaseClause; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/DebugTolerantCompletor.php b/lib/Completion/Bridge/TolerantParser/DebugTolerantCompletor.php deleted file mode 100644 index 31f975cae9..0000000000 --- a/lib/Completion/Bridge/TolerantParser/DebugTolerantCompletor.php +++ /dev/null @@ -1,44 +0,0 @@ -innerCompletor->complete($node, $source, $offset); - foreach ($generator as $result) { - yield $result->withShortDescription( - sprintf( - '[c: %s,n:%s<%s<%s] %s', - ClassName::fromString(get_class($this->innerCompletor))->short(), - ClassName::fromString(get_class($node))->short(), - $node->parent ? ClassName::fromString(get_class($node->parent))->short() : '-', - $node->parent->parent ? ClassName::fromString(get_class($node->parent->parent))->short() : '-', - $result->shortDescription(), - ) - ); - } - - return $generator->getReturn(); - } - - public function qualifier(): TolerantQualifier - { - if ($this->innerCompletor instanceof TolerantQualifiable) { - return $this->innerCompletor->qualifier(); - } - - return new AlwaysQualfifier(); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/Helper/NodeQuery.php b/lib/Completion/Bridge/TolerantParser/Helper/NodeQuery.php deleted file mode 100644 index 903060c37f..0000000000 --- a/lib/Completion/Bridge/TolerantParser/Helper/NodeQuery.php +++ /dev/null @@ -1,70 +0,0 @@ - $className - * @param list $validDescendants - * @return C|null - */ - public static function firstAncestorVia(Node $node, string $className, array $validDescendants): ?Node - { - $ancestor = $node; - while ($ancestor = $ancestor->parent) { - if ($ancestor instanceof $className) { - return $ancestor; - } - - if (!in_array(get_class($ancestor), $validDescendants)) { - break; - } - } - - return null; - } - - /** - * @template C of Node - * @param list> $classNames - * @param list $validDescendants - * @return ?C - */ - public static function firstAncestorInVia(Node $node, array $classNames, array $validDescendants): ?Node - { - $ancestor = $node; - - while ($ancestor = $ancestor->parent) { - if (in_array(get_class($ancestor), $classNames)) { - /** @phpstan-ignore-next-line */ - return $ancestor; - } - - if (!in_array(get_class($ancestor), $validDescendants)) { - break; - } - } - - return null; - } - - /** - * @template C of Node - * @param list> $classNames - * @param list $validDescendants - * @return ?C - */ - public static function firstAncestorOrSelfInVia(Node $node, array $classNames, array $validDescendants): ?Node - { - if (in_array(get_class($node), $classNames)) { - /** @phpstan-ignore-next-line */ - return $node; - } - - return self::firstAncestorInVia($node, $classNames, $validDescendants); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/LimitingCompletor.php b/lib/Completion/Bridge/TolerantParser/LimitingCompletor.php deleted file mode 100644 index 685dc13ab4..0000000000 --- a/lib/Completion/Bridge/TolerantParser/LimitingCompletor.php +++ /dev/null @@ -1,51 +0,0 @@ -completor; - $count = 0; - $suggestions = $completor->complete($node, $source, $offset); - foreach ($suggestions as $suggestion) { - yield $suggestion; - - if (++$count === $this->limit) { - return false; - } - } - - return $suggestions->getReturn(); - } - - public function qualifier(): TolerantQualifier - { - if (!$this->completor instanceof TolerantQualifiable) { - return new AlwaysQualfifier(); - } - - return $this->completor->qualifier(); - } - - public function decorates(): object - { - return $this->completor; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/NodeAtCursorProvider.php b/lib/Completion/Bridge/TolerantParser/NodeAtCursorProvider.php deleted file mode 100644 index 48224444ad..0000000000 --- a/lib/Completion/Bridge/TolerantParser/NodeAtCursorProvider.php +++ /dev/null @@ -1,58 +0,0 @@ -provider->get($document)); - assert($node instanceof Node); - $truncatedSourceCode = substr($document->__toString(), 0, $byteOffset->toInt()); - - $lastNonWhiteSpaceOffset = OffsetHelper::lastNonWhitespaceByteOffset($truncatedSourceCode); - - if ($byteOffset->toInt() > $lastNonWhiteSpaceOffset) { - $byteOffset = ByteOffset::fromInt($lastNonWhiteSpaceOffset); - } - - $node = $node->getDescendantNodeAtPosition($byteOffset->toInt()); - - $truncated = false; - - foreach ($node->getDescendantTokens() as $token) { - - // if the token finishes before the offset, then ignore it - if ($token->getEndPosition() <= $byteOffset->toInt()) { - continue; - } - - // otherwise the token is the one that _contains_ the byte offset - if (false === $truncated) { - - // truncate it up until the offset - $token->length = $byteOffset->toInt() - $token->getFullStartPosition(); - $token->start = $token->fullStart; - $truncated = true; - continue; - } - - // for all other tokens in the node, just truncate them - $token->length = 0; - } - - return $node; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/Qualifier/AlwaysQualfifier.php b/lib/Completion/Bridge/TolerantParser/Qualifier/AlwaysQualfifier.php deleted file mode 100644 index 10e3424fdf..0000000000 --- a/lib/Completion/Bridge/TolerantParser/Qualifier/AlwaysQualfifier.php +++ /dev/null @@ -1,14 +0,0 @@ -isMemberNode($node)) { - return $node; - } - - if ($this->isMemberNode($node->parent)) { - return $node->parent; - } - - return null; - } - - private function isMemberNode(?Node $node): bool - { - if (null === $node) { - return false; - } - - return - $node instanceof MemberAccessExpression || - $node instanceof ScopedPropertyAccessExpression; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/Qualifier/ClassQualifier.php b/lib/Completion/Bridge/TolerantParser/Qualifier/ClassQualifier.php deleted file mode 100644 index b0e4c7feaf..0000000000 --- a/lib/Completion/Bridge/TolerantParser/Qualifier/ClassQualifier.php +++ /dev/null @@ -1,50 +0,0 @@ -getText()) < $this->minimumLength) { - return null; - } - - if ($node instanceof QualifiedName) { - return $node; - } - - if ($node instanceof ObjectCreationExpression) { - return $node; - } - - if ($node instanceof NamespaceUseClause) { - return $node; - } - - if ($node instanceof NamespaceUseDeclaration) { - return $node; - } - - if ($node instanceof ClassBaseClause) { - return $node; - } - - return null; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/Qualifier/DocblockQualifier.php b/lib/Completion/Bridge/TolerantParser/Qualifier/DocblockQualifier.php deleted file mode 100644 index fd9f617363..0000000000 --- a/lib/Completion/Bridge/TolerantParser/Qualifier/DocblockQualifier.php +++ /dev/null @@ -1,20 +0,0 @@ -getLeadingCommentAndWhitespaceText(); - - if (!preg_match('{@[a-z-]+}', $docblock)) { - return null; - } - - return $node; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/AttributeCompletor.php b/lib/Completion/Bridge/TolerantParser/ReferenceFinder/AttributeCompletor.php deleted file mode 100644 index 8c3d9890f7..0000000000 --- a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/AttributeCompletor.php +++ /dev/null @@ -1,90 +0,0 @@ -__toString(); - if ($node instanceof QualifiedName && NameUtil::isQualified($name)) { - $name = NameUtil::toFullyQualified((string)$node->getResolvedName()); - } - - /** @var ClassDeclaration|ClassConstDeclaration|MethodDeclaration|FunctionDeclaration|PropertyDeclaration|Parameter|null $targetNode */ - $targetNode = $node->getFirstAncestor( - ClassDeclaration::class, - FunctionDeclaration::class, - MethodDeclaration::class, - PropertyDeclaration::class, - ClassConstDeclaration::class, - Parameter::class, - ); - - if (null === $targetNode) { - return true; - } - - yield from $this->completeName($name, $source->uri(), $node, $this->matchTargetNode($targetNode)); - - return true; - } - - /** - * @return NameSearcherType::ATTRIBUTE_TARGET_* - */ - private function matchTargetNode( - ClassDeclaration - |ClassConstDeclaration - |MethodDeclaration - |FunctionDeclaration - |PropertyDeclaration - |Parameter $targetNode, - ): string { - if ($targetNode instanceof Parameter) { - foreach ($targetNode->getChildTokens() as $token) { - if ( - in_array($token->kind, [ - TokenKind::PublicKeyword, - TokenKind::ProtectedKeyword, - TokenKind::PrivateKeyword, - ], true) - ) { - return NameSearcherType::ATTRIBUTE_TARGET_PROMOTED_PROPERTY; - } - } - - return NameSearcherType::ATTRIBUTE_TARGET_PARAMETER; - } - - return match ($targetNode::class) { - ClassDeclaration::class => NameSearcherType::ATTRIBUTE_TARGET_CLASS, - FunctionDeclaration::class => NameSearcherType::ATTRIBUTE_TARGET_FUNCTION, - MethodDeclaration::class => NameSearcherType::ATTRIBUTE_TARGET_METHOD, - PropertyDeclaration::class => NameSearcherType::ATTRIBUTE_TARGET_PROPERTY, - ClassConstDeclaration::class => NameSearcherType::ATTRIBUTE_TARGET_CLASS_CONSTANT, - }; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/ClassLikeCompletor.php b/lib/Completion/Bridge/TolerantParser/ReferenceFinder/ClassLikeCompletor.php deleted file mode 100644 index 62f654eeaa..0000000000 --- a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/ClassLikeCompletor.php +++ /dev/null @@ -1,74 +0,0 @@ -getText(); - $type = $this->resolveType($node); - - foreach ($this->nameSearcher->search($search, $type) as $result) { - if (!$result->type()->isClass()) { - continue; - } - - yield Suggestion::createWithOptions($result->name()->head(), [ - 'type' => Suggestion::TYPE_CLASS, - 'priority' => $this->prioritizer->priority($result->uri(), $source->uri()), - 'short_description' => sprintf('%s %s', $type ?: '', $result->name()->__toString()), - 'class_import' => $result->name()->__toString(), - 'name_import' => $result->name()->__toString(), - ]); - } - - return true; - } - - /** - * @return NameSearcherType::INTERFACE|NameSearcherType::CLASS_|NameSearcherType::TRAIT - */ - private function resolveType(Node $node): ?string - { - if (CompletionContext::nodeOrParentIs($node->parent, InterfaceBaseClause::class)) { - return NameSearcherType::INTERFACE; - } - if (CompletionContext::nodeOrParentIs($node->parent, ClassInterfaceClause::class)) { - return NameSearcherType::INTERFACE; - } - if (CompletionContext::nodeOrParentIs($node->parent, ClassBaseClause::class)) { - return NameSearcherType::CLASS_; - } - if (CompletionContext::nodeOrParentIs($node->parent, TraitUseClause::class)) { - return NameSearcherType::TRAIT; - } - return null; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/ExpressionNameCompletor.php b/lib/Completion/Bridge/TolerantParser/ReferenceFinder/ExpressionNameCompletor.php deleted file mode 100644 index 9ccfc0dfa5..0000000000 --- a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/ExpressionNameCompletor.php +++ /dev/null @@ -1,111 +0,0 @@ -parent; - - if (!CompletionContext::expression($node)) { - return true; - } - - if ($node instanceof ScopedPropertyAccessExpression) { - return true; - } - - $name = $this->resolveName($node); - - $suggestions = $this->completeName($name, $source->uri(), $node); - - yield from $suggestions; - - return $suggestions->getReturn(); - } - - protected function createSuggestionOptions( - NameSearchResult $result, - ?TextDocumentUri $sourceUri = null, - ?Node $node = null, - bool $wasAbsolute = false - ): array { - $suggestionOptions = parent::createSuggestionOptions($result, $sourceUri, $node, $wasAbsolute); - - if ($this->isNonObjectCreationClassResult($result, $node) || - !$this->snippetFormatter->canFormat($result)) { - return $suggestionOptions; - } - - return array_merge( - $suggestionOptions, - [ - 'snippet' => $this->snippetFormatter->format($result) - ] - ); - } - - private function isNonObjectCreationClassResult(NameSearchResult $result, ?Node $node): bool - { - if (!$result->type()->isClass()) { - return false; - } - - if ($node === null) { - return true; - } - - $parent = $node->getParent(); - - if ($parent === null) { - return true; - } - - return !($parent instanceof ObjectCreationExpression); - } - - private function resolveName(Node $node): string - { - if ($node instanceof ScopedPropertyAccessExpression) { - $token = $node->memberName; - return (string)$token->getText($node->getFileContents()); - } - $name = $node instanceof QualifiedName ? $node->__toString() : ''; - if ($node instanceof QualifiedName && NameUtil::isQualified($name)) { - $name = NameUtil::toFullyQualified((string)$node->getResolvedName()); - } - return $name ?: ''; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/TypeCompletor.php b/lib/Completion/Bridge/TolerantParser/ReferenceFinder/TypeCompletor.php deleted file mode 100644 index a75a6fb888..0000000000 --- a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/TypeCompletor.php +++ /dev/null @@ -1,27 +0,0 @@ -provider->provide($node, $node->getText()); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/UseNameCompletor.php b/lib/Completion/Bridge/TolerantParser/ReferenceFinder/UseNameCompletor.php deleted file mode 100644 index 8aa258e8d7..0000000000 --- a/lib/Completion/Bridge/TolerantParser/ReferenceFinder/UseNameCompletor.php +++ /dev/null @@ -1,30 +0,0 @@ -parent; - - if (!CompletionContext::useImport($node)) { - return true; - } - - $search = $node->getText(); - $search = NameUtil::toFullyQualified($search); - yield from $this->completeName($search, $source->uri(), $node); - - return true; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletor.php b/lib/Completion/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletor.php deleted file mode 100644 index bcf7398bbf..0000000000 --- a/lib/Completion/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletor.php +++ /dev/null @@ -1,120 +0,0 @@ -qualifier; - } - - public function complete(Node $node, TextDocument $source, ByteOffset $offset): Generator - { - $files = $this->filesystem->fileList()->phpFiles(); - - if ($node instanceof QualifiedName) { - $files = $files->filter(function (SplFileInfo $file) use ($node) { - return str_starts_with($file->getFilename(), $node->getText()); - }); - } - - $count = 0; - $currentNamespace = $this->getCurrentNamespace($node); - $imports = $node->getImportTablesForCurrentScope(); - - /** @var ScfFilePath $file */ - foreach ($files as $file) { - $candidates = $this->fileToClass->fileToClassCandidates(FilePath::fromString($file->path())); - - if ($candidates->noneFound()) { - continue; - } - - foreach ($candidates as $candidate) { - /** @var ClassName $candidate */ - yield Suggestion::createWithOptions( - $candidate->name(), - [ - 'type' => Suggestion::TYPE_CLASS, - 'short_description' => $candidate->__toString(), - 'class_import' => $this->getClassNameForImport($candidate, $imports, $currentNamespace), - 'range' => $this->getRange($node, $offset), - ] - ); - } - } - - return true; - } - /** - * @param array $imports - */ - private function getClassNameForImport(ClassName $candidate, array $imports, ?string $currentNamespace = null): ?string - { - $candidateNamespace = $candidate->namespace(); - - if ((string) $currentNamespace === (string) $candidateNamespace) { - return null; - } - - foreach ($imports[0] as $resolvedName) { - if ($candidate->__toString() === $resolvedName->getFullyQualifiedNameText()) { - return null; - } - } - - return $candidate->__toString(); - } - - - private function getCurrentNamespace(Node $node): ?string - { - $currentNamespaceDefinition = $node->getNamespaceDefinition(); - - if (!$currentNamespaceDefinition) { - return null; - } - - if (!$currentNamespaceDefinition->name instanceof QualifiedName) { - return null; - } - - return $currentNamespaceDefinition->name->getText(); - } - - private function getRange(Node $node, ByteOffset $offset): Range - { - if ($node instanceof QualifiedName) { - return Range::fromStartAndEnd($node->getStartPosition(), $node->getEndPosition()); - } - - return new Range($offset, $offset); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/TolerantArrayCompletor.php b/lib/Completion/Bridge/TolerantParser/TolerantArrayCompletor.php deleted file mode 100644 index ca6d82d350..0000000000 --- a/lib/Completion/Bridge/TolerantParser/TolerantArrayCompletor.php +++ /dev/null @@ -1,26 +0,0 @@ -suggestions; - - return true; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/TolerantCompletor.php b/lib/Completion/Bridge/TolerantParser/TolerantCompletor.php deleted file mode 100644 index 9f2e651f7c..0000000000 --- a/lib/Completion/Bridge/TolerantParser/TolerantCompletor.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ - public function complete(Node $node, TextDocument $source, ByteOffset $offset): Generator; -} diff --git a/lib/Completion/Bridge/TolerantParser/TolerantQualifiable.php b/lib/Completion/Bridge/TolerantParser/TolerantQualifiable.php deleted file mode 100644 index fe0a062f83..0000000000 --- a/lib/Completion/Bridge/TolerantParser/TolerantQualifiable.php +++ /dev/null @@ -1,8 +0,0 @@ - - */ - public function provide(Node $node, string $search): Generator - { - $search = $this->resolveSingleType($search); - yield from $this->builtInTypes(); - yield from $this->nameImports($node); - yield from $this->nameResults($search); - } - - /** - * @return Generator - */ - private function nameResults(string $search): Generator - { - if (!$search) { - return; - } - - foreach ($this->nameSearcher->search($search) as $result) { - if (!$result->type()->isClass()) { - continue; - } - - $wasAbsolute = str_starts_with($search, '\\'); - yield Suggestion::createWithOptions($result->name()->head(), [ - 'short_description' => $result->name()->__toString(), - 'name_import' => $wasAbsolute ? null : $result->name()->__toString(), - 'type' => Suggestion::TYPE_CLASS, - 'priority' => Suggestion::PRIORITY_MEDIUM, - ]); - } - } - - /** - * @return Generator - */ - private function nameImports(Node $node): Generator - { - $namespaceImports = $node->getImportTablesForCurrentScope()[0]; - - foreach ($namespaceImports as $alias => $resolvedName) { - yield Suggestion::createWithOptions( - $alias, - [ - 'type' => Suggestion::TYPE_CLASS, - 'short_description' => sprintf('%s', $resolvedName->__toString()), - 'priority' => Suggestion::PRIORITY_HIGH, - ] - ); - } - } - - /** - * @return Generator - */ - private function builtInTypes(): Generator - { - foreach (self::BUILT_IN_TYPES as $type) { - yield Suggestion::createWithOptions( - $type, - [ - 'type' => Suggestion::TYPE_KEYWORD, - 'priority' => Suggestion::PRIORITY_HIGH, - ] - ); - } - } - private function resolveSingleType(string $search): string - { - $split = preg_split('{[|&<>]}', $search); - if (!$split) { - return ''; - } - return $split[array_key_last($split)]; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/AbstractParameterCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/AbstractParameterCompletor.php deleted file mode 100644 index 6e6790377d..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/AbstractParameterCompletor.php +++ /dev/null @@ -1,173 +0,0 @@ -variableCompletionHelper = $variableCompletionHelper ?? new VariableCompletionHelper($reflector); - } - - /** - * @param WorseVariable[] $variables - * - * @return Generator - */ - protected function populateResponse(Node $callableExpression, ReflectionFunctionLike $functionLikeReflection, array $variables): Generator - { - // function has no parameters, return empty handed - if ($functionLikeReflection->parameters()->count() === 0) { - return true; - } - - $paramIndex = $this->paramIndex($callableExpression); - - if ($this->numberOfArgumentsExceedParameterArity($functionLikeReflection, $paramIndex)) { - return true; - } - - $parameter = $this->reflectedParameter($functionLikeReflection, $paramIndex); - - foreach ($variables as $variable) { - if ( - $variable->type()->isDefined() && - false === $this->isVariableValidForParameter($variable, $parameter) - ) { - // parameter has no types and is not valid for this position, ignore it - continue; - } - - yield Suggestion::createWithOptions( - '$' . $variable->name(), - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'priority' => Suggestion::PRIORITY_HIGH, - 'short_description' => sprintf( - '%s => param #%d %s', - $this->formatter->format($variable->type()), - $paramIndex, - $this->formatter->format($parameter) - ) - ] - ); - } - - return true; - } - - private function paramIndex(Node $node): int - { - $argumentList = $this->argumentListFromNode($node); - - if (null === $argumentList) { - return 1; - } - - $index = 0; - /** @var ArgumentExpression $element */ - foreach ($argumentList->getElements() as $element) { - $index++; - if (!$element->expression instanceof Variable) { - continue; - } - - $name = $element->expression->getName(); - - if ($name instanceof MissingToken) { - continue; - } - } - - // if we have a trailing comma, e.g. the argument list is `$foobar, ` - // then the above elements will contain only `$foobar` but the param - // index should be incremented. - if (str_ends_with(trim($argumentList->getText()), ',')) { - return $index + 1; - } - - return $index; - } - - private function isVariableValidForParameter(WorseVariable $variable, ReflectionParameter $parameter): bool - { - if (false === ($parameter->inferredType()->isDefined())) { - return true; - } - - foreach ($variable->type()->expandTypes() as $variableType) { - if ($parameter->inferredType()->accepts($variableType)->isTrue()) { - return true; - } - } - return false; - } - - private function reflectedParameter(ReflectionFunctionLike $reflectionFunctionLike, int $paramIndex): ReflectionParameter - { - $reflectedIndex = 1; - /** @var ReflectionParameter $parameter */ - foreach ($reflectionFunctionLike->parameters() as $parameter) { - if ($reflectedIndex == $paramIndex) { - return $parameter; - } - $reflectedIndex++; - } - - throw new LogicException(sprintf('Could not find parameter for index "%s"', $paramIndex)); - } - - private function numberOfArgumentsExceedParameterArity(ReflectionFunctionLike $reflectionFunctionLike, int $paramIndex): bool - { - return $reflectionFunctionLike->parameters()->count() < $paramIndex; - } - - /** - * @return ArgumentExpressionList|null - */ - private function argumentListFromNode(Node $node) - { - if ($node instanceof ObjectCreationExpression) { - return $node->argumentExpressionList; - } - - if ($node instanceof QualifiedName) { - $callExpression = $node->parent; - assert($callExpression instanceof CallExpression); - return $callExpression->argumentExpressionList; - } - - assert($node instanceof MemberAccessExpression || $node instanceof ScopedPropertyAccessExpression); - assert(null !== $node->parent); - - $list = $node->parent->getFirstDescendantNode(ArgumentExpressionList::class); - assert($list instanceof ArgumentExpressionList || is_null($list)); - - return $list; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/DocblockCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/DocblockCompletor.php deleted file mode 100644 index a775558a5a..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/DocblockCompletor.php +++ /dev/null @@ -1,147 +0,0 @@ - - */ - public function complete(Node $node, TextDocument $source, ByteOffset $byteOffset): Generator - { - $node = $this->provider->get($source); - $node = NodeUtil::firstDescendantNodeAfterOffset($node, $byteOffset->toInt()); - - [$tag, $type, $var] = $this->extractTag($source, $byteOffset); - - if (null === $tag) { - return false; - } - - $tag = '@' . $tag; - - if ($var) { - yield from $this->varCompletion($node, $byteOffset, $tag, $var); - return; - } - - if (in_array($tag, self::SUPPORTED_TAGS)) { - yield from $this->completeType($node, $tag, $type); - return false; - } - - foreach (self::SUPPORTED_TAGS as $supportedTag) { - if (str_starts_with($supportedTag, $tag)) { - yield Suggestion::createWithOptions( - $supportedTag, - [ - 'type' => Suggestion::TYPE_KEYWORD, - ] - ); - } - } - return true; - } - - /** - * @return array{string|null,string,string|null} - */ - private function extractTag(TextDocument $source, ByteOffset $byteOffset): array - { - $source = substr($source->__toString(), 0, $byteOffset->toInt()); - $line = LineAtOffset::lineAtByteOffset($source, $byteOffset); - - if (!preg_match('{/?\*{1,2}\s*@([a-z-]*)\s*([^\s]*)\s*(\$[^\s]*)?}', $line, $matches)) { - return [null, '', '']; - } - - return [$matches[1], $matches[2], $matches[3] ?? null]; - } - - /** - * @return Generator - */ - private function completeType(Node $node, string $tag, string $search): Generator - { - yield from $this->typeSuggestionProvider->provide($node, $search); - } - - /** - * @return Generator - */ - private function varCompletion(Node $node, ByteOffset $offset, string $tag, string $var): Generator - { - if (!in_array($tag, self::TAGS_WITH_VAR)) { - return; - } - - if (!$node instanceof FunctionDeclaration && !$node instanceof MethodDeclaration) { - return; - } - - /** @phpstan-ignore-next-line */ - if (!$node->parameters) { - return; - } - - foreach ($node->parameters->getElements() as $parameter) { - if (!$parameter instanceof Parameter) { - continue; - } - yield Suggestion::createWithOptions( - '$' . $parameter->getName(), - [ - 'type' => Suggestion::TYPE_VARIABLE, - ] - ); - } - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/DoctrineAnnotationCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/DoctrineAnnotationCompletor.php deleted file mode 100644 index 3cc51ca730..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/DoctrineAnnotationCompletor.php +++ /dev/null @@ -1,145 +0,0 @@ -parser->get($source); - - $truncatedSource = $this->truncateSource((string) $source, $byteOffset->toInt()); - - $node = $this->findNodeForPhpdocAtPosition( - $sourceNodeFile, - // the parser requires the byte offset, not the char offset - strlen($truncatedSource) - ); - - if (!$node) { - // Ignore this case is the cursor is not in a phpdoc block - return true; - } - - if (!$annotation = $this->extractAnnotation($truncatedSource)) { - // Ignore if not an annotation - return true; - } - - $namespace = NodeUtil::namespace($node); - if (NameUtil::isQualified($annotation) && $namespace) { - $annotation = '\\' . NameUtil::join($namespace, $annotation); - } - - $suggestions = $this->completeName($annotation, $source->uri()); - - foreach ($suggestions as $suggestion) { - if (!$this->isAnAnnotation($suggestion)) { - continue; - } - - yield $suggestion; - } - - return $suggestions->getReturn(); - } - - protected function createSuggestionOptions( - NameSearchResult $result, - ?TextDocumentUri $sourceUri = null, - ?Node $node = null, - bool $wasAbsolute = false - ): array { - return array_merge(parent::createSuggestionOptions($result, null, $node, $wasAbsolute), [ - 'snippet' => (string) $result->name()->head() .'($1)$0', - ]); - } - - private function truncateSource(string $source, int $byteOffset): string - { - // truncate source at byte offset - we don't want the rest of the source - // file contaminating the completion (for example `$foo($<>\n $bar = - // ` will evaluate the Variable node as an expression node with a - // double variable `$\n $bar = ` - $truncatedSource = substr($source, 0, $byteOffset); - - // determine the last non-whitespace _character_ offset - $characterOffset = OffsetHelper::lastNonWhitespaceCharacterOffset($truncatedSource); - - // truncate the source at the character offset - $truncatedSource = mb_substr($source, 0, $characterOffset); - - return $truncatedSource; - } - - private function findNodeForPhpdocAtPosition(SourceFileNode $sourceNodeFile, int $position): ?Node - { - /** @var Node $node */ - foreach ($sourceNodeFile->getDescendantNodes() as $node) { - if ( - $node->getFullStartPosition() < $position - && $position < $node->getStartPosition() - ) { - // If the text is a phpdoc block return the node - return $node->getDocCommentText() ? $node : null; - } - } - - return null; - } - - private function isAnAnnotation(Suggestion $suggestion): bool - { - if (null === $suggestion->nameImport()) { - return false; - } - - try { - $reflectionClass = $this->reflector->reflectClass($suggestion->nameImport()); - $docblock = $reflectionClass->docblock(); - - return str_contains($docblock->raw(), '@Annotation'); - } catch (NotFound) { - return false; - } - } - - private function extractAnnotation(string $truncatedSource): ?string - { - $count = 0; - $annotation = preg_replace('/.*@([^\\@\s\t*]+)$/s', '$1', $truncatedSource, 1, $count); - - if (0 === $count) { - return null; - } - - return $annotation; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/Helper/VariableCompletionHelper.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/Helper/VariableCompletionHelper.php deleted file mode 100644 index ab6ddd07ce..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/Helper/VariableCompletionHelper.php +++ /dev/null @@ -1,108 +0,0 @@ -getText(); - } - - $offset = $this->offsetToReflect($node, $offset->toInt()); - - try { - $reflectionOffset = $this->reflector->reflectOffset($source, $offset); - } catch (NotFound) { - return []; - } - - $frame = $reflectionOffset->frame(); - - if (CompletionContext::anonymousUse($node)) { - $frame = $frame->parent(); - } - - if (null === $frame) { - return []; - } - - // Get all declared variables up until the start of the current - // expression. The most recently declared variables should be first - // (which is why we reverse the array). - $reversedLocals = $this->orderedVariablesUntilOffset($frame, $node->getStartPosition()); - - // Ignore variables that have already been suggested. - $seen = []; - $variables = []; - - /** @var Variable $local */ - foreach ($reversedLocals as $local) { - if (isset($seen[$local->name()])) { - continue; - } - - - $name = ltrim($partialMatch, '$'); - $matchPos = -1; - - if ($name) { - $matchPos = mb_strpos($local->name(), $name); - } - - // if there is a partial match and the variable does not start with - // it, skip the variable. - if ($partialMatch && ('$' !== $partialMatch && 0 !== $matchPos)) { - continue; - } - - $seen[$local->name()] = true; - $variables[] = $local; - } - - return $variables; - } - - private function offsetToReflect(Node $node, int $offset): int - { - $parentNode = $node->parent; - - // If the parent is an assignment expression, then only parse - // until the start of the expression, not the start of the variable - // under completion: - // - // $left = $lef<> - // - // Otherwise $left will be evaluated to . - if ($parentNode instanceof AssignmentExpression) { - $offset = $parentNode->getFullStartPosition(); - } - - return $offset; - } - - private function orderedVariablesUntilOffset(Frame $frame, int $offset): array - { - return array_reverse(iterator_to_array($frame->locals()->lessThan($offset))); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/ImportedNameCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/ImportedNameCompletor.php deleted file mode 100644 index 31835f4681..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/ImportedNameCompletor.php +++ /dev/null @@ -1,58 +0,0 @@ -getImportTablesForCurrentScope()[0]; - - /** @var ResolvedName $resolvedName */ - foreach ($namespaceImports as $alias => $resolvedName) { - yield Suggestion::createWithOptions( - $alias, - [ - 'type' => Suggestion::TYPE_CLASS, - 'short_description' => sprintf('%s', $resolvedName->__toString()), - 'fqn' => $resolvedName->__toString(), - ] - ); - } - - return true; - } - - public function qualifier(): TolerantQualifier - { - return $this->qualifier; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/KeywordCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/KeywordCompletor.php deleted file mode 100644 index e4c4a9676e..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/KeywordCompletor.php +++ /dev/null @@ -1,113 +0,0 @@ - "(\$1)\n{\$0\n}", - '__call' => "(string \\\$\${1:name}, array \\\$\${2:arguments}): \${3:mixed}\n{\$0\n}", - '__callStatic' => "(string \\\$\${1:name}, array \\\$\${2:arguments}): \${3:mixed}\n{\$0\n}", - '__clone' => "(): void\n{\$0\n}", - '__debugInfo' => "(): array\n{\$0\n}", - '__destruct' => "(): void\n{\$0\n}", - '__get' => "(string \\\$\${1:name}): \${3:mixed}\n{\$0\n}", - '__invoke' => "(\$1): \${2:mixed}\n{\$0\n}", - '__isset' => "(string \\\$\${1:name}): bool\n{\$0\n}", - '__serialize' => "(): array\n{\$0\n}", - '__set' => "(string \\\$\${1:name}, mixed \\\$\${2:value}): void\n{\$0\n}", - '__set_state' => "(array \\\$\${1:properties}): object\n{\$0\n}", - '__sleep' => "(): array\n{\$0\n}", - '__toString' => "(): string\n{\$0\n}", - '__unserialize' => "(array \\\$\${1:data}): void\n{\$0\n}", - '__unset' => "(string \\\$\${1:name}): void\n{\$0\n}", - '__wakeup' => "(): void\n{\$0\n}", - ]; - - public function complete(Node $node, TextDocument $source, ByteOffset $offset): Generator - { - if (CompletionContext::promotedPropertyVisibility($node)) { - yield from $this->keywords(['private ', 'public ', 'protected ', ]); - return true; - } - if (CompletionContext::classClause($node, $offset)) { - yield from $this->keywords(['implements ', 'extends ']); - return true; - } - if (CompletionContext::declaration($node, $offset)) { - yield from $this->keywords(['class ', 'enum ', 'trait ', 'function ', 'interface ']); - return true; - } - - if (CompletionContext::conditionInfix($node)) { - yield from $this->keywords(['instanceof ']); - return true; - } - - if (CompletionContext::methodName($node)) { - yield from $this->methods(); - return true; - } - - if (CompletionContext::attribute($node)) { - return true; - } - - if (!$node instanceof MethodDeclaration && CompletionContext::classMembersBody($node->parent)) { - yield from $this->keywords([ - 'function ', - 'const ', - ]); - return true; - } - - if (CompletionContext::classMembersBody($node)) { - yield from $this->keywords(['private ', 'protected ', 'public ']); - return true; - } - - return true; - } - - /** - * @return Generator - */ - private function methods(): Generator - { - foreach (self::MAGIC_METHODS as $name => $snippet) { - yield Suggestion::createWithOptions($name . '(', [ - 'type' => Suggestion::TYPE_METHOD, - 'priority' => match ($name) { - '__construct' => -255, - default => 1, - }, - 'snippet' => $name . $snippet, - ]); - } - } - - /** - * @return Generator - * @param string[] $keywords - */ - private function keywords(array $keywords): Generator - { - foreach ($keywords as $keyword) { - yield Suggestion::createWithOptions($keyword, [ - 'type' => Suggestion::TYPE_KEYWORD, - 'priority' => 1, - ]); - } - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletor.php deleted file mode 100644 index 3576d0dd64..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletor.php +++ /dev/null @@ -1,226 +0,0 @@ -arrowToken->getFullStartPosition(); - } - - if ($node instanceof ScopedPropertyAccessExpression) { - $memberStartOffset = $node->doubleColon->getFullStartPosition(); - $isInstance = false; - } - - assert($node instanceof MemberAccessExpression || $node instanceof ScopedPropertyAccessExpression); - - $memberName = $node->memberName; - - if ($memberName instanceof Variable) { - $memberName = $memberName->name; - } - - if (!$memberName instanceof Token) { - return true; - } - - $shouldCompleteOnlyName = strlen($source) > $offset->toInt() && substr($source, $offset->toInt(), 1) == '('; - - $partialMatch = (string) $memberName->getText($node->getFileContents()); - - $reflectionOffset = $this->reflector->reflectOffset($source, $memberStartOffset); - - $nodeContext = $reflectionOffset->nodeContext(); - $type = $nodeContext->type(); - $static = $node instanceof ScopedPropertyAccessExpression; - - foreach ($type->expandTypes()->classLike() as $type) { - foreach ($this->populateSuggestions($nodeContext, $type, $static, $shouldCompleteOnlyName, $isInstance) as $suggestion) { - if ($partialMatch && !str_starts_with($suggestion->name(), $partialMatch)) { - continue; - } - - yield $suggestion; - } - } - - return true; - } - - /** - * @return Generator - */ - private function populateSuggestions(NodeContext $nodeContext, Type $type, bool $static, bool $completeOnlyName, bool $isInstance): Generator - { - if (false === ($type->isDefined())) { - return; - } - - $isParent = $nodeContext->symbol()->name() === 'parent'; - $publicOnly = !in_array($nodeContext->symbol()->name(), ['this', 'self'], true); - - - $type = $type->expandTypes()->classLike()->firstOrNull(); - - if (!$type) { - return; - } - - if (!$type instanceof ClassLikeType) { - return; - } - - $members = $type->members(); - - if (!$isParent && $static) { - yield Suggestion::createWithOptions('class', [ - 'type' => Suggestion::TYPE_CONSTANT, - 'short_description' => $type->name(), - 'priority' => Suggestion::PRIORITY_HIGH, - ]); - } - - try { - $classReflection = $this->reflector->reflectClassLike($type->name()); - } catch (NotFound) { - return; - } - - foreach ($members->methods() as $method) { - if (false === $isParent && $method->name() === '__construct') { - continue; - } - if ($publicOnly && false === $method->visibility()->isPublic()) { - continue; - } - - if (!$isParent && $static && false === $method->isStatic()) { - continue; - } - - $snippet = null; - if ($this->snippetFormatter->canFormat($method)) { - $snippet = $completeOnlyName ? $method->name() : $this->snippetFormatter->format($method); - } - - yield Suggestion::createWithOptions($method->name(), [ - 'type' => Suggestion::TYPE_METHOD, - 'short_description' => fn () => $this->formatter->format($method), - 'documentation' => function () use ($method) { - return $this->objectRenderer->render(new ItemDocumentation(sprintf( - '%s::%s', - $method->class()->name(), - $method->name() - ), $method->docblock()->formatted(), $method)); - }, - 'snippet' => $snippet, - ]); - } - - if ($classReflection instanceof ReflectionClass) { - /** @var ReflectionProperty $property */ - foreach ($members->properties() as $property) { - if ($publicOnly && false === $property->visibility()->isPublic()) { - continue; - } - - if ($static && false === $property->isStatic()) { - continue; - } - - $name = $property->name(); - if ($static) { - $name = '$' . $name; - } - - yield Suggestion::createWithOptions($name, [ - 'type' => Suggestion::TYPE_PROPERTY, - 'short_description' => fn () => $this->formatter->format($property), - 'documentation' => function () use ($property) { - return $this->objectRenderer->render(new ItemDocumentation(sprintf( - '%s::%s', - $property->class()->name(), - $property->name() - ), $property->docblock()->formatted(), $property)); - }, - ]); - } - } - - if (false === $isInstance && $classReflection instanceof ReflectionClass || - $classReflection instanceof ReflectionInterface || - $classReflection instanceof ReflectionEnum - ) { - foreach ($members->constants() as $constant) { - if ($publicOnly && false === $constant->visibility()->isPublic()) { - continue; - } - - yield Suggestion::createWithOptions($constant->name(), [ - 'type' => Suggestion::TYPE_CONSTANT, - 'short_description' => fn () => $this->formatter->format($constant), - 'documentation' => fn () => $constant->docblock()->formatted(), - ]); - } - } - - if ($classReflection instanceof ReflectionEnum) { - foreach ($members->enumCases() as $case) { - yield Suggestion::createWithOptions($case->name(), [ - 'type' => Suggestion::TYPE_ENUM, - 'short_description' => fn () => $this->formatter->format($case), - 'documentation' => fn () => $case->docblock()->formatted(), - ]); - } - } - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseConstantCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseConstantCompletor.php deleted file mode 100644 index b4b079b1d6..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseConstantCompletor.php +++ /dev/null @@ -1,44 +0,0 @@ -getText(); - - foreach ($definedConstants as $name => $value) { - $name = Name::fromString((string) $name); - - if (str_starts_with($name->short(), $partial)) { - yield Suggestion::createWithOptions( - $name->short(), - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'short_description' => sprintf('%s = %s', $name->full(), var_export($value, true)) - ] - ); - } - } - - return true; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseConstructorCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseConstructorCompletor.php deleted file mode 100644 index 407e922d24..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseConstructorCompletor.php +++ /dev/null @@ -1,97 +0,0 @@ -parent; - } - - if ($node instanceof ArgumentExpressionList) { - $node = $node->parent; - } - - if (!$node instanceof Variable && !$node instanceof ObjectCreationExpression) { - return true; - } - - $creationExpression = $node instanceof ObjectCreationExpression ? $node : $node->getFirstAncestor(ObjectCreationExpression::class); - - if (!$creationExpression || ($creationExpression instanceof ObjectCreationExpression && null === $creationExpression->openParen)) { - return true; - } - - $variables = $this->variableCompletionHelper->variableCompletions($node, $source, $offset); - - // no variables available for completion, return empty handed - if ($variables === []) { - return true; - } - - assert($creationExpression instanceof ObjectCreationExpression); - - try { - $reflectionClass = $this->reflectClass($source, $creationExpression); - } catch (NotFound) { - return true; - } - - if (null === $reflectionClass) { - return true; - } - - if (false === $reflectionClass->methods()->has('__construct')) { - return true; - } - - $reflectionConstruct = $reflectionClass->methods()->get('__construct'); - - // function has no parameters, return empty handed - if ($reflectionConstruct->parameters()->count() === 0) { - return true; - } - - $suggestions = $this->populateResponse($creationExpression, $reflectionConstruct, $variables); - yield from $suggestions; - - return $suggestions->getReturn(); - } - - /** - * @return ReflectionClass|null - */ - private function reflectClass(string $source, ObjectCreationExpression $creationExpresion) - { - $typeName = $creationExpresion->classTypeDesignator; - - if (!$typeName instanceof QualifiedName) { - return null; - } - - $resolvedName = $typeName->getResolvedName(); - - if (null === $resolvedName) { - return null; - } - - return $this->reflector->reflectClass((string) $resolvedName); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseDeclaredClassCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseDeclaredClassCompletor.php deleted file mode 100644 index 137975b7fc..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseDeclaredClassCompletor.php +++ /dev/null @@ -1,60 +0,0 @@ -short(), $node->getText()); - }); - - foreach ($classes as $class) { - try { - $reflectionClass = $this->reflector->reflectClass($class); - } catch (NotFound) { - continue; - } - - yield Suggestion::createWithOptions( - $reflectionClass->name()->short(), - [ - 'type' => Suggestion::TYPE_CLASS, - 'short_description' => $this->formatter->format($reflectionClass), - 'documentation' => $reflectionClass->docblock()->formatted() - ] - ); - } - - return true; - } - - public function qualifier(): TolerantQualifier - { - return new ClassQualifier(); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php deleted file mode 100644 index e9a5f92247..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php +++ /dev/null @@ -1,107 +0,0 @@ -parent instanceof MethodDeclaration) { - return true; - } - - if ($node->parent instanceof QualifiedNameList) { - return true; - } - - if ($node->parent instanceof Parameter) { - return true; - } - - $functionNames = $this->reflectedFunctions($source); - $functionNames = $this->definedNamesFor($functionNames, $node->getText()); - $functions = $this->functionReflections($functionNames); - - /** @var ReflectionFunction $functionReflection */ - foreach ($functions as $functionReflection) { - yield Suggestion::createWithOptions( - $functionReflection->name()->short(), - [ - 'type' => Suggestion::TYPE_FUNCTION, - 'short_description' => $this->formatter->format($functionReflection), - 'documentation' => $functionReflection->docblock()->formatted(), - 'snippet' => $this->snippetFormatter->format($functionReflection), - ] - ); - } - - return true; - } - - private function definedNamesFor(array $reflectedFunctions, string $partialName): Generator - { - $functions = get_defined_functions(); - $functions['reflected'] = $reflectedFunctions; - - return $this->filterFunctions($functions, $partialName); - } - - private function reflectedFunctions(TextDocument $source): array - { - $functionNames = []; - foreach ($this->reflector->reflectFunctionsIn($source) as $function) { - $functionNames[] = $function->name()->full(); - } - - return $functionNames; - } - - private function filterFunctions(array $functions, string $partialName): Generator - { - foreach ($functions as $type => $functionNames) { - foreach ($functionNames as $functionName) { - $functionName = Name::fromString($functionName); - if (str_starts_with($functionName->short(), $partialName)) { - yield $functionName; - } - } - } - } - - private function functionReflections(Generator $functionNames): Generator - { - foreach ($functionNames as $functionName) { - try { - yield $this->reflector->reflectFunction($functionName); - } catch (NotFound) { - } - } - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletor.php deleted file mode 100644 index 0847f1b885..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletor.php +++ /dev/null @@ -1,92 +0,0 @@ -couldComplete($node, $source, $offset)) { - return true; - } - - foreach ($this->variableCompletionHelper->variableCompletions($node, $source, $offset) as $local) { - $localType = $local->type(); - if ($localType instanceof ArrayShapeType) { - yield from $this->arrayShapeSuggestions($local->name(), $localType); - } - - yield Suggestion::createWithOptions( - '$' . $local->name(), - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'short_description' => $this->informationFormatter->format($local), - 'documentation' => function () use ($local) { - return $local->type()->__toString(); - }, - ] - ); - } - - return true; - } - - private function couldComplete(?Node $node, TextDocument $source, ByteOffset $offset): bool - { - if (null === $node) { - return false; - } - - $parentNode = $node->parent; - - if ($parentNode instanceof MemberAccessExpression) { - return false; - } - - if ($parentNode instanceof ScopedPropertyAccessExpression) { - return false; - } - - if ($node instanceof TolerantVariable) { - return true; - } - - return false; - } - - /** - * @return Generator - */ - private function arrayShapeSuggestions(string $varName, ArrayShapeType $localType): Generator - { - foreach ($localType->typeMap as $key => $type) { - $key = is_numeric($key) ? $key : '\'' . $key . '\''; - yield 'why'.$key => Suggestion::createWithOptions(sprintf('$%s[%s]', $varName, (string)$key), [ - 'type' => Suggestion::TYPE_FIELD, - 'short_description' => $this->informationFormatter->format($type), - 'documentation' => function () use ($type) { - return $type->__toString(); - }, - ]); - } - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseNamedParameterCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseNamedParameterCompletor.php deleted file mode 100644 index c098761112..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseNamedParameterCompletor.php +++ /dev/null @@ -1,200 +0,0 @@ -fromObjectCreation($subject); - } - - if ($subject instanceof Attribute) { - return yield from $this->fromAttribute($subject); - } - - if ($subject instanceof CallExpression) { - return yield from $this->fromCallExpression($subject); - } - - return true; - } - - /** - * @return Generator - */ - private function fromObjectCreation(ObjectCreationExpression $creation): Generator - { - $type = $creation->classTypeDesignator; - - if (!$type instanceof QualifiedName) { - return true; - } - - try { - $class = $this->reflector->reflectClass((string)$type->getResolvedName()); - } catch (NotFound) { - return true; - } - - yield from $this->fromMethod($class, '__construct'); - - return true; - } - - /** - * @return Generator - */ - private function fromAttribute(Attribute $attribute): Generator - { - /** @var QualifiedName|null $type */ - $type = $attribute->name; - - if (!$type instanceof QualifiedName) { - return true; - } - - try { - $class = $this->reflector->reflectClass((string)$type->getResolvedName()); - } catch (NotFound) { - return true; - } - - yield from $this->fromMethod($class, '__construct'); - - return true; - } - - /** - * @return Generator - */ - private function fromMethod(ReflectionClassLike $class, string $method): Generator - { - if (!$class->methods()->has($method)) { - return true; - } - - foreach ($class->methods()->get($method)->parameters() as $parameter) { - yield Suggestion::createWithOptions( - sprintf('%s: ', $parameter->name()), - [ - 'type' => Suggestion::TYPE_FIELD, - 'priority' => Suggestion::PRIORITY_HIGH, - 'short_description' => $this->formatter->format($parameter), - ] - ); - } - } - - /** - * @return Generator - */ - private function fromFunction(ReflectionFunction $function): Generator - { - foreach ($function->parameters() as $parameter) { - yield Suggestion::createWithOptions( - sprintf('%s: ', $parameter->name()), - [ - 'type' => Suggestion::TYPE_FIELD, - 'priority' => Suggestion::PRIORITY_HIGH, - 'short_description' => $this->formatter->format($parameter), - ] - ); - } - } - - /** - * @return Generator - */ - private function fromCallExpression(CallExpression $creation): Generator - { - /** @var Node */ - $callableExpression = $creation->callableExpression; - if ( - !$callableExpression instanceof MemberAccessExpression && - !$callableExpression instanceof QualifiedName && - !$callableExpression instanceof ScopedPropertyAccessExpression - ) { - return true; - } - - if ($callableExpression instanceof QualifiedName) { - try { - $function = $this->reflector->reflectFunction( - $callableExpression->getNamespacedName()->__toString() - ); - yield from $this->fromFunction($function); - } catch (NotFound) { - return true; - } - - return true; - } - - try { - $classLike = $this->reflector->reflectMethodCall( - NodeToTextDocumentConverter::convert($creation), - $callableExpression->getEndPosition() - ); - yield from $this->fromMethod($classLike->class(), $classLike->name()); - } catch (NotFound) { - return true; - } - - return true; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseParameterCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseParameterCompletor.php deleted file mode 100644 index 128e4ec59f..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseParameterCompletor.php +++ /dev/null @@ -1,97 +0,0 @@ -parent; - } - - if ($node instanceof ArgumentExpressionList) { - $node = $node->parent; - } - - if (!$node instanceof Variable && !$node instanceof CallExpression) { - return true; - } - - $callExpression = $node instanceof CallExpression ? $node : $node->getFirstAncestor(CallExpression::class); - - if (!$callExpression) { - return true; - } - - assert($callExpression instanceof CallExpression); - $callableExpression = $callExpression->callableExpression; - - $variables = $this->variableCompletionHelper->variableCompletions($node, $source, $offset); - - // no variables available for completion, return empty handed - if (empty($variables)) { - return true; - } - - try { - $reflectionFunctionLike = $this->reflectFunctionLike($source, $callableExpression); - } catch (NotFound) { - return true; - } - - if (null === $reflectionFunctionLike) { - return true; - } - - $suggestions = $this->populateResponse($callableExpression, $reflectionFunctionLike, $variables); - yield from $suggestions; - - return $suggestions->getReturn(); - } - - private function reflectFunctionLike(TextDocument $source, Node $callableExpression): ?ReflectionFunctionLike - { - $offset = $this->reflector->reflectOffset($source, $callableExpression->getEndPosition()); - - $containerType = $offset->nodeContext()->containerType(); - if ($containerType->isDefined()) { - $containerType = $containerType->expandTypes()->classLike()->firstOrNull(); - if (!$containerType instanceof ReflectedClassType) { - return null; - } - - $containerClass = $containerType->reflectionOrNull(); - - if ($containerClass === null) { - return null; - } - - return $containerClass->methods()->get($offset->nodeContext()->symbol()->name()); - } - - if (!$callableExpression instanceof QualifiedName) { - return null; - } - - $name = $callableExpression->getResolvedName() ?? $callableExpression->getText(); - - return $this->reflector->reflectFunction((string) $name); - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseSignatureHelper.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseSignatureHelper.php deleted file mode 100644 index 9d6fef7e2b..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseSignatureHelper.php +++ /dev/null @@ -1,275 +0,0 @@ -doSignatureHelp($textDocument, $offset); - } catch (NotFound $notFound) { - throw new CouldNotHelpWithSignature($notFound->getMessage(), 0, $notFound); - } - } - - private function doSignatureHelp(TextDocument $textDocument, ByteOffset $offset): SignatureHelp // NOSONAR - { - $rootNode = $this->parser->get($textDocument); - $nodeAtPosition = $rootNode->getDescendantNodeAtPosition($offset->toInt()); - - [$argsNode, $callNode] = $this->resolveArgsAndCallNode($nodeAtPosition, $offset); - - // if current position not inside a call expression - if (!$callNode && self::isACallExpression($nodeAtPosition)) { - $callNode = $nodeAtPosition; - $argsNode = null; - } - - if (!$callNode) { - throw new CouldNotHelpWithSignature(sprintf( - 'Could not provide signature for AST node of type "%s"', - get_class($nodeAtPosition) - )); - } - - $position = 0; - if ($argsNode) { - /** @var Node $argNode */ - foreach ($argsNode->getChildNodes() as $argNode) { - if ($argNode->getEndPosition() >= $offset->toInt()) { - break; - } - - ++$position; - } - } - - if ($callNode instanceof ObjectCreationExpression) { - return $this->signatureHelperForObjectCreation($callNode, $position); - } - - if ($callNode instanceof Attribute) { - return $this->signatureHelperForAttribute($callNode, $position); - } - - if (!$callNode instanceof CallExpression) { - throw new CouldNotHelpWithSignature(sprintf( - 'Could not resolve signature help for "%s"', - get_class($callNode) - )); - } - - $callable = $callNode->callableExpression; - - if ($callable instanceof QualifiedName) { - return $this->signatureHelpForFunction($callable, $position); - } - - if ($callable instanceof ScopedPropertyAccessExpression) { - return $this->signatureHelpForScopedPropertyAccess($callable, $callNode, $position); - } - - if ($callable instanceof MemberAccessExpression) { - $reflectionOffset = $this->reflector->reflectOffset($textDocument, $callable->getEndPosition()); - $nodeContext = $reflectionOffset->nodeContext(); - - if ($nodeContext->symbol()->symbolType() !== Symbol::METHOD) { - throw new CouldNotHelpWithSignature(sprintf( - 'Could not provide signature member type "%s"', - $nodeContext->symbol()->symbolType() - )); - } - - $containerType = $nodeContext->containerType()->expandTypes()->classLike()->firstOrNull(); - - if (!$containerType instanceof ClassType) { - throw new CouldNotHelpWithSignature(sprintf( - 'Container type is not a class: "%s"', - $nodeContext->symbol()->name() - )); - } - - $reflectionClass = $this->reflector->reflectClassLike($containerType->name()); - $reflectionMethod = $reflectionClass->methods()->get($nodeContext->symbol()->name()); - - return $this->createSignatureHelp($reflectionMethod, $position); - } - - throw new CouldNotHelpWithSignature(sprintf('Could not provide signature for AST node of type "%s"', get_class($callable))); - } - - private function signatureHelpForFunction(QualifiedName $callable, int $position): SignatureHelp - { - $name = $callable->__toString(); - $functionReflection = $this->reflector->reflectFunction($name); - - return $this->createSignatureHelp($functionReflection, $position); - } - - private function signatureHelperForObjectCreation(ObjectCreationExpression $node, int $position): SignatureHelp - { - $name = $node->classTypeDesignator; - if (!$name instanceof QualifiedName) { - throw new CouldNotHelpWithSignature(sprintf( - 'Only provide help for qualified names, got "%s"', - get_class($name) - )); - } - - $offset = $this->reflector->reflectOffset( - NodeToTextDocumentConverter::convert($node), - $name->getStartPosition() - ); - - $reflectionClass = $this->reflector->reflectClass($offset->nodeContext()->type()->__toString()); - $constructor = $reflectionClass->methods()->get('__construct'); - - return $this->createSignatureHelp($constructor, $position); - } - - private function createSignatureHelp(ReflectionFunctionLike $functionReflection, int $position): SignatureHelp - { - $signatures = []; - $parameters = []; - - /** @var ReflectionParameter $parameter */ - foreach ($functionReflection->parameters() as $parameter) { - $formatted = $this->formatter->format($parameter); - $parameters[] = new ParameterInformation($parameter->name(), $formatted); - } - - $formatted = $this->formatter->format($functionReflection); - $signatures[] = new SignatureInformation($formatted, $parameters); - - return new SignatureHelp($signatures, 0, $position); - } - - private function signatureHelpForScopedPropertyAccess(ScopedPropertyAccessExpression $callable, CallExpression $node, int $position): SignatureHelp - { - $scopeResolutionQualifier = $callable->scopeResolutionQualifier; - - if (!$scopeResolutionQualifier instanceof QualifiedName) { - throw new CouldNotHelpWithSignature(sprintf('Static calls only supported with qualified names')); - } - - $offset = $this->reflector->reflectOffset(NodeToTextDocumentConverter::convert($node), $scopeResolutionQualifier->getStartPosition()); - - $reflectionClass = $this->reflector->reflectClass($offset->nodeContext()->type()->__toString()); - - $memberName = $callable->memberName; - - if (!$memberName instanceof Token) { - throw new CouldNotHelpWithSignature('Variable member names not supported'); - } - - $memberName = $memberName->getText($node->getFileContents()); - $reflectionMethod = $reflectionClass->methods()->get((string) $memberName); - - return $this->createSignatureHelp($reflectionMethod, $position); - } - - private static function isACallExpression(Node $node): bool - { - return $node instanceof CallExpression || $node instanceof ObjectCreationExpression; - } - - private function signatureHelperForAttribute(Attribute $attrNode, int $position): SignatureHelp - { - $name = $attrNode->name; - if (!$name instanceof QualifiedName) { - throw new CouldNotHelpWithSignature(sprintf( - 'Only provide help for qualified names, got "%s"', - get_class($name) - )); - } - - $offset = $this->reflector->reflectOffset(NodeToTextDocumentConverter::convert($attrNode), $name->getStartPosition()); - - $reflectionClass = $this->reflector->reflectClass($offset->nodeContext()->type()->__toString()); - $constructor = $reflectionClass->methods()->get('__construct'); - - return $this->createSignatureHelp($constructor, $position); - } - - /** - * @return array{?Node,?Node} - */ - private function resolveArgsAndCallNode(Node $nodeAtPosition, ByteOffset $offset): array - { - $callNode = $nodeAtPosition; - if ( - ($nodeAtPosition instanceof CallExpression || $nodeAtPosition instanceof ObjectCreationExpression) - && null === $nodeAtPosition->argumentExpressionList - && null !== $nodeAtPosition->openParen - && $nodeAtPosition->openParen->getEndPosition() == $offset->toInt() - ) { - return [null, $nodeAtPosition]; - } - - if ($nodeAtPosition instanceof ArgumentExpressionList) { - $argsNode = $nodeAtPosition; - $callNode = $argsNode->parent ?? null; - return [$argsNode, $callNode]; - } - - if ($argsNode = $nodeAtPosition->getFirstChildNode(ArgumentExpressionList::class)) { - return [$argsNode, $nodeAtPosition]; - } - - $argsNode = $nodeAtPosition->getFirstAncestor(ArgumentExpressionList::class); - if ($argsNode) { - $callNode = $argsNode->parent ?? null; - return [$argsNode, $callNode]; - } - - // try the first node before the position of the given offset - // this is needed when the parser gets confused, f.e. `($foo, <>` - $nodeBeforeOffset = NodeUtil::firstDescendantNodeBeforeOffset($nodeAtPosition->getRoot(), $offset->toInt()); - $argsNode = $nodeBeforeOffset->getFirstAncestor(ArgumentExpressionList::class); - - if ($argsNode) { - $callNode = $argsNode->parent ?? null; - return [$argsNode, $callNode]; - } - - return [null, null]; - } -} diff --git a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseSubscriptCompletor.php b/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseSubscriptCompletor.php deleted file mode 100644 index 3019075a0c..0000000000 --- a/lib/Completion/Bridge/TolerantParser/WorseReflection/WorseSubscriptCompletor.php +++ /dev/null @@ -1,48 +0,0 @@ -couldComplete($node, $source, $offset)) { - return true; - } - - $offset = $this->reflector->reflectOffset($source, $node->getEndPosition()); - $type = $offset->nodeContext()->type(); - - if (!$type instanceof ArrayShapeType) { - return true; - } - - foreach ($type->keys() as $key) { - yield Suggestion::createWithOptions(sprintf('[\'%s\']', (string)$key), [ - 'type' => Suggestion::TYPE_FIELD, - 'short_description' => $type->typeAtOffset($key)->__toString(), - ]); - } - - return true; - } - - private function couldComplete(?Node $node, TextDocument $source, ByteOffset $offset): bool - { - return $node instanceof SubscriptExpression; - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Completor/ContextSensitiveCompletor.php b/lib/Completion/Bridge/WorseReflection/Completor/ContextSensitiveCompletor.php deleted file mode 100644 index b5fa3cad8a..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Completor/ContextSensitiveCompletor.php +++ /dev/null @@ -1,204 +0,0 @@ -inner->complete($node, $source, $offset); - if ( - !$node instanceof CallExpression && - ($node instanceof QualifiedName && !$node->parent instanceof ObjectCreationExpression) - ) { - yield from $generator; - return $generator->getReturn(); - } - - $type = $this->resolveFilterableType($node, $source, $offset); - if (null === $type) { - yield from $generator; - return $generator->getReturn(); - } - - foreach ($generator as $suggestion) { - $fqn = $suggestion->fqn(); - if (!$fqn) { - yield $suggestion; - continue; - } - - try { - $refection = $this->reflector->reflectClassLike($fqn); - } catch (NotFound $e) { - continue; - } - - if (!$refection->isInstanceOf($type->name())) { - continue; - } - yield $suggestion; - } - - return $generator->getReturn(); - } - - public function qualifier(): TolerantQualifier - { - if ($this->inner instanceof TolerantQualifiable) { - return $this->inner->qualifier(); - } - - return new AlwaysQualfifier(); - } - - public function decorates(): object - { - return $this->inner; - } - - private function resolveFilterableType(Node $node, TextDocument $source, ByteOffset $offset): ?ClassLikeType - { - $argumentNb = 0; - $memberAccessOrObjectCreation = $node; - $node = NodeUtil::firstDescendantNodeBeforeOffset($node, $offset->toInt()); - - if ($node instanceof QualifiedName) { - $memberAccessOrObjectCreation = null; - $argumentExpression = $node->parent?->parent; - - if ($argumentExpression instanceof ArgumentExpression) { - $list = $argumentExpression->getFirstAncestor(ArgumentExpressionList::class); - - if (!$list instanceof ArgumentExpressionList) { - return null; - } - $argumentNb = NodeUtil::argumentOffset($list, $argumentExpression) ?? 0; - $memberAccessOrObjectCreation = $list->parent; - } - } else { - $argumentList = $node->parent?->parent; - if ($argumentList instanceof ArgumentExpressionList) { - $values = $argumentList->getValues(); - assert(is_iterable($values)); - $argumentNb = max(0, count(iterator_to_array($values)) - 1); - $memberAccessOrObjectCreation = $argumentList->parent; - } - } - - if ( - !$memberAccessOrObjectCreation instanceof CallExpression && - !$memberAccessOrObjectCreation instanceof ObjectCreationExpression - ) { - return null; - } - - $offset = $memberAccessOrObjectCreation->openParen?->getStartPosition(); - if (null === $offset) { - return null; - } - try { - $memberAccessOrObjectCreation = $this->reflector->reflectOffset($source, $offset)->nodeContext(); - } catch (NotFound $e) { - return null; - } - if ($memberAccessOrObjectCreation instanceof MemberAccessContext) { - return $this->typeFromMemberAccess($memberAccessOrObjectCreation, $argumentNb); - } - - if ($memberAccessOrObjectCreation instanceof ClassLikeContext) { - return $this->typeFromClassInstantiation($memberAccessOrObjectCreation, $argumentNb); - } - - return null; - } - /** - * @param MemberAccessContext $memberAccessOrObjectCreation - */ - private function typeFromMemberAccess(MemberAccessContext $memberAccessOrObjectCreation, int $argumentNb): ?ClassLikeType - { - try { - $functionLike = $memberAccessOrObjectCreation->accessedMember(); - } catch (NotFound) { - return null; - } - if (!$functionLike instanceof ReflectionFunctionLike) { - return null; - } - $parameters = $functionLike->parameters(); - return $this->typeFromParameters($parameters, $argumentNb); - } - - /** - * @param int<0, max> $argumentNb - */ - private function typeFromClassInstantiation(ClassLikeContext $classLikeContext, int $argumentNb): ?ClassLikeType - { - try { - $classLike = $classLikeContext->classLike(); - $constructor = $classLike->methods()->get('__construct'); - $parameters = $constructor->parameters(); - } catch (NotFound) { - return null; - } - return $this->typeFromParameters($parameters, $argumentNb); - } - - private function typeFromParameters(ReflectionParameterCollection $parameters, int $argumentNb): ?ClassLikeType - { - $parameter = $parameters->at($argumentNb); - if (null === $parameter) { - $lastParameter = $parameters->lastOrNull(); - if (null === $lastParameter) { - return null; - } - if (!$lastParameter->isVariadic()) { - return null; - } - $parameter = $lastParameter; - } - - $type = $parameter->type(); - if ($parameter->isVariadic()) { - if ($type instanceof ArrayType) { - $type = $type->iterableValueType(); - } - } - if (!$type instanceof ClassLikeType) { - return null; - } - - return $type; - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/ClassFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/ClassFormatter.php deleted file mode 100644 index 628996fbb6..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/ClassFormatter.php +++ /dev/null @@ -1,40 +0,0 @@ -deprecation()->isDefined()) { - $info [] = '⚠ '; - } - - $info[] = $class->name(); - - if ($class->methods()->has('__construct')) { - $info[] = '('; - $info[] = $formatter->format( - $class->methods() - ->get('__construct') - ->parameters() - ); - $info[] = ')'; - } - - return implode('', $info); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/ConstantFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/ConstantFormatter.php deleted file mode 100644 index 907aa519dd..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/ConstantFormatter.php +++ /dev/null @@ -1,22 +0,0 @@ -name(), json_encode($object->value())); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/EnumCaseFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/EnumCaseFormatter.php deleted file mode 100644 index 58f7839ed7..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/EnumCaseFormatter.php +++ /dev/null @@ -1,22 +0,0 @@ -name()); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/FunctionFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/FunctionFormatter.php deleted file mode 100644 index 0f5d7eb7c7..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/FunctionFormatter.php +++ /dev/null @@ -1,39 +0,0 @@ -name() - ]; - - $paramInfos = []; - - foreach ($function->parameters() as $parameter) { - $paramInfos[] = $formatter->format($parameter); - } - $info[] = '(' . implode(', ', $paramInfos) . ')'; - - $returnType = $function->inferredType(); - - if ($returnType->isDefined()) { - $info[] = ': ' . $formatter->format($returnType); - } - - return implode('', $info); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/InterfaceFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/InterfaceFormatter.php deleted file mode 100644 index 06507711fb..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/InterfaceFormatter.php +++ /dev/null @@ -1,30 +0,0 @@ -deprecation()->isDefined()) { - $info [] = '⚠ '; - } - assert($object instanceof ReflectionInterface); - $info[] = sprintf('%s (interface)', $object->name()->full()); - - return implode('', $info); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/MethodFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/MethodFormatter.php deleted file mode 100644 index e25405ff4a..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/MethodFormatter.php +++ /dev/null @@ -1,51 +0,0 @@ -deprecation()->isDefined()) { - $info [] = '⚠ '; - } - - $info[] = substr((string) $method->visibility(), 0, 3); - $info[] = ' '; - $info[] = $method->name(); - - if ($method->isAbstract()) { - array_unshift($info, 'abstract '); - } - - $paramInfos = []; - - /** @var ReflectionParameter $parameter */ - foreach ($method->parameters() as $parameter) { - $paramInfos[] = $formatter->format($parameter); - } - $info[] = '(' . implode(', ', $paramInfos) . ')'; - - $returnType = $method->inferredType(); - - if (($returnType->isDefined())) { - $info[] = ': ' . $formatter->format($returnType); - } - - return implode('', $info); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/ParameterFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/ParameterFormatter.php deleted file mode 100644 index 6d25eed518..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/ParameterFormatter.php +++ /dev/null @@ -1,32 +0,0 @@ -inferredType(); - if ($type->isDefined()) { - $paramInfo[] = $formatter->format($object->inferredType()); - } - $paramInfo[] = '$' . $object->name(); - - if ($object->default()->isDefined()) { - $paramInfo[] = '= '. str_replace("\n", '', var_export($object->default()->value(), true)); - } - return implode(' ', $paramInfo); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/ParametersFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/ParametersFormatter.php deleted file mode 100644 index a2a26ce4d1..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/ParametersFormatter.php +++ /dev/null @@ -1,26 +0,0 @@ -format($parameter); - } - - return implode(', ', $formatted); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/PropertyFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/PropertyFormatter.php deleted file mode 100644 index acdf539a91..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/PropertyFormatter.php +++ /dev/null @@ -1,37 +0,0 @@ -visibility(), 0, 3), - ]; - - if ($object->isStatic()) { - $info[] = ' static'; - } - - $info[] = ' '; - $info[] = '$' . $object->name(); - - if (($object->inferredType()->isDefined())) { - $info[] = ': ' . $object->inferredType()->short(); - } - - return implode('', $info); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/TraitFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/TraitFormatter.php deleted file mode 100644 index 6bdd18da80..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/TraitFormatter.php +++ /dev/null @@ -1,29 +0,0 @@ -deprecation()->isDefined()) { - $info [] = '⚠ '; - } - - $info[] = sprintf('%s (trait)', $object->name()->full()); - - return implode('', $info); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/Formatter/TypeFormatter.php b/lib/Completion/Bridge/WorseReflection/Formatter/TypeFormatter.php deleted file mode 100644 index 4cbe061341..0000000000 --- a/lib/Completion/Bridge/WorseReflection/Formatter/TypeFormatter.php +++ /dev/null @@ -1,22 +0,0 @@ -format($object->type()); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/SnippetFormatter/FunctionLikeSnippetFormatter.php b/lib/Completion/Bridge/WorseReflection/SnippetFormatter/FunctionLikeSnippetFormatter.php deleted file mode 100644 index b49abca0e2..0000000000 --- a/lib/Completion/Bridge/WorseReflection/SnippetFormatter/FunctionLikeSnippetFormatter.php +++ /dev/null @@ -1,32 +0,0 @@ -name()->short() - : $functionLike->name(); - $parameters = $functionLike->parameters(); - - return $name . $formatter->format($parameters); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/SnippetFormatter/NameSearchResultClassSnippetFormatter.php b/lib/Completion/Bridge/WorseReflection/SnippetFormatter/NameSearchResultClassSnippetFormatter.php deleted file mode 100644 index 1328ac60ff..0000000000 --- a/lib/Completion/Bridge/WorseReflection/SnippetFormatter/NameSearchResultClassSnippetFormatter.php +++ /dev/null @@ -1,41 +0,0 @@ -type()->isClass(); - } - - /** - * @param NameSearchResult $nameSearchResult - */ - public function format(ObjectFormatter $formatter, object $nameSearchResult): string - { - assert($nameSearchResult instanceof NameSearchResult); - $className = $nameSearchResult->name()->__toString(); - - $classReflection = $this->reflector->reflectClassLike($className); - $shortName = $classReflection->name()->short(); - - if ($classReflection->methods()->has('__construct') === false) { - return $shortName . '()'; - } - - $constructorReflection = $classReflection->methods()->get('__construct'); - $parameters = $constructorReflection->parameters(); - return $shortName . $formatter->format($parameters); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/SnippetFormatter/NameSearchResultFunctionSnippetFormatter.php b/lib/Completion/Bridge/WorseReflection/SnippetFormatter/NameSearchResultFunctionSnippetFormatter.php deleted file mode 100644 index 6b2c76aa34..0000000000 --- a/lib/Completion/Bridge/WorseReflection/SnippetFormatter/NameSearchResultFunctionSnippetFormatter.php +++ /dev/null @@ -1,30 +0,0 @@ -type()->isFunction(); - } - - - public function format(ObjectFormatter $formatter, object $nameSearchResult): string - { - assert($nameSearchResult instanceof NameSearchResult); - $functionName = $nameSearchResult->name()->__toString(); - $functionReflection = $this->reflector->reflectFunction($functionName); - return $formatter->format($functionReflection); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/SnippetFormatter/ParametersSnippetFormatter.php b/lib/Completion/Bridge/WorseReflection/SnippetFormatter/ParametersSnippetFormatter.php deleted file mode 100644 index 338d752c88..0000000000 --- a/lib/Completion/Bridge/WorseReflection/SnippetFormatter/ParametersSnippetFormatter.php +++ /dev/null @@ -1,45 +0,0 @@ -count() === 0) { - return '()'; - } - - $placeholders = []; - $position = 0; - /** @var ReflectionParameter $parameter */ - foreach ($parameters as $parameter) { - if ($parameter->default()->isDefined()) { - continue; // Ignore optional parameters - } - - $placeholders[] = Placeholder::escape(++$position, '$' . $parameter->name()); - } - - return \sprintf( - '(%s)%s', - // If no placeholders then all parameters are optional - // But we still want to stop between the parentheses - \implode(', ', $placeholders ?: [Placeholder::raw(1)]), - Placeholder::raw(0) - ); - } -} diff --git a/lib/Completion/Bridge/WorseReflection/SuggestionDocumentor/WorseSuggestionDocumentor.php b/lib/Completion/Bridge/WorseReflection/SuggestionDocumentor/WorseSuggestionDocumentor.php deleted file mode 100644 index a3d651bafd..0000000000 --- a/lib/Completion/Bridge/WorseReflection/SuggestionDocumentor/WorseSuggestionDocumentor.php +++ /dev/null @@ -1,75 +0,0 @@ -fqn(); - - if (null === $fqn) { - return ''; - } - - if ($suggestion->type() === Suggestion::TYPE_CLASS) { - try { - $reflectionClass = $this->reflector->reflectClassLike($fqn); - } catch (NotFound) { - return $suggestion->documentation(); - } - - return $this->renderer->render(new ItemDocumentation( - $reflectionClass->name(), - $reflectionClass->docblock()->formatted(), - $reflectionClass - )); - } - - if ($suggestion->type() === Suggestion::TYPE_FUNCTION) { - try { - $reflectionFunction = $this->reflector->reflectFunction($fqn); - } catch (NotFound) { - return $suggestion->documentation(); - } - - return $this->renderer->render(new ItemDocumentation( - $reflectionFunction->name(), - $reflectionFunction->docblock()->formatted(), - $reflectionFunction - )); - } - - if ($suggestion->type() === Suggestion::TYPE_CONSTANT) { - try { - $reflectionConstant = $this->reflector->reflectConstant($fqn); - } catch (NotFound) { - return $suggestion->documentation(); - } - - return $this->renderer->render(new ItemDocumentation( - $reflectionConstant->name(), - $reflectionConstant->docblock()->formatted(), - $reflectionConstant - )); - } - - return $suggestion->documentation(); - }; - } -} diff --git a/lib/Completion/Core/ChainCompletor.php b/lib/Completion/Core/ChainCompletor.php deleted file mode 100644 index 56c769aa32..0000000000 --- a/lib/Completion/Core/ChainCompletor.php +++ /dev/null @@ -1,36 +0,0 @@ -completors as $completor) { - $start = microtime(true); - $suggestions = $completor->complete($source, $offset); - - yield from $suggestions; - - $this->logger->timeTaken($completor, microtime(true) - $start); - $isComplete = $isComplete && $suggestions->getReturn(); - } - - return $isComplete; - } -} diff --git a/lib/Completion/Core/ChainSignatureHelper.php b/lib/Completion/Core/ChainSignatureHelper.php deleted file mode 100644 index 8265723a45..0000000000 --- a/lib/Completion/Core/ChainSignatureHelper.php +++ /dev/null @@ -1,50 +0,0 @@ -add($helper); - } - } - - public function signatureHelp( - TextDocument $document, - ByteOffset $offset - ): SignatureHelp { - foreach ($this->helpers as $helper) { - try { - return $helper->signatureHelp($document, $offset); - } catch (CouldNotHelpWithSignature $couldNotHelp) { - $this->logger->debug(sprintf( - 'Could not provide signature: "%s"', - $couldNotHelp->getMessage() - )); - } - } - - throw new CouldNotHelpWithSignature( - 'Could not provide signature with chain helper' - ); - } - - private function add(SignatureHelper $helper): void - { - $this->helpers[] = $helper; - } -} diff --git a/lib/Completion/Core/Completor.php b/lib/Completion/Core/Completor.php deleted file mode 100644 index cda19ffb48..0000000000 --- a/lib/Completion/Core/Completor.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ - public function complete(TextDocument $source, ByteOffset $byteOffset): Generator; -} diff --git a/lib/Completion/Core/Completor/ArrayCompletor.php b/lib/Completion/Core/Completor/ArrayCompletor.php deleted file mode 100644 index 3153ec1b63..0000000000 --- a/lib/Completion/Core/Completor/ArrayCompletor.php +++ /dev/null @@ -1,27 +0,0 @@ -suggestions; - - return true; - } -} diff --git a/lib/Completion/Core/Completor/DedupeCompletor.php b/lib/Completion/Core/Completor/DedupeCompletor.php deleted file mode 100644 index 22a0bac975..0000000000 --- a/lib/Completion/Core/Completor/DedupeCompletor.php +++ /dev/null @@ -1,41 +0,0 @@ -innerCompletor->complete($source, $byteOffset); - foreach ($suggestions as $suggestion) { - $key = $suggestion->name().$suggestion->type(); - - if ($this->matchNameImport) { - $key .= $suggestion->fqn(); - } - - if (isset($seen[$key])) { - continue; - } - - $seen[$key] = $suggestion; - - yield $suggestion; - } - - return $suggestions->getReturn(); - } -} diff --git a/lib/Completion/Core/Completor/DocumentingCompletor.php b/lib/Completion/Core/Completor/DocumentingCompletor.php deleted file mode 100644 index eaacff13f0..0000000000 --- a/lib/Completion/Core/Completor/DocumentingCompletor.php +++ /dev/null @@ -1,30 +0,0 @@ -innerCompletor->complete($source, $byteOffset); - foreach ($suggestions as $suggestion) { - if (false === $suggestion->hasDocumentation()) { - $suggestion = $suggestion->withDocumentation($this->documentor->document($suggestion)); - } - yield $suggestion; - } - return $suggestions->getReturn(); - } -} diff --git a/lib/Completion/Core/Completor/LabelFormattingCompletor.php b/lib/Completion/Core/Completor/LabelFormattingCompletor.php deleted file mode 100644 index 30dd50d120..0000000000 --- a/lib/Completion/Core/Completor/LabelFormattingCompletor.php +++ /dev/null @@ -1,44 +0,0 @@ -completor->complete($source, $byteOffset); - foreach ($suggestions as $suggestion) { - if ( - $suggestion->type() === Suggestion::TYPE_CLASS || - $suggestion->type() === Suggestion::TYPE_CONSTANT || - $suggestion->type() === Suggestion::TYPE_FUNCTION - ) { - $label = $this->labelFormatter->format($suggestion->fqn() ?? $suggestion->label(), $seen); - $seen[$label] = true; - yield $suggestion->withLabel($label); - continue; - } - - - yield $suggestion; - } - - return $suggestions->getReturn(); - } -} diff --git a/lib/Completion/Core/Completor/LimitingCompletor.php b/lib/Completion/Core/Completor/LimitingCompletor.php deleted file mode 100644 index dbfed889f5..0000000000 --- a/lib/Completion/Core/Completor/LimitingCompletor.php +++ /dev/null @@ -1,45 +0,0 @@ -innerCompletor->complete($source, $byteOffset); - foreach ($suggestions as $suggestion) { - if ($count++ >= $this->limit) { - return false; - } - yield $suggestion; - } - - return $suggestions->getReturn(); - } - - public function decorates(): object - { - return $this->innerCompletor; - } -} diff --git a/lib/Completion/Core/Completor/NameSearcherCompletor.php b/lib/Completion/Core/Completor/NameSearcherCompletor.php deleted file mode 100644 index 86def5128b..0000000000 --- a/lib/Completion/Core/Completor/NameSearcherCompletor.php +++ /dev/null @@ -1,141 +0,0 @@ - - * @param NameSearcherType::* $type - */ - protected function completeName( - string $name, - ?TextDocumentUri $sourceUri = null, - ?Node $node = null, - ?string $type = null, - ): Generator { - $wasQualified = NameUtil::isQualified($name); - $visitedChildSegments = []; - foreach ($this->nameSearcher->search($name, $type) as $result) { - // if the child segment relative to the search is not the last segment - // then suggest the child segment only - [$segment, $isLast] = NameUtil::childSegmentAtSearch($result->name(), $name); - if ($wasQualified && $segment && false === $isLast) { - yield from $this->suggestChildSegment($visitedChildSegments, $name, $result, $sourceUri, $segment); - continue; - } - - yield $this->createSuggestion( - $name, - $result, - $wasQualified, - $node, - $this->createSuggestionOptions($result, $sourceUri, $node, $wasQualified), - ); - } - - return true; - } - - /** - * @param array $options - */ - protected function createSuggestion(string $search, NameSearchResult $result, bool $wasQualified, ?Node $node = null, array $options = []): Suggestion - { - $options = array_merge($this->createSuggestionOptions($result, null, $node), $options); - - if ($node !== null && $wasQualified) { - $name = NameUtil::relativeToSearch(ltrim($search, '\\'), $result->name()->__toString()); - /** @phpstan-ignore-next-line */ - return Suggestion::createWithOptions($name, $options); - } - - /** @phpstan-ignore-next-line */ - return Suggestion::createWithOptions($result->name()->head(), $options); - } - - /** - * @return array - */ - protected function createSuggestionOptions(NameSearchResult $result, ?TextDocumentUri $sourceUri = null, ?Node $node = null, bool $wasFullyQualified = false): array - { - $options = [ - 'short_description' => $result->name()->__toString(), - 'type' => $this->suggestionType($result), - 'class_import' => null, - 'name_import' => null, - 'priority' => $this->prioritizer->priority($result->uri(), $sourceUri) - ]; - - if (!$wasFullyQualified && ($node === null || !($node->getParent() instanceof NamespaceUseClause))) { - $options['class_import'] = $this->classImport($result); - $options['name_import'] = $result->name()->__toString(); - } - - return $options; - } - - protected function suggestionType(NameSearchResult $result): ?string - { - if ($result->type()->isClass()) { - return Suggestion::TYPE_CLASS; - } - - if ($result->type()->isFunction()) { - return Suggestion::TYPE_FUNCTION; - } - - if ($result->type()->isConstant()) { - return Suggestion::TYPE_CONSTANT; - } - - return null; - } - - protected function classImport(NameSearchResult $result): ?string - { - if ($result->type()->isClass()) { - return $result->name()->__toString(); - } - - return null; - } - /** - * @param array $visitedSegments - * @return Generator - */ - private function suggestChildSegment(&$visitedSegments, string $search, NameSearchResult $result, ?TextDocumentUri $sourceUri, string $segment): Generator - { - if (isset($visitedSegments[$segment])) { - return; - } - $visitedSegments[$segment] = true; - - yield Suggestion::createWithOptions($segment, [ - 'short_description' => NameUtil::join( - NameUtil::relativeToSearch($result->name()->__toString(), $search), - $segment - ), - 'type' => Suggestion::TYPE_MODULE, - 'priority' => $this->prioritizer->priority($result->uri(), $sourceUri) - ]); - } -} diff --git a/lib/Completion/Core/CompletorDecorator.php b/lib/Completion/Core/CompletorDecorator.php deleted file mode 100644 index 9615047019..0000000000 --- a/lib/Completion/Core/CompletorDecorator.php +++ /dev/null @@ -1,8 +0,0 @@ -logger->info(sprintf( - 'COMP %s %s', - number_format($time, 4), - self::format($completor), - )); - } - - private static function format(object $completor): string - { - $shortName = substr($completor::class, strrpos($completor::class, '\\') + 1); - - if (!$completor instanceof CompletorDecorator) { - return $shortName; - } - - return $shortName . '/' . self::format($completor->decorates()); - } -} diff --git a/lib/Completion/Core/DocumentPrioritizer/DefaultResultPrioritizer.php b/lib/Completion/Core/DocumentPrioritizer/DefaultResultPrioritizer.php deleted file mode 100644 index bd36c2401e..0000000000 --- a/lib/Completion/Core/DocumentPrioritizer/DefaultResultPrioritizer.php +++ /dev/null @@ -1,18 +0,0 @@ -priority; - } -} diff --git a/lib/Completion/Core/DocumentPrioritizer/DocumentPrioritizer.php b/lib/Completion/Core/DocumentPrioritizer/DocumentPrioritizer.php deleted file mode 100644 index 0dcc5c260a..0000000000 --- a/lib/Completion/Core/DocumentPrioritizer/DocumentPrioritizer.php +++ /dev/null @@ -1,10 +0,0 @@ -resolveWeight($one, $two); - - $range = Suggestion::PRIORITY_LOW - Suggestion::PRIORITY_MEDIUM; - - return (int)(Suggestion::PRIORITY_MEDIUM + $range - $range * $weight); - } - - private function resolveWeight(TextDocumentUri $one, TextDocumentUri $two): float - { - $e1 = explode('/', $one->path()); - $e2 = explode('/', $two->path()); - - foreach ($e1 as $index => $segment) { - if (isset($e2[$index]) && $e2[$index] === $segment) { - unset($e1[$index], $e2[$index]); - } - } - - $count1 = count($e1); - $count2 = count($e2); - - $distance = $count1 + $count2; - - if ($distance === 0) { - return 1; - } - - $max = max($count1, $count2); - $weight = $max / $distance; - return 1 - $weight; - } -} diff --git a/lib/Completion/Core/DocumentPrioritizer/SimilarityResultPrioritizer.php b/lib/Completion/Core/DocumentPrioritizer/SimilarityResultPrioritizer.php deleted file mode 100644 index 29bc9cee8e..0000000000 --- a/lib/Completion/Core/DocumentPrioritizer/SimilarityResultPrioritizer.php +++ /dev/null @@ -1,37 +0,0 @@ -path()); - $e2 = explode('/', $two->path()); - $e3 = array_intersect($e1, $e2); - $max = max(count($e1), count($e2)); - $similarity = (1 / $max) * count($e3); - - $range = Suggestion::PRIORITY_LOW - Suggestion::PRIORITY_MEDIUM; - - return (int) (Suggestion::PRIORITY_MEDIUM + $range - $range * $similarity); - } -} diff --git a/lib/Completion/Core/Exception/CouldNotFormat.php b/lib/Completion/Core/Exception/CouldNotFormat.php deleted file mode 100644 index 9d3d7fee0c..0000000000 --- a/lib/Completion/Core/Exception/CouldNotFormat.php +++ /dev/null @@ -1,9 +0,0 @@ - - */ - private array $formatters = []; - - /** - * @param array $formatters - */ - public function __construct(array $formatters = []) - { - foreach ($formatters as $formatter) { - $this->add($formatter); - } - } - - public function format(object $object): string - { - foreach ($this->formatters as $formatter) { - if (false === $formatter->canFormat($object)) { - continue; - } - - return $formatter->format($this, $object); - } - - throw new CouldNotFormat(sprintf( - 'Do not know how to format "%s"', - get_class($object) - )); - } - - public function canFormat(object $object): bool - { - foreach ($this->formatters as $formatter) { - if ($formatter->canFormat($object)) { - return true; - } - } - - return false; - } - - private function add(Formatter $formatter): void - { - $this->formatters[] = $formatter; - } -} diff --git a/lib/Completion/Core/LabelFormatter.php b/lib/Completion/Core/LabelFormatter.php deleted file mode 100644 index 2e7f57bd07..0000000000 --- a/lib/Completion/Core/LabelFormatter.php +++ /dev/null @@ -1,20 +0,0 @@ - $seen - */ - public function format(string $name, array $seen, int $offset = 1): string; -} diff --git a/lib/Completion/Core/LabelFormatter/HelpfulLabelFormatter.php b/lib/Completion/Core/LabelFormatter/HelpfulLabelFormatter.php deleted file mode 100644 index d5ae0570fc..0000000000 --- a/lib/Completion/Core/LabelFormatter/HelpfulLabelFormatter.php +++ /dev/null @@ -1,34 +0,0 @@ - $seen - */ - public function format(string $name, array $seen, int $offset = 1): string - { - $parts = explode('\\', $name); - $end = array_pop($parts); - if (count($parts) === 0) { - return $end; - } - - // the offset is more than the number of parts -- this should not - // happen as it implies two identically named classes - if (count($parts) < $offset) { - return $end; - } - - $label = sprintf('%s (%s)', $end, implode('\\', array_slice($parts, 0, $offset))); - - if (isset($seen[$label])) { - return self::format($name, $seen, $offset + 1); - } - - return $label; - } -} diff --git a/lib/Completion/Core/LabelFormatter/PassthruLabelFormatter.php b/lib/Completion/Core/LabelFormatter/PassthruLabelFormatter.php deleted file mode 100644 index 73b29c1791..0000000000 --- a/lib/Completion/Core/LabelFormatter/PassthruLabelFormatter.php +++ /dev/null @@ -1,13 +0,0 @@ -documentation; - } - - public function label(): string - { - return $this->label; - } -} diff --git a/lib/Completion/Core/Range.php b/lib/Completion/Core/Range.php deleted file mode 100644 index 0c1850f370..0000000000 --- a/lib/Completion/Core/Range.php +++ /dev/null @@ -1,37 +0,0 @@ -byteStart; - } - - public function end(): ByteOffset - { - return $this->byteEnd; - } - - public function toArray(): array - { - return [ $this->byteStart->toInt(), $this->byteEnd->toInt() ]; - } -} diff --git a/lib/Completion/Core/SignatureHelp.php b/lib/Completion/Core/SignatureHelp.php deleted file mode 100644 index 4135ed35db..0000000000 --- a/lib/Completion/Core/SignatureHelp.php +++ /dev/null @@ -1,34 +0,0 @@ -activeParameter; - } - - public function activeSignature(): int - { - return $this->activeSignature; - } - - /** - * @return SignatureInformation[] - */ - public function signatures(): array - { - return $this->signatures; - } -} diff --git a/lib/Completion/Core/SignatureHelper.php b/lib/Completion/Core/SignatureHelper.php deleted file mode 100644 index 392c8cca22..0000000000 --- a/lib/Completion/Core/SignatureHelper.php +++ /dev/null @@ -1,11 +0,0 @@ -add($parameter); - } - } - - public function parameters(): array - { - return $this->parameters; - } - - public function documentation(): ?string - { - return $this->documentation; - } - - public function label(): string - { - return $this->label; - } - - private function add(ParameterInformation $parameter): void - { - $this->parameters[] = $parameter; - } -} diff --git a/lib/Completion/Core/Suggestion.php b/lib/Completion/Core/Suggestion.php deleted file mode 100644 index e65c60eba7..0000000000 --- a/lib/Completion/Core/Suggestion.php +++ /dev/null @@ -1,240 +0,0 @@ -shortDescription = $shortDescription; - $this->label = $label ?: $name; - $this->documentation = $documentation; - } - - public static function create(string $name): self - { - return new self($name); - } - - /** - * @param SuggestionOptions $options - */ - public static function createWithOptions(string $name, array $options): self - { - $defaults = [ - 'short_description' => '', - 'documentation' => '', - 'type' => null, - 'class_import' => null, - 'name_import' => null, - 'fqn' => null, - 'label' => null, - 'range' => null, - 'snippet' => null, - 'priority' => null, - ]; - - if ($diff = array_diff(array_keys($options), array_keys($defaults))) { - throw new RuntimeException(sprintf( - 'Invalid options for suggestion: "%s" valid options: "%s"', - implode('", "', $diff), - implode('", "', array_keys($defaults)) - )); - } - - $options = array_merge($defaults, $options); - - return new self( - $name, - $options['type'], - $options['short_description'], - $options['name_import'] ? $options['name_import'] : $options['class_import'], - $options['label'], - $options['documentation'], - $options['range'], - $options['snippet'], - $options['priority'], - $options['fqn'], - ); - } - - /** - * @return array - */ - public function toArray(): array - { - return [ - 'type' => $this->type(), - 'name' => $this->name(), - 'snippet' => $this->snippet(), - 'label' => $this->label(), - 'short_description' => $this->shortDescription(), - 'documentation' => $this->documentation(), - 'class_import' => $this->type() === self::TYPE_CLASS && $this->nameImport ? $this->nameImport : null, - 'name_import' => $this->nameImport, - 'fqn' => $this->fqn, - 'range' => $this->range ? $this->range->toArray() : null, - - // removed - 'info' => '', - ]; - } - - public function type(): ?string - { - return $this->type; - } - - public function name(): string - { - return $this->name; - } - - public function snippet(): ?string - { - return $this->snippet; - } - - public function shortDescription(): ?string - { - if ($this->shortDescription instanceof Closure) { - $shortDescription = $this->shortDescription; - return $shortDescription(); - } - return $this->shortDescription; - } - - /** - * @param string|Closure $description - */ - public function withShortDescription($description): self - { - $clone = clone $this; - $clone->shortDescription = $description; - - return $clone; - } - - /** - * Return the FQN if the name should be imported. - */ - public function nameImport(): ?string - { - return $this->nameImport; - } - - /** - * Fully qualified name of suggestion, if applicable - */ - public function fqn(): ?string - { - return $this->fqn ?? $this->nameImport; - } - - public function label(): string - { - return $this->label; - } - - public function range(): ?Range - { - return $this->range; - } - - public function documentation(): ?string - { - if ($this->documentation instanceof Closure) { - $documentation = $this->documentation; - return $documentation(); - } - return $this->documentation; - } - - public function hasDocumentation(): bool - { - return !empty($this->documentation); - } - - public function priority(): ?int - { - return $this->priority; - } - - public function withLabel(string $label): self - { - $new = clone $this; - $new->label = $label; - return $new; - } - - /** - * @param null|string|Closure $documentation - */ - public function withDocumentation($documentation): self - { - $new = clone $this; - $new->documentation = $documentation; - return $new; - } -} diff --git a/lib/Completion/Core/SuggestionDocumentor.php b/lib/Completion/Core/SuggestionDocumentor.php deleted file mode 100644 index 1ba04646e5..0000000000 --- a/lib/Completion/Core/SuggestionDocumentor.php +++ /dev/null @@ -1,10 +0,0 @@ - - */ -final class Suggestions implements IteratorAggregate, Countable -{ - /** - * @var Suggestion[] - */ - private array $suggestions; - - public function __construct(Suggestion ...$suggestions) - { - $this->suggestions = $suggestions; - } - - public function at(int $index): Suggestion - { - if (!isset($this->suggestions[$index])) { - throw new RuntimeException(sprintf( - 'No suggestion at index %d', - $index - )); - } - - return $this->suggestions[$index]; - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->suggestions); - } - - public function count(): int - { - return count($this->suggestions); - } -} diff --git a/lib/Completion/Core/TypedCompletorRegistry.php b/lib/Completion/Core/TypedCompletorRegistry.php deleted file mode 100644 index 35211785dc..0000000000 --- a/lib/Completion/Core/TypedCompletorRegistry.php +++ /dev/null @@ -1,38 +0,0 @@ - - */ - private array $completors; - - /** - * Map should be from language ID to completor for that language - * (can be a chain completor): - * - * @param array $completorMap - */ - public function __construct(array $completorMap) - { - foreach ($completorMap as $type => $completor) { - $this->add($type, $completor); - } - } - - public function completorForType(string $type): Completor - { - if (!isset($this->completors[$type])) { - return new ChainCompletor([]); - } - - return $this->completors[$type]; - } - - private function add(string $type, Completor $completor): void - { - $this->completors[$type] = $completor; - } -} diff --git a/lib/Completion/Core/Util/OffsetHelper.php b/lib/Completion/Core/Util/OffsetHelper.php deleted file mode 100644 index 7264008b78..0000000000 --- a/lib/Completion/Core/Util/OffsetHelper.php +++ /dev/null @@ -1,34 +0,0 @@ -createTolerant($source) - ]); - } -} diff --git a/lib/Completion/Tests/Benchmark/Bridge/TolerantParser/WorseReflection/ClassMemberCompletorBench.php b/lib/Completion/Tests/Benchmark/Bridge/TolerantParser/WorseReflection/ClassMemberCompletorBench.php deleted file mode 100644 index e69ad9e349..0000000000 --- a/lib/Completion/Tests/Benchmark/Bridge/TolerantParser/WorseReflection/ClassMemberCompletorBench.php +++ /dev/null @@ -1,24 +0,0 @@ -addSource($source)->build(); - return new WorseClassMemberCompletor( - $reflector, - new ObjectFormatter(), - new ObjectFormatter(), - ObjectRendererBuilder::create()->build() - ); - } -} diff --git a/lib/Completion/Tests/Benchmark/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletorBench.php b/lib/Completion/Tests/Benchmark/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletorBench.php deleted file mode 100644 index 26c437ab55..0000000000 --- a/lib/Completion/Tests/Benchmark/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletorBench.php +++ /dev/null @@ -1,24 +0,0 @@ -addSource($source)->build(); - return new WorseLocalVariableCompletor( - new VariableCompletionHelper( - $reflector - ), - new ObjectFormatter() - ); - } -} diff --git a/lib/Completion/Tests/Benchmark/Code/Example1.php.test b/lib/Completion/Tests/Benchmark/Code/Example1.php.test deleted file mode 100644 index b0fbe80b5f..0000000000 --- a/lib/Completion/Tests/Benchmark/Code/Example1.php.test +++ /dev/null @@ -1,213 +0,0 @@ -reflector = $reflector; - } - - public function couldComplete(string $source, int $offset): bool - { - $untilCursor = mb_substr($source, 0, $offset); - - while ($offset) { - $chars = mb_substr($untilCursor, $offset - 2, 2); - if (in_array($chars, ['->', '::'])) { - var_dump($chars); - return true; - } - - $offset--; - $untilCursor = mb_substr($untilCursor, 0, $offset); - } - - return false; - } - - public function complete(string $source, int $offset): Response - { - list($offset, $partialMatch) = $this->getOffetToReflect($source, $offset); - - $reflectionOffset = $this->reflector->reflectOffset( - SourceCode::fromString($source), - Offset::fromint($offset) - ); - - $nodeContext = $reflectionOffset->nodeContext(); - $types = $nodeContext->types(); - - $suggestions = new Suggestions(); - - foreach ($types as $type) { - $nodeContext = $this->populateSuggestions($nodeContext, $type, $suggestions); - } - - - return new Response($suggestions, Issues::fromStrings($nodeContext->issues())); - } - - private function getOffetToReflect($source, $offset) - { - /** @var string $source */ - $source = str_replace("\n", ' ', $source); - $untilCursor = substr($source, 0, $offset); - - $pos = strlen($untilCursor) - 1; - $original = null; - while ($pos) { - if (in_array(substr($untilCursor, $pos, 2), [ '->', '::' ])) { - $original = $pos; - break; - } - $pos--; - } - - $pos--; - while (isset($untilCursor[$pos]) && $untilCursor[$pos] == ' ') { - $pos--; - } - $pos++; - - $accessorOffset = ($original - $pos) + 2; - $extra = substr($untilCursor, $pos + $accessorOffset, $offset); - - return [ $pos, $extra ]; - } - - private function getMethodInfo(ReflectionMethod $method) - { - $info = [ - substr((string) $method->visibility(), 0, 3), - ' ', - $method->name() - ]; - - if ($method->isAbstract()) { - array_unshift($info, 'abstract '); - } - - $paramInfos = []; - - /** @var ReflectionParameter $parameter */ - foreach ($method->parameters() as $parameter) { - $paramInfo = []; - if ($parameter->type()->isDefined()) { - $paramInfo[] = $parameter->type()->short(); - } - $paramInfo[] = '$' . $parameter->name(); - - if ($parameter->default()->isDefined()) { - $paramInfo[] = '= '. str_replace("\n", '', var_export($parameter->default()->value(), true)); - } - $paramInfos[] = implode(' ', $paramInfo); - } - $info[] = '(' . implode(', ', $paramInfos) . ')'; - - $returnTypes = $method->inferredReturnTypes(); - if ($returnTypes->count() > 0) { - $info[] = ': ' . implode('|', array_map(function (Type $type) { - return $type->short(); - }, iterator_to_array($returnTypes))); - } - - return implode('', $info); - } - - private function getPropertyInfo(ReflectionProperty $property) - { - $info = [ - substr((string) $property->visibility(), 0, 3), - ]; - - if ($property->isStatic()) { - $info[] = ' static'; - } - - $info[] = ' '; - $info[] = '$' . $property->name(); - - if ($property->inferredTypes()->best()->isDefined()) { - $info[] = ': ' . $property->inferredTypes()->best()->short(); - } - - return implode('', $info); - } - - private function populateSuggestions(SymbolContext $nodeContext, Type $type, Suggestions $suggestions): SymbolContext - { - if (false === $type->isDefined()) { - return $nodeContext; - } - - if ($type->isPrimitive()) { - return $nodeContext->withIssue(sprintf('Cannot complete members on scalar value (%s)', (string) $type)); - } - - try { - $classReflection = $this->reflector->reflectClassLike((string) $type); - } catch (NotFound $e) { - return $nodeContext->withIssue(sprintf('Could not find class "%s"', (string) $type)); - } - - $publicOnly = !in_array($nodeContext->symbol()->name(), ['this', 'self'], true); - /** @var ReflectionMethod $method */ - foreach ($classReflection->methods() as $method) { - if ($method->name() === '__construct') { - continue; - } - if ($publicOnly && false === $method->visibility()->isPublic()) { - continue; - } - $info = $this->getMethodInfo($method); - $suggestions->add(Suggestion::create('f', $method->name(), $info)); - } - - if ($classReflection instanceof ReflectionClass) { - foreach ($classReflection->properties() as $property) { - if ($publicOnly && false === $property->visibility()->isPublic()) { - continue; - } - $suggestions->add(Suggestion::create('m', $property->name(), $this->getPropertyInfo($property))); - } - } - - if ($classReflection instanceof ReflectionClass || - $classReflection instanceof ReflectionInterface - ) { - /** @var ReflectionClass|ReflectionInterface */ - foreach ($classReflection->constants() as $constant) { - $suggestions->add(Suggestion::create('m', $constant->name(), 'const ' . $constant->name())); - - $foobar-><> - } - } - - return $nodeContext; - } -} diff --git a/lib/Completion/Tests/Benchmark/Code/Short.php.test b/lib/Completion/Tests/Benchmark/Code/Short.php.test deleted file mode 100644 index 21e3940a3b..0000000000 --- a/lib/Completion/Tests/Benchmark/Code/Short.php.test +++ /dev/null @@ -1,5 +0,0 @@ -<> diff --git a/lib/Completion/Tests/Benchmark/CompletorBenchCase.php b/lib/Completion/Tests/Benchmark/CompletorBenchCase.php deleted file mode 100644 index 4c4209863e..0000000000 --- a/lib/Completion/Tests/Benchmark/CompletorBenchCase.php +++ /dev/null @@ -1,55 +0,0 @@ -source = $source; - $this->offset = $offset; - $this->completor = $this->create($source); - } - - /** - * @ParamProviders({"provideComplete"}) - * @BeforeMethods({"setUp"}) - * @Revs(1) - * @Iterations(10) - * @OutputTimeUnit("milliseconds") - */ - public function benchComplete($params): void - { - iterator_to_array($this->completor->complete( - TextDocumentBuilder::create($this->source)->build(), - ByteOffset::fromInt($this->offset) - )); - } - - public function provideComplete() - { - return [ - 'short' => [ - 'source' => 'Code/Short.php.test', - ], - 'long' => [ - 'source' => 'Code/Example1.php.test', - ], - ]; - } - - abstract protected function create(string $source): Completor; -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/DoctrineAnnotationCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/DoctrineAnnotationCompletorTest.php deleted file mode 100644 index 11cbf353e2..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/DoctrineAnnotationCompletorTest.php +++ /dev/null @@ -1,177 +0,0 @@ -assertComplete($source, $expected); - } - - public static function provideComplete(): Generator - { - yield 'not a docblock' => [ - <<<'EOT' - - */ - class Foo {} - EOT - , [] - ]; - - yield 'not a text annotation' => [ - <<<'EOT' - - */ - class Foo {} - EOT - , [] - ]; - - yield 'in a namespace' => [ - <<<'EOT' - - */ - class Foo {} - } - EOT - , [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Entity', - 'short_description' => 'App\Annotation\Entity', - 'snippet' => 'Entity($1)$0' - ] - ]]; - - yield 'annotation on a node in the middle of the AST' => [ - <<<'EOT' - - */ - public function foo(): string - { - return 'foo'; - } - - public function bar(): string - { - return 'bar'; - } - } - EOT - , [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Annotation', - 'short_description' => 'Annotation', - 'snippet' => 'Annotation($1)$0' - ] - ]]; - - yield 'not an annotation class' => [ - <<<'EOT' - - */ - class Foo {} - EOT - , [] - ]; - - yield 'handle errors if class not found' => [ - <<<'EOT' - - */ - class Foo {} - EOT - , [] - ]; - } - - protected function createCompletor(string $source): Completor - { - $source = TextDocumentBuilder::create($source)->uri('file:///tmp/test')->build(); - - $searcher = $this->prophesize(NameSearcher::class); - $searcher->search(Argument::any())->willYield([]); - $searcher->search('Ann', null)->willYield([ - NameSearchResult::create('class', 'Annotation') - ]); - $searcher->search('Ent', null)->willYield([ - NameSearchResult::create('class', 'App\Annotation\Entity') - ]); - $searcher->search('NotAnn', null)->willYield([ - NameSearchResult::create('class', 'NotAnnotation') - ]); - - $reflector = ReflectorBuilder::create() - ->addMemberProvider(new DocblockMemberProvider()) - ->addSource($source)->build(); - - return new DoctrineAnnotationCompletor( - $searcher->reveal(), - $reflector, - ); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/ClassMemberQualifierTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/ClassMemberQualifierTest.php deleted file mode 100644 index 3dfefa9d54..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/ClassMemberQualifierTest.php +++ /dev/null @@ -1,67 +0,0 @@ - - */ - public function provideCouldComplete(): Generator - { - yield 'non member access' => [ - '', - function (?Node $node): void { - $this->assertNull($node); - } - ]; - - yield 'variable with previous accessor' => [ - 'hello; $hello<>', - function (?Node $node): void { - $this->assertNull($node); - } - - ]; - - yield 'statement with previous member access' => [ - 'foobar) { echo<>', - function (?Node $node): void { - $this->assertNull($node); - } - ]; - - yield 'variable with previous static member access' => [ - '', - function (?Node $node): void { - $this->assertNull($node); - } - ]; - - yield 'returns the scoped property access expression' => [ - '', - function (?Node $node): void { - self::assertInstanceOf(ScopedPropertyAccessExpression::class, $node); - } - ]; - - yield 'returns the scoped property access expression parent' => [ - '', - function (?Node $node): void { - $this->assertInstanceOf(ScopedPropertyAccessExpression::class, $node); - } - ]; - } - - public function createQualifier(): TolerantQualifier - { - return new ClassMemberQualifier(); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/ClassQualifierTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/ClassQualifierTest.php deleted file mode 100644 index da3913f977..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/ClassQualifierTest.php +++ /dev/null @@ -1,43 +0,0 @@ - [ - '', - function ($node): void { - $this->assertNull($node); - } - ]; - yield 'variable with previous accessor' => [ - 'hello; $hello<>', - function ($node): void { - $this->assertNull($node); - } - ]; - yield 'statement with previous member access' => [ - 'foobar) { echo<>', - function ($node): void { - $this->assertNull($node); - } - ]; - yield 'variable with previous static member access' => [ - '', - function ($node): void { - $this->assertNull($node); - } - ]; - } - - public function createQualifier(): TolerantQualifier - { - return new ClassQualifier(); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/DocblockQualifierTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/DocblockQualifierTest.php deleted file mode 100644 index 656c96525e..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/DocblockQualifierTest.php +++ /dev/null @@ -1,33 +0,0 @@ - [ - '', - function (?Node $node): void { - $this->assertNull($node); - } - ]; - - yield 'docblock' => [ - '', - function (?Node $node): void { - self::assertInstanceOf(Node::class, $node); - } - ]; - } - - public function createQualifier(): TolerantQualifier - { - return new DocblockQualifier(); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/TolerantQualifierTestCase.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/TolerantQualifierTestCase.php deleted file mode 100644 index e10d784607..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/TolerantQualifierTestCase.php +++ /dev/null @@ -1,28 +0,0 @@ -get(TextDocumentBuilder::create($source)->build()); - $node = $root->getDescendantNodeAtPosition($offset); - - $assertion($this->createQualifier()->couldComplete($node)); - } - - abstract public function createQualifier(): TolerantQualifier; -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/AttributeCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/AttributeCompletorTest.php deleted file mode 100644 index b59e3cbb2b..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/AttributeCompletorTest.php +++ /dev/null @@ -1,179 +0,0 @@ ->} $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - $this->assertComplete($source, $expected); - } - - /** - * @return Generator>}> - */ - public static function provideComplete(): Generator - { - yield 'new class instance' => [ - ']class Bar{}', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Foobar', - 'short_description' => 'Foobar', - ], - ], - ]; - - yield 'method' => [ - '] public function zxc() {} }', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'FoobarMethod', - 'short_description' => 'FoobarMethod', - ], - ], - ]; - - yield 'class constant' => [ - '] const X = 1; }', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'FoobarClassConstsant', - 'short_description' => 'FoobarClassConstsant', - ], - ], - ]; - - yield 'parameter' => [ - '] $x, $y) {} }', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'FoobarParameter', - 'short_description' => 'FoobarParameter', - ], - ], - ]; - - yield 'property' => [ - '] private $x; }', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'FoobarProperty', - 'short_description' => 'FoobarProperty', - ], - ], - ]; - - yield 'function' => [ - '] function x(); }', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'FoobarFunction', - 'short_description' => 'FoobarFunction', - ], - ], - ]; - - yield 'promoted property' => [ - '] private $x); }', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'FoobarParameter', - 'short_description' => 'FoobarParameter', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'FoobarProperty', - 'short_description' => 'FoobarProperty', - ], - ], - ]; - - yield 'only show children for qualified names' => [ - ']class Bar{}', [ - [ - 'type' => Suggestion::TYPE_MODULE, - 'name' => 'One', - 'short_description' => 'Foo\Relative\One', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Two', - 'short_description' => 'Foo\Relative\Two', - ], - [ - 'type' => Suggestion::TYPE_MODULE, - 'name' => 'Two', - 'short_description' => 'Foo\Relative\Two', - ], - ], - ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $searcher = $this->prophesize(NameSearcher::class); - $searcher->search('Foo', NameSearcherType::ATTRIBUTE_TARGET_CLASS)->willYield([ - NameSearchResult::create('class', 'Foobar'), - ]); - $searcher->search('Foo', NameSearcherType::ATTRIBUTE_TARGET_METHOD)->willYield([ - NameSearchResult::create('class', 'FoobarMethod'), - ]); - $searcher->search('Foo', NameSearcherType::ATTRIBUTE_TARGET_CLASS_CONSTANT)->willYield([ - NameSearchResult::create('class', 'FoobarClassConstsant'), - ]); - $searcher->search('Foo', NameSearcherType::ATTRIBUTE_TARGET_PARAMETER)->willYield([ - NameSearchResult::create('class', 'FoobarParameter'), - ]); - $searcher->search('Foo', NameSearcherType::ATTRIBUTE_TARGET_PROPERTY)->willYield([ - NameSearchResult::create('class', 'FoobarProperty'), - ]); - $searcher->search('Foo', NameSearcherType::ATTRIBUTE_TARGET_PROMOTED_PROPERTY)->willYield([ - NameSearchResult::create('class', 'FoobarProperty'), - NameSearchResult::create('class', 'FoobarParameter'), - ]); - $searcher->search('Foo', NameSearcherType::ATTRIBUTE_TARGET_FUNCTION)->willYield([ - NameSearchResult::create('class', 'FoobarFunction'), - ]); - $searcher->search('\\Foo\\Relative', NameSearcherType::ATTRIBUTE_TARGET_CLASS)->willYield([ - NameSearchResult::create('class', 'Foo\Relative\One\Blah\Boo'), - NameSearchResult::create('class', 'Foo\Relative\One\Glorm\Bar'), - NameSearchResult::create('class', 'Foo\Relative\One\Blah'), - NameSearchResult::create('class', 'Foo\Relative\Two'), - NameSearchResult::create('class', 'Foo\Relative\Two\Glorm\Bar'), - ]); - - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - - return new AttributeCompletor( - $searcher->reveal(), - new DefaultResultPrioritizer(), - ); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/ExpressionNameCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/ExpressionNameCompletorTest.php deleted file mode 100644 index f6270ccef8..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/ExpressionNameCompletorTest.php +++ /dev/null @@ -1,327 +0,0 @@ - $searchResults - * @param Closure(Suggestions): void $assertion - */ - #[DataProvider('provideComplete')] - public function testComplete(array $searchResults, string $source, Closure $assertion): void - { - $searcher = new PredefinedNameSearcher($searchResults); - - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - - $completor = new ExpressionNameCompletor( - $searcher, - $this->snippetFormatter($reflector) - ); - - [$source, $offset] = ExtractOffset::fromSource($source); - $document = TextDocumentBuilder::fromPathAndString(__DIR__, $source); - $node = (new TolerantAstProvider())->get($document)->getDescendantNodeAtPosition($offset); - $results = new Suggestions( - ...iterator_to_array( - $completor->complete( - $node, - $document, - ByteOffset::fromInt($offset) - ), - false - ) - ); - $assertion($results); - } - - /** - * @return Generator,string,Closure(Suggestions):void}> - */ - public static function provideComplete(): Generator - { - yield 'new class instance' => [ - [ - NameSearchResult::create('class', 'Foobar'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals(Suggestion::TYPE_CLASS, $suggestions->at(0)->type()); - self::assertEquals('Foobar', $suggestions->at(0)->name()); - self::assertEquals('Foobar', $suggestions->at(0)->shortDescription()); - self::assertEquals('Foobar(${1:\\$cparam})${0}', $suggestions->at(0)->snippet()); - } - ]; - yield 'new class instance (empty constructor)' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals(Suggestion::TYPE_CLASS, $suggestions->at(0)->type()); - self::assertEquals('Foobar', $suggestions->at(0)->name()); - self::assertEquals('Foobar', $suggestions->at(0)->shortDescription()); - self::assertEquals('Foobar()', $suggestions->at(0)->snippet()); - } - ]; - yield 'class typehint (no instantiation)' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals(Suggestion::TYPE_CLASS, $suggestions->at(0)->type()); - self::assertEquals('Foobar', $suggestions->at(0)->name()); - self::assertEquals('Foobar', $suggestions->at(0)->shortDescription()); - } - ]; - - yield 'function' => [ - [ - NameSearchResult::create('class', 'Bar'), - NameSearchResult::create('function', 'bar_foo'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals(Suggestion::TYPE_FUNCTION, $suggestions->at(0)->type()); - self::assertEquals('bar_foo', $suggestions->at(0)->name()); - self::assertEquals('bar_foo', $suggestions->at(0)->shortDescription()); - self::assertEquals('bar_foo(${1:\\$foo})${0}', $suggestions->at(0)->snippet()); - } - ]; - yield 'function (empty params)' => [ - [ - NameSearchResult::create('function', 'bar'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals(Suggestion::TYPE_FUNCTION, $suggestions->at(0)->type()); - self::assertEquals('bar', $suggestions->at(0)->name()); - self::assertEquals('bar', $suggestions->at(0)->shortDescription()); - self::assertEquals('bar()', $suggestions->at(0)->snippet()); - } - ]; - - yield 'constant' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('constant', 'FOO'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals(Suggestion::TYPE_CONSTANT, $suggestions->at(0)->type()); - self::assertEquals('FOO', $suggestions->at(0)->name()); - }, - ]; - - yield 'class constant inside class constant declaration' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - NameSearchResult::create('class', 'FOOBAR'), - ], - ' }', - function (Suggestions $suggestions): void { - self::assertCount(0, $suggestions); - } - ]; - - yield 'class name inside class constant declaration' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - NameSearchResult::create('constant', 'FOO'), - ], - ' }', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals(Suggestion::TYPE_CONSTANT, $suggestions->at(0)->type()); - self::assertEquals('FOO', $suggestions->at(0)->name()); - } - ]; - - yield 'class name inside heredoc' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('constant', 'FOO'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(0, $suggestions); - } - ]; - - yield 'nested class name inside class constant declaration' => [ - [ - NameSearchResult::create('constant', 'FOO'), - ], - ' }', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals(Suggestion::TYPE_CONSTANT, $suggestions->at(0)->type()); - } - ]; - - yield 'class name inside the first match arm' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - ' }', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - } - ]; - - yield 'class name inside the second match arm' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - ' }', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - } - ]; - - yield 'class name inside match expression' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - ' Fo<> }', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - } - ]; - - yield 'within namespace with no import match' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'NS1\Foo\Foobar'), - NameSearchResult::create('class', 'Class'), - ], - ' Foo\Fo<> }', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals('Foobar', $suggestions->at(0)->name()); - } - ]; - - yield 'within namespace with with import match' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'NS1\Foo\Foobar'), - NameSearchResult::create('class', 'Foobar\Foo\Foo'), - NameSearchResult::create('class', 'Class'), - ], - ' Foo\Fo<> }', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals('Foo', $suggestions->at(0)->name()); - } - ]; - - yield 'only show children for qualified names' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - NameSearchResult::create('class', 'NS1\Relative\One\Blah\Boo'), - NameSearchResult::create('class', 'NS1\Relative\One\Glorm\Bar'), - NameSearchResult::create('class', 'NS1\Relative\One\Blah'), - NameSearchResult::create('class', 'NS1\Relative\Two'), - NameSearchResult::create('class', 'NS1\Relative\Two\Glorm\Bar'), - ], - ' Relative\<> }', - function (Suggestions $suggestions): void { - self::assertCount(3, $suggestions); - self::assertEquals('One', $suggestions->at(0)->name()); - self::assertEquals('Two', $suggestions->at(1)->name()); - self::assertEquals('Two', $suggestions->at(2)->name()); - self::assertEquals(Suggestion::TYPE_MODULE, $suggestions->at(2)->type()); - } - ]; - yield 'bare call' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - 'bar(<>', - function (Suggestions $suggestions): void { - self::assertCount(2, $suggestions); - } - ]; - yield 'bare call 2' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - 'bar(F<>', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - } - ]; - yield 'php tag' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(0, $suggestions); - } - ]; - yield 'after php tag' => [ - [ - NameSearchResult::create('class', 'Foobar'), - NameSearchResult::create('class', 'Class'), - ], - '', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - } - ]; - yield 'attribute parameter value outside class' => [ - [ - NameSearchResult::create('class', 'Xxyz'), - ], - ')] function foo() {}', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - }, - ]; - yield 'attribute parameter value - property' => [ - [ - NameSearchResult::create('class', 'Xxyz'), - ], - ')] public x; }', - function (Suggestions $suggestions): void { - self::assertCount(1, $suggestions); - }, - ]; - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/UseNameCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/UseNameCompletorTest.php deleted file mode 100644 index c8ed6d76cd..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/ReferenceFinder/UseNameCompletorTest.php +++ /dev/null @@ -1,74 +0,0 @@ ->} $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - $this->assertComplete($source, $expected); - } - - /** - * @return Generator>}> - */ - public static function provideComplete(): Generator - { - yield 'first segment' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Foobar', - 'short_description' => 'Foobar', - ] - ] - ]; - yield 'second segment' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Barfoo', - 'short_description' => 'Foobar\Barfoo', - ] - ] - ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $searcher = $this->prophesize(NameSearcher::class); - $searcher->search('\Fo', null)->willYield([ - NameSearchResult::create('class', 'Foobar'), - ]); - - $searcher->search('\Foobar\Bar', null)->willYield([ - NameSearchResult::create('class', 'Foobar\Barfoo'), - ]); - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - - return new UseNameCompletor( - $searcher->reveal(), - new DefaultResultPrioritizer(), - ); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletorTest.php deleted file mode 100644 index d919525be6..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletorTest.php +++ /dev/null @@ -1,218 +0,0 @@ -assertComplete($source, $expected); - } - - public static function provideComplete(): Generator - { - yield 'extends' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Alphabet', - 'short_description' => 'Test\Name\Alphabet', - 'range' => [ 27, 27 ], - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Backwards', - 'short_description' => 'Test\Name\Backwards', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Clapping', - 'short_description' => 'Test\Name\Clapping', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'WithoutNS', - 'short_description' => 'WithoutNS', - ], - ], - ]; - - yield 'extends partial' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Clapping', - 'short_description' => 'Test\Name\Clapping', - 'range' => [ 27, 29 ], - ], - ], - ]; - - yield 'extends keyword with subsequent code' => [ - ' { }', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Clapping', - 'short_description' => 'Test\Name\Clapping', - ], - ], - ]; - - yield 'new keyword' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Alphabet', - 'short_description' => 'Test\Name\Alphabet', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Backwards', - 'short_description' => 'Test\Name\Backwards', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Clapping', - 'short_description' => 'Test\Name\Clapping', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'WithoutNS', - 'short_description' => 'WithoutNS', - ], - ], - ]; - - yield 'new keyword with partial' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Clapping', - 'short_description' => 'Test\Name\Clapping', - ], - ], - ]; - - yield 'use keyword' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Alphabet', - 'short_description' => 'Test\Name\Alphabet', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Backwards', - 'short_description' => 'Test\Name\Backwards', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Clapping', - 'short_description' => 'Test\Name\Clapping', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'WithoutNS', - 'short_description' => 'WithoutNS', - ], - ], - ]; - - yield 'use keyword with partial' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Clapping', - 'short_description' => 'Test\Name\Clapping', - ], - ], - ]; - } - - #[DataProvider('provideImportClass')] - public function testImportClass($source, $expected): void - { - $this->assertComplete($source, $expected); - } - - public static function provideImportClass(): Generator - { - yield 'does not import from the root namespace when in the root namespace' => [ - '', - [ - [ - 'name' => 'WithoutNS', - 'class_import' => null, - ], - ], - ]; - - yield 'does not import when candidate class is in the same namespace' => [ - '', - [ - [ - 'name' => 'Alphabet', - 'class_import' => null, - ], - ], - ]; - - yield 'does not import when candidate class is already imported' => [ - '', - [ - [ - 'name' => 'Alphabet', - 'class_import' => null - ], - ], - ]; - - yield 'when candidate class is in different namespace' => [ - '', - [ - [ - 'name' => 'Alphabet', - 'class_import' => 'Test\Name\Alphabet', - ], - ], - ]; - - yield 'when the candidate class is in the root namespace' => [ - '', - [ - [ - 'name' => 'WithoutNS', - 'class_import' => 'WithoutNS', - ], - ], - ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $filesystem = new SimpleFilesystem(FilePath::fromString(__DIR__ . '/files')); - $fileToClass = new SimpleFileToClass(); - - return new ScfClassCompletor($filesystem, $fileToClass, new ClassQualifier(0)); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/files/Alphabet.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/files/Alphabet.php deleted file mode 100644 index 54535e3fcc..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/files/Alphabet.php +++ /dev/null @@ -1,7 +0,0 @@ -uri('file:///tmp/test')->build(); - return new ChainTolerantCompletor([ - $this->createTolerantCompletor($source) - ]); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/TypeSuggestionProviderTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/TypeSuggestionProviderTest.php deleted file mode 100644 index def2e617a9..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/TypeSuggestionProviderTest.php +++ /dev/null @@ -1,89 +0,0 @@ - $expected - */ - #[DataProvider('provideProvide')] - public function testProvide(string $source, string $search, array $expected): void - { - $searcher = new PredefinedNameSearcher([ - NameSearchResult::create( - 'class', - FullyQualifiedName::fromString('Namespace\Aardvark') - ), - ]); - [$source, $offset] = ExtractOffset::fromSource($source); - $node = (new TolerantAstProvider())->parseString($source)->getDescendantNodeAtPosition((int)$offset); - $suggestions = iterator_to_array((new TypeSuggestionProvider($searcher))->provide($node, $search)); - self::assertArraySubset( - $expected, - array_map(fn (Suggestion $s) => $s->name(), $suggestions) - ); - } - - /** - * @return Generator - */ - public static function provideProvide(): Generator - { - yield [ - '', - '', - [], - ]; - yield 'imported name' => [ - '', - 'F', - [ - 'Foobar', - ], - ]; - yield 'scalar' => [ - '', - '', - [ - 'string', - 'float', - 'int', - ], - ]; - yield 'generic' => [ - '', - 'Foo [ - '', - 'Foo|', - [ - 'string', - ], - ]; - yield 'intersection' => [ - '', - 'Foo&', - [ - 'string', - ], - ]; - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/DocblockCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/DocblockCompletorTest.php deleted file mode 100644 index db9cefa25a..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/DocblockCompletorTest.php +++ /dev/null @@ -1,167 +0,0 @@ - $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - $results = [ - NameSearchResult::create( - 'class', - FullyQualifiedName::fromString('Namespace\Aardvark') - ), - ]; - - [$source, $offset] = ExtractOffset::fromSource($source); - $node = (new TolerantAstProvider())->parseString($source)->getDescendantNodeAtPosition((int)$offset); - $suggestions = iterator_to_array((new DocblockCompletor( - new TypeSuggestionProvider(new PredefinedNameSearcher($results)), - new TolerantAstProvider(), - ))->complete($node, TextDocumentBuilder::create($source)->build(), ByteOffset::fromInt((int)$offset)), false); - $actualNames = array_map(fn (Suggestion $s) => $s->name(), $suggestions); - foreach ($expected as $expectedName) { - if (!in_array($expectedName, $actualNames)) { - self::fail(sprintf( - 'Expected "%s" to be in set of completion results: "%s"', - $expectedName, - implode('", "', $actualNames) - )); - } - } - $this->addToAssertionCount(1); - } - - /** - * @return Generator - */ - public static function provideComplete(): Generator - { - yield 'not in docblock' => [ - '@param<>', - [] - ]; - - yield 'in docblock' => [ - '/** @para<> */', - [ - '@param', - ] - ]; - - yield 'in second-line docblock' => [ - '* @para<> */', - [ - '@param', - ] - ]; - - yield 'in second-line docblock with more spaces' => [ - ' * @para<> */', - [ - '@param', - ] - ]; - - yield 'bare ampersand' => [ - ' * @<>', - DocblockCompletor::SUPPORTED_TAGS, - ]; - - yield 'param type' => [ - ' * @param A<> */', - [ - 'Aardvark', - ], - ]; - - yield 'param type no match' => [ - ' * @param Zed<> */', - [ - ], - ]; - - yield 'var type match' => [ - ' * @var Aar<> */', - [ - 'Aardvark', - ], - ]; - - yield 'throws type match' => [ - ' * @throws Aar<> */', - [ - 'Aardvark', - ], - ]; - - yield 'param variable' => [ - ' */function bar($aardvark, $foo)', - [ - '$aardvark', - ], - ]; - - yield 'param variable for method' => [ - <<<'EOT' - - */ - private function resolveSingleType(string $search): string - } - EOT - , [ - '$search', - ], - ]; - - yield 'no var if not param' => [ - ' */function bar($aardvark, $foo)', - [ - ], - ]; - - yield 'property docblock for class' => [ - <<<'EOT' - - */ - class Bar - { - private function resolveSingleType(string $search): string - } - EOT - , [ - 'Foobar', - ], - ]; - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/ImportedNameCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/ImportedNameCompletorTest.php deleted file mode 100644 index 03963e4ea3..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/ImportedNameCompletorTest.php +++ /dev/null @@ -1,129 +0,0 @@ - $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - $this->assertComplete($source, $expected); - } - - public static function provideComplete(): Generator - { - yield 'no imports' => [ - <<<'EOT' - - EOT - , - [] - ]; - - yield 'import local' => [ - <<<'EOT' - - EOT - , - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Barfoo', - 'short_description' => 'Barfoo', - ] - ] - ]; - - yield 'import with aliased class' => [ - <<<'EOT' - - } - EOT - , - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'BarfooThis', - 'short_description' => 'Barfoo', - ] - ] - ]; - - yield 'import with aliased class and concrete class' => [ - <<<'EOT' - - } - EOT - , - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Barbar', - 'short_description' => 'Barbar', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'BarfooThis', - 'short_description' => 'Barfoo', - ] - ] - ]; - - yield 'import multi-part non-aliased class' => [ - <<<'EOT' - - } - EOT - , - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Barbar', - 'short_description' => 'Foo\\Bar\\Barbar', - ], - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'Barfoo', - 'short_description' => 'Foo\\Bar\\Barfoo', - ] - ] - ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - return new ImportedNameCompletor(new ClassQualifier(0)); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/KeywordCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/KeywordCompletorTest.php deleted file mode 100644 index c840eaf9bd..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/KeywordCompletorTest.php +++ /dev/null @@ -1,149 +0,0 @@ -[]} $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - $this->assertComplete($source, $expected); - } - - /** - * @return Generator[]}> - */ - public function provideComplete(): Generator - { - yield 'member keywords' => [ - '', - $this->expect(['private ', 'protected ', 'public ']), - ]; - - yield 'member keyword postfix' => [ - '', - $this->expect(['const ', 'function ']), - ]; - yield 'member keyword postfix 2' => [ - '', - $this->expect(['const ', 'function ']), - ]; - - yield '__construct' => [ - '', - [...$this->expectMagicMethods()], - ]; - yield '__construct 2' => [ - ' }', - [...$this->expectMagicMethods()], - ]; - - yield 'no magic methods here' => [ - ')', - [], - ]; - - yield 'class implements 1' => [ - '', - $this->expect(['extends ', 'implements ']), - ]; - yield 'class implements 2' => [ - '', - $this->expect(['extends ', 'implements ']), - ]; - - yield 'class keyword' => [ - '', - $this->expect(['class ', 'enum ', 'function ', 'interface ', 'trait ']), - ]; - yield 'class keyword 2' => [ - '', - $this->expect(['class ', 'enum ', 'function ', 'interface ', 'trait ']), - ]; - yield 'class keyword 3' => [ - '', - $this->expect(['class ', 'enum ', 'function ', 'interface ', 'trait ']), - ]; - - yield 'if condition classes' => [ - '} }', - $this->expect(['instanceof ']), - ]; - yield 'if condition' => [ - '', - $this->expect(['instanceof ']), - ]; - yield 'while with empty expression' => [ - '', - $this->expect([]), - ]; - yield 'while condition' => [ - '', - $this->expect(['instanceof ']), - ]; - yield 'while condition (without variable)' => [ - '', - [], - ]; - yield 'while condition (with expression)' => [ - 'getParent() i<>', - $this->expect(['instanceof ']), - ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - return new KeywordCompletor(); - } - - /** - * @return array> - * @param array $array - */ - private function expect(array $array): array - { - return array_map(fn (string $keyword) => [ - 'name' => $keyword, - ], $array); - } - - /** - * @return Generator - */ - private function expectMagicMethods(): Generator - { - $methods = [ - '__construct' => "(\$1)\n{\$0\n}", - '__call' => "(string \\\$\${1:name}, array \\\$\${2:arguments}): \${3:mixed}\n{\$0\n}", - '__callStatic' => "(string \\\$\${1:name}, array \\\$\${2:arguments}): \${3:mixed}\n{\$0\n}", - '__clone' => "(): void\n{\$0\n}", - '__debugInfo' => "(): array\n{\$0\n}", - '__destruct' => "(): void\n{\$0\n}", - '__get' => "(string \\\$\${1:name}): \${3:mixed}\n{\$0\n}", - '__invoke' => "(\$1): \${2:mixed}\n{\$0\n}", - '__isset' => "(string \\\$\${1:name}): bool\n{\$0\n}", - '__serialize' => "(): array\n{\$0\n}", - '__set' => "(string \\\$\${1:name}, mixed \\\$\${2:value}): void\n{\$0\n}", - '__set_state' => "(array \\\$\${1:properties}): object\n{\$0\n}", - '__sleep' => "(): array\n{\$0\n}", - '__toString' => "(): string\n{\$0\n}", - '__unserialize' => "(array \\\$\${1:data}): void\n{\$0\n}", - '__unset' => "(string \\\$\${1:name}): void\n{\$0\n}", - '__wakeup' => "(): void\n{\$0\n}", - ]; - - foreach ($methods as $name => $snippet) { - yield ['name' => $name . '(', 'snippet' => $name . $snippet]; - } - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletorTest.php deleted file mode 100644 index 6caec6e447..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletorTest.php +++ /dev/null @@ -1,1075 +0,0 @@ - $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - $this->assertComplete($source, $expected); - } - - /** - * @return Generator}> - */ - public static function provideComplete(): Generator - { - yield 'Public property' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => 'foo', - 'short_description' => 'pub $foo', - ] - ] - ]; - - yield 'Private property' => [ - <<<'EOT' - <> - - EOT - , - [ ] - ]; - - yield 'Public property access' => [ - <<<'EOT' - foo-><> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => 'bar', - 'short_description' => 'pub $bar', - ] - ] - ]; - - yield 'Public method with parameters' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'foo', - 'short_description' => 'pub foo(string $zzzbar = \'bar\', $def): Barbar', - 'snippet' => 'foo(${1:\$def})${0}', - ] - ] - ]; - - yield 'Public method multiple return types' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'foo', - 'short_description' => 'pub foo(): Foobar|Barbar', - 'snippet' => 'foo()', - ] - ] - ]; - - yield 'Private method' => [ - <<<'EOT' - <> - - EOT - , [ - ] - ]; - - yield 'Public method with documentation' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'foo', - 'short_description' => 'pub foo(): Foobar|Barbar', - 'documentation' => '', - 'snippet' => 'foo()', - ] - ] - ]; - - yield 'Virtual method' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'foo', - 'short_description' => 'pub foo(): Foobar', - 'snippet' => 'foo()', - ] - ] - ]; - - yield 'Virtual static method' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'foo', - 'short_description' => 'pub foo(): Foobar', - 'snippet' => 'foo()', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'class', - 'short_description' => 'Foobar', - ], - ] - ]; - - yield 'Static property' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => '$foo', - 'short_description' => 'pub static $foo', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'class', - 'short_description' => 'Foobar', - ], - ] - ]; - - yield 'Static property with previous arrow accessor' => [ - <<<'EOT' - me::<> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => '$foo', - 'short_description' => 'pub static $foo', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'class', - 'short_description' => 'Foobar', - ], - ] - ]; - - yield 'Partially completed method with text after' => [ - <<<'EOT' - bb<>new Foobar(); - } - - public function bbb() {} - public function ccc() {} - } - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'bbb', - 'short_description' => 'pub bbb()', - 'snippet' => 'bbb()', - ] - ] - ]; - - yield 'Partially completed static method with brackets' => [ - <<<'EOT' - (); - } - - public static function bbb() {} - public static function ccc() {} - } - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'bbb', - 'short_description' => 'pub bbb()', - 'snippet' => 'bbb', - ] - ] - ]; - - yield 'Partially completed static method with text after' => [ - <<<'EOT' - new Foobar(); - } - - public static function bbb() {} - public static function ccc() {} - } - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'bbb', - 'short_description' => 'pub bbb()', - 'snippet' => 'bbb()', - ] - ] - ]; - - yield 'Partially completed 3' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => '$foobar', - 'short_description' => 'pub static $foobar', - ] - ] - ]; - - yield 'Partially completed 2' => [ - <<<'EOT' - bb<> - } - - public function bbb() {} - public function ccc() {} - } - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'bbb', - 'short_description' => 'pub bbb()', - 'snippet' => 'bbb()', - ] - ] - ]; - - yield 'Partially completed' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'BARFOO', - 'short_description' => 'BARFOO = "barfoo"', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'FOOBAR', - 'short_description' => 'FOOBAR = "foobar"', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'class', - 'short_description' => 'Foobar', - ], - ], - ]; - - yield 'Constant visibility from outside' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'FOOBAR', - 'short_description' => 'FOOBAR = "foobar"', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'class', - 'short_description' => 'Foobar', - ], - ], - ]; - - yield 'Constant visibility from inside' => [ - <<<'EOT' - - } - } - - EOT - , [ - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'BARFOO', - 'short_description' => 'BARFOO = "barfoo"', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'BARFOX', - 'short_description' => 'BARFOX = "barfox"', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'FOOBAR', - 'short_description' => 'FOOBAR = "foobar"', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'class', - 'short_description' => 'Foobar', - ], - ], - ]; - - yield 'Accessor on new line' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => 'foobar', - 'short_description' => 'pub $foobar', - ], - ], - ]; - - yield 'Completion on collection' => [ - <<<'EOT' - - */ - public function collection() {} - } - - $foobar = new Foobar(); - $collection = $foobar->collection(); - $collection-><> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'heyho', - 'short_description' => 'pub heyho()', - 'snippet' => 'heyho()', - ], - ], - ]; - - yield 'Completion on assignment' => [ - <<<'EOT' - meth<> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'method1', - 'short_description' => 'pub method1()', - 'snippet' => 'method1()', - ], - ], - ]; - - yield 'member is variable name' => [ - <<<'EOT' - $bar<>; - EOT - , [ - ] - ]; - - yield 'chained method call with arguments' => [ - <<<'EOT' - goodbye() - ->hello('one', 'two') - -><> - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'goodbye', - 'snippet' => 'goodbye()', - ], - ] - ]; - - yield 'chained static method call with arguments' => [ - <<<'EOT' - hello('one', 'two') - -><> - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'goodbye', - 'snippet' => 'goodbye()', - ], - ] - ]; - - yield 'instance member on static method' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'hello', - 'snippet' => 'hello()', - ], - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'class', - 'short_description' => 'BarBar', - ], - ] - ]; - - yield 'shows static member on instance method' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'goodbye', - 'snippet' => 'goodbye()', - ], - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'hello', - 'snippet' => 'hello()', - ], - ] - ]; - - yield 'static property' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => '$foo', - 'short_description' => 'pub static $foo: Foo', - ], - ] - ]; - - yield 'union' => [ - <<<'EOT' - <>; - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'fun', - 'short_description' => 'pub fun(): string', - 'snippet' => 'fun()', - ], - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'not', - 'short_description' => 'pub not(): string', - 'snippet' => 'not()', - ], - ] - ]; - - yield 'enum' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'FOO', - 'short_description' => 'FOO = "FOO"', - ], - [ - 'type' => Suggestion::TYPE_ENUM, - 'name' => 'FOOBAR', - 'short_description' => 'case FOOBAR', - ], - ] - ]; - - yield 'enum case' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'cases', - 'short_description' => 'pub cases(): BackedEnumCase[]', - 'snippet' => 'cases()', - ], - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'from', - 'short_description' => 'pub from(int|string $value): static(Enum1)', - 'snippet' => 'from(${1:\\$value})${0}', - ], - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => 'name', - ], - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'tryFrom', - 'short_description' => 'pub tryFrom(int|string $value): static(Enum1)|null', - 'snippet' => 'tryFrom(${1:\\$value})${0}', - ], - [ - 'type' => Suggestion::TYPE_PROPERTY, - 'name' => 'value', - ], - ] - ]; - - /** See https://github.com/phpactor/phpactor/issues/1612 - yield 'backed enum' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_ENUM, - 'name' => 'FOOBAR', - 'short_description' => 'case FOOBAR = "bar"', - ], - ] - ]; - */ - - yield 'nullable' => [ - <<<'EOT' - <>; - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'not', - 'short_description' => 'pub not(): string', - 'snippet' => 'not()', - ], - ] - ]; - - yield 'No constants for instance' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'bar', - 'short_description' => 'pub bar(): string', - 'snippet' => 'bar()' - ], - ], - ]; - - yield 'parent::' => [ - <<<'EOT' - - } - } - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'baz', - 'short_description' => 'pub baz(): string', - 'snippet' => 'baz()' - ], - ], - ]; - - yield 'parent::__construct' => [ - <<<'EOT' - - } - } - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => '__construct', - 'short_description' => 'pub __construct(string $foo)', - 'snippet' => '__construct(${1:\\$foo})${0}' - ], - ], - ]; - - yield 'parenthesized type' => [ - <<<'EOT' - <> - } - EOT - , [ - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'foo', - 'short_description' => 'pub foo()', - 'snippet' => 'foo()' - ], - [ - 'type' => Suggestion::TYPE_METHOD, - 'name' => 'foo', - 'short_description' => 'pub foo()', - 'snippet' => 'foo()' - ], - ], - ]; - } - - #[DataProvider('provideCouldNotComplete')] - public function testCouldNotComplete(string $source): void - { - $this->assertCouldNotComplete($source); - } - - /** - * @return Generator - */ - public static function provideCouldNotComplete(): Generator - { - yield 'non member access' => [ '' ]; - yield 'variable with previous accessor' => [ 'hello; $hello<>' ]; - yield 'statement with previous member access' => [ 'foobar) { echo<>' ]; - yield 'variable with previous static member access' => [ '' ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create() - ->addMemberProvider(new DocblockMemberProvider()) - ->addSource($source)->build(); - - return new WorseClassMemberCompletor( - $reflector, - $this->formatter(), - $this->snippetFormatter($reflector), - ObjectRendererBuilder::create()->renderEmptyOnNotFound()->build() - ); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletorTestWithoutSnippetFormatter.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletorTestWithoutSnippetFormatter.php deleted file mode 100644 index b5b621701e..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletorTestWithoutSnippetFormatter.php +++ /dev/null @@ -1,51 +0,0 @@ - $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - // Expect all snippets to be null - foreach ($expected as &$suggestion) { - if (array_key_exists('snippet', $suggestion)) { - $suggestion['snippet'] = null; - } - } - - $this->assertComplete($source, $expected); - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create() - ->addMemberProvider(new DocblockMemberProvider()) - ->addSource($source)->build(); - - return new WorseClassMemberCompletor( - $reflector, - $this->formatter(), - new ObjectFormatter(), - ObjectRendererBuilder::create()->renderEmptyOnNotFound()->build() - ); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstantCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstantCompletorTest.php deleted file mode 100644 index 0d88641816..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstantCompletorTest.php +++ /dev/null @@ -1,61 +0,0 @@ -assertComplete($source, $expected); - } - - #[DataProvider('provideCouldNotComplete')] - public function testCouldNotComplete(string $source): void - { - $this->assertCouldNotComplete($source); - } - - public static function provideComplete(): Generator - { - define('PHPACTOR_TEST_FOO', 'Hello'); - yield 'constant' => [ - '', [ - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'PHPACTOR_TEST_FOO', - 'short_description' => "PHPACTOR_TEST_FOO = 'Hello'", - ] - ] - ]; - - define('namespaced\PHPACTOR_NAMESPACED', 'Hello'); - yield 'namespaced constant' => [ - '', [ - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name' => 'PHPACTOR_NAMESPACED', - 'short_description' => "namespaced\PHPACTOR_NAMESPACED = 'Hello'", - ] - ] - ]; - } - - public static function provideCouldNotComplete(): Generator - { - yield 'non member access' => [ '' ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - return new WorseConstantCompletor($this->formatter()); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstructorCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstructorCompletorTest.php deleted file mode 100644 index 2883877288..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstructorCompletorTest.php +++ /dev/null @@ -1,179 +0,0 @@ -assertComplete($source, $expected); - } - - public static function provideCompleteMethodParameter(): Generator - { - yield 'no parameters' => [ - <<<'EOT' - ); - EOT - , [], - ]; - - yield 'parameter 1' => [ - <<<'EOT' - ); - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$param', - 'short_description' => '"string" => param #1 string $foo', - ] - ] - ]; - - yield 'parameter, 2nd pos' => [ - <<<'EOT' - ); - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$hello', - 'short_description' => 'Foobar => param #2 Foobar $bar', - ] - ] - ]; - - yield 'parameter, 3rd pos' => [ - <<<'EOT' - ); - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$param', - 'short_description' => '"string" => param #3 $mixed', - ], - ] - ]; - - yield 'no suggestions when exceeding parameter arity' => [ - <<<'EOT' - ); - EOT - , [] - ]; - - yield 'namespaced class' => [ - <<<'EOT' - ); - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$param', - 'short_description' => '"string" => param #1 string $foo', - ], - ] - ]; - - yield 'complete on open braclet' => [ - <<<'EOT' - - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$mar', - 'short_description' => '"" => param #1 string $foobar', - ], - ], - ]; - } - - public function provideCompleteStaticClassParameter() - { - yield 'complete static method parameter' => [ - <<<'EOT' - ); - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$param', - 'short_description' => 'string => param #3 $mixed', - ], - ], - ]; - } - - #[DataProvider('provideCouldNotComplete')] - public function testCouldNotComplete(string $source): void - { - $this->assertCouldNotComplete($source); - } - - public static function provideCouldNotComplete(): Generator - { - yield 'non member access' => [ '' ]; - yield 'variable with previous accessor' => [ 'hello; $hello<>' ]; - yield 'statement with previous member access' => [ 'foobar) { echo<>' ]; - yield 'variable with previous static member access' => [ '' ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - return new WorseConstructorCompletor($reflector, $this->formatter()); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseDeclaredClassCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseDeclaredClassCompletorTest.php deleted file mode 100644 index 00d37bbe6b..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseDeclaredClassCompletorTest.php +++ /dev/null @@ -1,57 +0,0 @@ -assertComplete($source, $expected); - } - - /** - * @return Generator>}> - */ - public static function provideComplete(): Generator - { - yield 'array object' => [ - <<<'EOT' - - EOT - , - [ - [ - 'type' => Suggestion::TYPE_CLASS, - 'name' => 'RangeException', - ] - ] - ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create() - ->addLocator(new StubSourceLocator( - ReflectorBuilder::create()->build(), - __DIR__ . '/../../../../../../../vendor/jetbrains/phpstorm-stubs', - __DIR__ . '/../../../../../cache' - )) - ->addSource($source) - ->build(); - - return new WorseDeclaredClassCompletor($reflector, $this->formatter()); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletorTest.php deleted file mode 100644 index 3c04d88edf..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletorTest.php +++ /dev/null @@ -1,77 +0,0 @@ -assertComplete($source, $expected); - } - - #[DataProvider('provideCouldNotComplete')] - public function testCouldNotComplete(string $source): void - { - $this->assertCouldNotComplete($source); - } - - /** - * @return Generator>}> - */ - public static function provideComplete(): Generator - { - yield 'function with parameters' => [ - '', [ - [ - 'type' => Suggestion::TYPE_FUNCTION, - 'name' => 'mystrpos', - 'snippet' => 'mystrpos(${1:\$haystack}, ${2:\$needle})${0}', - ] - ] - ]; - - yield 'namespaced function name' => [ - ' }', [ - [ - 'type' => Suggestion::TYPE_FUNCTION, - 'name' => 'barfoo', - 'short_description' => 'foobar\barfoo(): int', - 'snippet' => 'barfoo()', - ] - ] - ]; - } - - /** - * @return Generator - */ - public static function provideCouldNotComplete(): Generator - { - yield 'non member access' => [ '' ]; - - yield 'return value' => [ '' ]; - - yield 'parameter type' => [ ')' ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - - return new WorseFunctionCompletor( - $reflector, - $this->formatter(), - $this->snippetFormatter($reflector) - ); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletorTest.php deleted file mode 100644 index 3d5fcd737f..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletorTest.php +++ /dev/null @@ -1,207 +0,0 @@ -assertComplete($source, $expected); - } - - #[DataProvider('provideCouldNotComplete')] - public function testCouldNotComplete(string $source): void - { - $this->assertCouldNotComplete($source); - } - - /** - * @return Generator - */ - public static function provideCouldNotComplete(): Generator - { - yield 'empty string' => [ '' ]; - yield 'function call' => [ '' ]; - yield 'variable with space' => [ '' ]; - yield 'static variable' => ['']; - } - - /** - * @return Generator>}> - */ - public static function provideComplete(): Generator - { - yield 'Nothing' => [ - '', [] - ]; - - yield 'Variable' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$foobar', - 'short_description' => '"hello"', - 'documentation' => '"hello"', - ] - ] - ]; - - yield 'Partial variable' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$foobar', - 'short_description' => '"hello"', - ] - ] - ]; - - yield 'Variables' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$barfoo', - 'short_description' => '12', - ], - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$foobar', - 'short_description' => '"hello"', - ], - ] - ]; - - yield 'Complete previously declared variable which had no type' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$callMe', - 'short_description' => 'Barfoo', - ], - ], - ]; - - yield 'Does not assign offer suggestion for incomplete assignment' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$std', - 'short_description' => 'stdClass', - ], - ], - ]; - - yield 'array keys' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$foo', - 'short_description' => 'array{foo:string,baz:int}', - ], - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => "\$foo['baz']", - 'short_description' => 'int', - ], - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => "\$foo['foo']", - 'short_description' => 'string', - ], - ] - ]; - - yield 'no array keys' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$foo', - 'short_description' => 'array{string,int}', - ], - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => '$foo[0]', - 'short_description' => 'string', - ], - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => '$foo[1]', - 'short_description' => 'int', - ], - ] - ]; - } - - /** - * @return Generator>}> - */ - public static function provideUseVariables(): Generator - { - yield 'Use variables' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$barfoo', - 'short_description' => '12', - ], - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$foobar', - 'short_description' => '"hello"', - ], - ] - ]; - - yield 'Use variables not in function body' => [ - '', - [ - ] - ]; - } - - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - return new WorseLocalVariableCompletor( - new VariableCompletionHelper($reflector), - $this->formatter() - ); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseNamedParameterCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseNamedParameterCompletorTest.php deleted file mode 100644 index 2c69fdd534..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseNamedParameterCompletorTest.php +++ /dev/null @@ -1,155 +0,0 @@ -assertComplete($source, $expected); - } - - public static function provideComplete(): Generator - { - yield 'Variable' => [ - '', [] - ]; - - yield 'Constructor' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => 'one: ', - 'short_description' => 'string $one', - ] - ] - ]; - - yield 'Method' => [ - 'bee(o<>', - [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => 'one: ', - 'short_description' => 'string $one', - ] - ] - ]; - yield 'no completion after string literal' => [ - 'bee(\'foo\'<>', - [ - ] - ]; - - yield 'Ignore when completing a variable' => [ - 'bee($o<>', - [ - ] - ]; - - yield 'Method call in partial method call' => [ - 'bee($b->boo()-><>', - [ - ] - ]; - - yield 'Method call in method call' => [ - 'bee($b->boo(<>', - [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => 'two: ', - 'short_description' => 'string $two', - ] - ] - ]; - - yield 'Static' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => 'one: ', - 'short_description' => 'string $one', - ] - ] - ]; - - yield 'Static call begin' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => 'one: ', - 'short_description' => 'string $one', - ] - ] - ]; - yield 'function' => [ - '', - [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => 'one: ', - 'short_description' => 'string $one', - ] - ] - ]; - - yield 'Attributes' => [ - <<)] - class Foo {} - PHP, - [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => 'param: ', - 'short_description' => 'string $param', - ] - ] - ]; - } - - #[DataProvider('provideCouldNotComplete')] - public function testCouldNotComplete(string $source): void - { - $this->assertCouldNotComplete($source); - } - - /** - * @return Generator - */ - public static function provideCouldNotComplete(): Generator - { - yield 'empty string' => [ '' ]; - yield 'function call' => [ '' ]; - yield 'variable with space' => [ '' ]; - yield 'static variable' => ['']; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - return new WorseNamedParameterCompletor($reflector, $this->formatter()); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseParameterCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseParameterCompletorTest.php deleted file mode 100644 index 3949ed0dd2..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseParameterCompletorTest.php +++ /dev/null @@ -1,332 +0,0 @@ -assertComplete($source, $expected); - } - - public static function provideCompleteMethodParameter(): Generator - { - yield 'no parameters' => [ - <<<'EOT' - barbar($<> - EOT - , [ - ] - ]; - - yield 'parameter' => [ - <<<'EOT' - barbar($<> - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$param', - 'short_description' => '"string" => param #1 string $foo', - ] - ] - ]; - - yield 'parameter, 2nd pos' => [ - <<<'EOT' - barbar($foo, $<> - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$foobar', - 'short_description' => 'Foobar => param #2 Foobar $bar', - ] - ] - ]; - - yield 'parameter, 3rd pos' => [ - <<<'EOT' - barbar($param, $foobar, $<>); - EOT - , [ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$foobar', - 'short_description' => 'Foobar => param #3 $mixed', - ], - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$param', - 'short_description' => '"string" => param #3 $mixed', - ], - ] - ]; - - yield 'no suggestions when exceeding parameter arity' => [ - <<<'EOT' - barbar($param, $<>); - EOT - , [] - ]; - - yield 'function parameter completion' => [ - <<<'EOT' - ); - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$hello', - 'short_description' => '"string" => param #2 string $barbar', - ], - ], - ]; - - yield 'function parameter completion, single parameters' => [ - <<<'EOT' - ); - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$hello', - 'short_description' => '"string" => param #1 $bar', - ], - ], - ]; - - - yield 'does not use variables declared after offset a' => [ - <<<'EOT' - - $hello = 1234; - } - } - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$hello', - 'short_description' => '"string" => param #1 $bar', - ], - ], - ]; - - yield 'does not use variables declared after offset with bracket' => [ - <<<'EOT' - - $hello = 1234; - } - } - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$hello', - 'short_description' => '"string" => param #1 $bar', - ], - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$this', - 'short_description' => 'Hello => param #1 $bar', - ], - ], - ]; - - yield 'can complete methods declared after the offset' => [ - <<<'EOT' - bonjour($<> - } - - public function bonjour($bar) - { - } - } - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$this', - 'short_description' => 'Hello => param #1 $bar', - ], - ], - ]; - - yield 'complete on open braclet' => [ - <<<'EOT' - bonjour(<> - } - - public function bonjour($bar) - { - } - } - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$this', - 'short_description' => 'Hello => param #1 $bar', - ], - ], - ]; - } - - #[DataProvider('provideCompleteFunctionParameter')] - public function testCompleteFunctionParameter(string $source, array $expected): void - { - $this->assertComplete($source, $expected); - } - - public static function provideCompleteFunctionParameter(): Generator - { - yield 'complete after comma' => [ - <<<'EOT' - ); - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$hello', - 'short_description' => '"string" => param #2 string $barbar', - ], - ], - ]; - - yield 'complete on open braclet' => [ - <<<'EOT' - - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$hello', - 'short_description' => '"string" => param #1 $bar', - ], - ], - ]; - } - - #[DataProvider('provideCompleteStaticClassParameter')] - public function testCompleteStaticClassParameter(string $source, array $expected): void - { - $this->assertComplete($source, $expected); - } - - public static function provideCompleteStaticClassParameter(): Generator - { - yield 'complete static method parameter' => [ - <<<'EOT' - ); - EOT - ,[ - [ - 'type' => Suggestion::TYPE_VARIABLE, - 'name' => '$param', - 'short_description' => '"string" => param #3 $mixed', - ], - ], - ]; - } - - #[DataProvider('provideCouldNotComplete')] - public function testCouldNotComplete(string $source): void - { - $this->assertCouldNotComplete($source); - } - - public static function provideCouldNotComplete(): Generator - { - yield 'non member access' => [ '' ]; - yield 'variable with previous accessor' => [ 'hello; $hello<>' ]; - yield 'statement with previous member access' => [ 'foobar) { echo<>' ]; - yield 'variable with previous static member access' => [ '' ]; - } - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - return new WorseParameterCompletor($reflector, $this->formatter()); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseSignatureHelperTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseSignatureHelperTest.php deleted file mode 100644 index 046dd288ce..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseSignatureHelperTest.php +++ /dev/null @@ -1,508 +0,0 @@ -expectException(CouldNotHelpWithSignature::class); - } - - [ $source, $offset ] = ExtractOffset::fromSource($source); - $source = TextDocumentBuilder::create($source)->language('php')->uri('file:///tmp/test')->build(); - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - - $helper = new WorseSignatureHelper($reflector, $this->formatter()); - - $help = $helper->signatureHelp( - $source, - ByteOffset::fromInt((int)$offset) - ); - - $this->assertEquals($expected, $help); - } - - public static function provideSignatureHelper(): Generator - { - yield 'not a signature' => [ - 'ello";', - null - ]; - - yield 'not existing function' => [ - '', - null - ]; - - yield 'function signature with no parameters' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'hello()', - [] - )], - 0 - ) - ]; - - yield 'function signature with no parameters inside another function' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'hi()', - [] - )], - 0 - ) - ]; - - yield 'function signature with no parameters inside another function 2' => [ - ')', - new SignatureHelp( - [new SignatureInformation( - 'hi()', - [] - )], - 0 - ) - ]; - - yield 'constructor signature with no parameters inside another function' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'pub __construct()', - [] - )], - 0 - ) - ]; - - yield 'constructor signature with no parameters inside another function 2' => [ - ')', - - new SignatureHelp( - [new SignatureInformation( - 'pub __construct()', - [] - )], - 0 - ) - ]; - - yield 'static method signature with no parameters inside another function' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'pub hi()', - [] - )], - 0 - ) - ]; - yield 'self method signature with no parameters inside another function' => [ - ' }', - new SignatureHelp( - [new SignatureInformation( - 'pub hi()', - [] - )], - 0 - ) - ]; - - yield 'function with parameter' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'hello(string $foo)', - [ - new ParameterInformation('foo', 'string $foo'), - ] - )], - 0, - 0 - ) - ]; - - yield 'function with parameters' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 0 - ) - ]; - - yield 'function with parameters, 2nd active' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'function with parameters, 2nd active and already filled' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'function with parameters, 2nd active within other nodes' => [ - ']]', - new SignatureHelp( - [new SignatureInformation( - 'hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'function with parameters, 2nd active on multiple lines' => [ - <<<'EOT' - - ); - EOT, - new SignatureHelp( - [new SignatureInformation( - 'hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'nested function with parameters, 2nd active' => [ - <<<'EOT' - - )); - EOT, - new SignatureHelp( - [new SignatureInformation( - 'goodbye(string $good, int $by)', - [ - new ParameterInformation('good', 'string $good'), - new ParameterInformation('by', 'int $by'), - ] - )], - 0, - 1 - ) - ]; - - yield 'nested function on the second function, 2nd arg of 1st call active' => [ - <<<'EOT' - dbye("good", "by") - ); - EOT, - new SignatureHelp( - [new SignatureInformation( - 'hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'function with parameters, 1st contains comma and 2nd active' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'hello(string $foo, int $bar, bool $foobar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - new ParameterInformation('foobar', 'bool $foobar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'static method call' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'pub hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 0 - ) - ]; - - yield 'static method call on non existing class' => [ - '', - null - ]; - - yield 'static method call, 2nd active' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'pub hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'static method call, on variable' => [ - '', - null - ]; - - yield 'instance method' => [ - 'hello(<>', - new SignatureHelp( - [new SignatureInformation( - 'pub hello(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 0 - ) - ]; - - yield 'instance from an interface' => [ - 'hello(<>', - new SignatureHelp( - [new SignatureInformation( - 'pub hello(string $foo, int $bar): void', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 0 - ) - ]; - - yield 'non existing method throws exception' => [ - 'bads(<>', - null, - ]; - - yield 'class no constructor' => [ - '', - null - ]; - - yield 'class with construct' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'pub __construct(string $foo)', - [ - new ParameterInformation('foo', 'string $foo'), - ] - )], - 0, - 0 - ) - ]; - - yield 'class with construct 2nd pos' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'pub __construct(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'class with construct 2nd pos after space' => [ - '', - new SignatureHelp( - [new SignatureInformation( - 'pub __construct(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'class with namespaced' => [ - <<<'EOT' - - } - EOT - , - new SignatureHelp( - [new SignatureInformation( - 'pub __construct(string $foo, int $bar)', - [ - new ParameterInformation('foo', 'string $foo'), - new ParameterInformation('bar', 'int $bar'), - ] - )], - 0, - 1 - ) - ]; - - yield 'non-existing static member' => [ - <<<'EOT' - ); - EOT - , null - ]; - } - - /** - * @return Generator - */ - public static function providePhp8(): Generator - { - yield 'attribute 1' => [ - <<<'EOT' - - class Bar {} - EOT - , new SignatureHelp( - [new SignatureInformation( - 'pub __construct(string $bar)', - [ - new ParameterInformation('bar', 'string $bar'), - ] - )], - 0, - 0 - ) - ]; - - yield 'attribute 2' => [ - <<<'EOT' - - class Bar {} - EOT - , new SignatureHelp( - [new SignatureInformation( - 'pub __construct(string $bar, string $baz)', - [ - new ParameterInformation('bar', 'string $bar'), - new ParameterInformation('baz', 'string $baz'), - ] - )], - 0, - 1 - ) - ]; - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseSubscriptCompletorTest.php b/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseSubscriptCompletorTest.php deleted file mode 100644 index 3d5f685fe5..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseSubscriptCompletorTest.php +++ /dev/null @@ -1,80 +0,0 @@ - $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - $this->assertComplete($source, $expected); - } - - #[DataProvider('provideCouldNotComplete')] - public function testCouldNotComplete(string $source): void - { - $this->assertCouldNotComplete($source); - } - - /** - * @return Generator - */ - public static function provideCouldNotComplete(): Generator - { - yield 'empty string' => [ '' ]; - yield 'function call' => [ '' ]; - yield 'variable with space' => [ '' ]; - yield 'static variable' => ['']; - } - - /** - * @return Generator>}> - */ - public static function provideComplete(): Generator - { - yield 'variable' => [ - '', [] - ]; - - yield 'subscript with no type' => [ - '', [] - ]; - yield 'subscript with type' => [ - '', [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => '[\'foo\']', - 'short_description' => 'string', - ] - ] - ]; - yield 'nested subscript with type' => [ - '', [ - [ - 'type' => Suggestion::TYPE_FIELD, - 'name' => '[\'one\']', - 'short_description' => 'int', - ] - ] - ]; - } - - - protected function createTolerantCompletor(TextDocument $source): TolerantCompletor - { - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - return new WorseSubscriptCompletor($reflector); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/WorseReflection/Formatter/ConstantFormatterTest.php b/lib/Completion/Tests/Integration/Bridge/WorseReflection/Formatter/ConstantFormatterTest.php deleted file mode 100644 index 045eae89e3..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/WorseReflection/Formatter/ConstantFormatterTest.php +++ /dev/null @@ -1,43 +0,0 @@ -build()->reflectClassesIn($code)->classes()->first()->constants()->first(); - - self::assertTrue($this->formatter()->canFormat($constant)); - self::assertEquals($expected, $this->formatter()->format($constant)); - } - - /** - * @return Generator - */ - public static function provideFormatConstant(): Generator - { - yield 'string' => [ - ' [ - ' [ - 'build()->reflectClassesIn(TextDocumentBuilder::fromUnknown('first(); - self::assertTrue($this->formatter()->canFormat($interface)); - self::assertEquals('Bar\\Foobar (interface)', $this->formatter()->format($interface)); - } -} diff --git a/lib/Completion/Tests/Integration/Bridge/WorseReflection/Formatter/MethodFormatterTest.php b/lib/Completion/Tests/Integration/Bridge/WorseReflection/Formatter/MethodFormatterTest.php deleted file mode 100644 index b5cd7df22b..0000000000 --- a/lib/Completion/Tests/Integration/Bridge/WorseReflection/Formatter/MethodFormatterTest.php +++ /dev/null @@ -1,40 +0,0 @@ -build()->reflectClassesIn( - $code - )->first()->methods()->first(); - - self::assertTrue($this->formatter()->canFormat($constant)); - self::assertEquals($expected, $this->formatter()->format($constant)); - } - - /** - * @return Generator - */ - public static function provideFormatConstant(): Generator - { - yield [ - 'build()->reflectClassesIn(TextDocumentBuilder::fromUnknown('first(); - self::assertTrue($this->formatter()->canFormat($trait)); - self::assertEquals('Bar\\Foobar (trait)', $this->formatter()->format($trait)); - } - - public function testFormatsDeprecatedTrait(): void - { - $trait = ReflectorBuilder::create()->build()->reflectClassesIn(TextDocumentBuilder::fromUnknown('first(); - self::assertTrue($this->formatter()->canFormat($trait)); - self::assertEquals('⚠ Bar\\Foobar (trait)', $this->formatter()->format($trait)); - } -} diff --git a/lib/Completion/Tests/Integration/CompletorTestCase.php b/lib/Completion/Tests/Integration/CompletorTestCase.php deleted file mode 100644 index bf412ade90..0000000000 --- a/lib/Completion/Tests/Integration/CompletorTestCase.php +++ /dev/null @@ -1,61 +0,0 @@ -createCompletor($source); - $suggestions = $completor->complete( - TextDocumentBuilder::create($source)->language('php')->uri('file:///tmp/test')->build(), - ByteOffset::fromInt($offset) - ); - - $array = iterator_to_array($suggestions); - $this->assertEmpty($array); - $this->assertTrue($suggestions->getReturn()); - } - - abstract protected function createCompletor(string $source): Completor; - - protected function assertComplete(string $source, array $expected, bool $isComplete = true): void - { - [$source, $offset] = ExtractOffset::fromSource($source); - $completor = $this->createCompletor($source); - $suggestionGenerator = $completor->complete( - TextDocumentBuilder::create($source)->language('php')->uri('file:///tmp/test')->build(), - ByteOffset::fromInt((int)$offset) - ); - $suggestions = iterator_to_array($suggestionGenerator, false); - usort($suggestions, function (Suggestion $suggestion1, Suggestion $suggestion2) { - if ($suggestion1->priority() !== $suggestion2->priority()) { - return $suggestion1->priority() <=> $suggestion2->priority(); - } - - return $suggestion1->name() <=> $suggestion2->name(); - }); - - $this->assertCount(count($expected), $suggestions); - foreach ($expected as $index => $expectedSuggestion) { - $actual = $suggestions[$index]->toArray(); - $this->assertArraySubset($expectedSuggestion, $actual); - if (array_key_exists('snippet', $expectedSuggestion) === false) { - self::assertEmpty($actual['snippet'], 'got unexpected snippet "' . $actual['snippet'] . '"'); - } - } - - $this->assertCount(count($expected), $suggestions); - $this->assertEquals($isComplete, $suggestionGenerator->getReturn(), '"is complete" was as expected'); - } -} diff --git a/lib/Completion/Tests/Integration/IntegrationTestCase.php b/lib/Completion/Tests/Integration/IntegrationTestCase.php deleted file mode 100644 index 2bf532c922..0000000000 --- a/lib/Completion/Tests/Integration/IntegrationTestCase.php +++ /dev/null @@ -1,54 +0,0 @@ -assertEquals($expected, $formatter->format($type)); - } - - /** - * @return Generator - */ - public static function provideFormat(): Generator - { - $reflector = ReflectorBuilder::create()->build(); - yield 'no types' => [ - TypeFactory::unknown(), - '', - ]; - - yield 'single scalar' => [ - TypeFactory::string(), - 'string', - ]; - - yield 'union' => [ - TypeFactory::union(TypeFactory::string(), TypeFactory::null()), - 'string|null', - ]; - - yield 'typed array' => [ - TypeFactory::array(TypeFactory::string()), - 'string[]', - ]; - - yield 'generic' => [ - TypeFactory::collection($reflector, 'Collection', 'Item'), - 'Collection', - ]; - } -} diff --git a/lib/Completion/Tests/Unit/Adapter/WorseReflection/SuggestionDocumentor/WorseSuggestionDocumentorTest.php b/lib/Completion/Tests/Unit/Adapter/WorseReflection/SuggestionDocumentor/WorseSuggestionDocumentorTest.php deleted file mode 100644 index 53f948e4b9..0000000000 --- a/lib/Completion/Tests/Unit/Adapter/WorseReflection/SuggestionDocumentor/WorseSuggestionDocumentorTest.php +++ /dev/null @@ -1,63 +0,0 @@ -createDocumentor('document(Suggestion::createWithOptions('Foobar', [ - 'type' => Suggestion::TYPE_CLASS, - 'name_import' => 'Foobar', - 'documentation' => 'Boo', - ])); - self::assertNotEmpty($documentation); - } - - public function testFunctionSuggestion(): void - { - $documentation = $this->createDocumentor('document(Suggestion::createWithOptions('Foobar', [ - 'type' => Suggestion::TYPE_FUNCTION, - 'name_import' => 'boo', - ])); - self::assertNotEmpty($documentation); - } - - public function testConstantSuggestion(): void - { - $documentation = $this->createDocumentor('document( - Suggestion::createWithOptions( - 'foobar', - [ - 'type' => Suggestion::TYPE_CONSTANT, - 'name_import' => 'foobar' - ] - ) - ); - self::assertNotEmpty($documentation); - } - - public function testOtherSuggestion(): void - { - $documentation = $this->createDocumentor('document(Suggestion::createWithOptions('Foobar', [ - 'type' => Suggestion::TYPE_UNIT, - 'name_import' => 'boo', - 'documentation' => 'Boo', - ])); - self::assertEquals('Boo', $documentation()); - } - - private function createDocumentor(string $string): WorseSuggestionDocumentor - { - return new WorseSuggestionDocumentor( - ReflectorBuilder::create()->addSource($string)->build(), - ObjectRendererBuilder::create()->enableInterfaceCandidates()->enableAncestoralCandidates()->renderEmptyOnNotFound()->build() - ); - } -} diff --git a/lib/Completion/Tests/Unit/Bridge/TolerantParser/ChainTolerantCompletorTest.php b/lib/Completion/Tests/Unit/Bridge/TolerantParser/ChainTolerantCompletorTest.php deleted file mode 100644 index af12e2271d..0000000000 --- a/lib/Completion/Tests/Unit/Bridge/TolerantParser/ChainTolerantCompletorTest.php +++ /dev/null @@ -1,171 +0,0 @@ - - */ - private ObjectProphecy $completor1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $qualifiableCompletor1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $qualifier1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $qualifiableCompletor2; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $qualifier2; - - protected function setUp(): void - { - $this->completor1 = $this->prophesize(TolerantCompletor::class); - $this->qualifiableCompletor1 = $this->prophesize(TolerantCompletor::class) - ->willImplement(TolerantQualifiable::class); - $this->qualifiableCompletor2 = $this->prophesize(TolerantCompletor::class) - ->willImplement(TolerantQualifiable::class); - - $this->qualifier1 = $this->prophesize(TolerantQualifier::class); - $this->qualifier2 = $this->prophesize(TolerantQualifier::class); - } - - public function testEmptyResponseWithNoCompletors(): void - { - $completor = $this->create([]); - $suggestions = $completor->complete( - TextDocumentBuilder::create('build(), - ByteOffset::fromInt(1) - ); - $this->assertCount(0, iterator_to_array($suggestions, false)); - $this->assertTrue($suggestions->getReturn()); - } - - public function testCallsCompletors(): void - { - $completor = $this->create([ - $this->completor1->reveal(), - ]); - - $this->completor1->complete( - Argument::type(Node::class), - TextDocumentBuilder::create('build(), - ByteOffset::fromInt(1) - )->will(function () { - yield Suggestion::create('foo'); - return false; - }); - - $suggestions = $completor->complete( - TextDocumentBuilder::create('build(), - ByteOffset::fromInt(1) - ); - $this->assertCount(1, iterator_to_array($suggestions, false)); - $this->assertFalse($suggestions->getReturn()); - } - - public function testPassesCorrectByteOffsetToParser(): void - { - $completor = $this->create([ $this->completor1->reveal() ]); - [$source, $offset] = ExtractOffset::fromSource( - <<<'EOT' - - EOT - ); - - // the parser node passed to the tolerant completor should be the one - // at the requested char offset - $this->completor1->complete( - Argument::that(function ($arg) { - return $arg->getText() === '$'; - }), - $source, - $offset - )->will(function ($args): void { - return; - }); - - $completor->complete( - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt($offset) - ); - $this->addToAssertionCount(1); - } - - public function testExcludesNonQualifingClasses(): void - { - $completor = $this->create([ - $this->qualifiableCompletor1->reveal(), - $this->qualifiableCompletor2->reveal(), - ]); - $this->qualifiableCompletor1->qualifier()->willReturn($this->qualifier1->reveal()); - $this->qualifiableCompletor2->qualifier()->willReturn($this->qualifier2->reveal()); - - $this->qualifier1->couldComplete(Argument::type(Node::class))->shouldBeCalled()->will(function (array $args) { - return $args[0]; - }); - $this->qualifier2->couldComplete(Argument::type(Node::class))->shouldBeCalled()->willReturn(null); - - $this->qualifiableCompletor1->complete( - Argument::type(Node::class), - TextDocumentBuilder::create('build(), - ByteOffset::fromInt(1) - )->will(function () { - yield Suggestion::create('foo'); - return true; - }); - $this->qualifiableCompletor2->complete(Argument::cetera())->shouldNotBeCalled(); - - $suggestions = $completor->complete( - TextDocumentBuilder::create('build(), - ByteOffset::fromInt(1) - ); - $this->assertCount(1, iterator_to_array($suggestions, false)); - $this->assertTrue($suggestions->getReturn()); - } - - private function create(array $completors): ChainTolerantCompletor - { - return new ChainTolerantCompletor($completors); - } -} diff --git a/lib/Completion/Tests/Unit/Bridge/TolerantParser/CompletionContextTest.php b/lib/Completion/Tests/Unit/Bridge/TolerantParser/CompletionContextTest.php deleted file mode 100644 index b67ea979ac..0000000000 --- a/lib/Completion/Tests/Unit/Bridge/TolerantParser/CompletionContextTest.php +++ /dev/null @@ -1,316 +0,0 @@ -parseString($source)->getDescendantNodeAtPosition((int)$offset); - self::assertEquals($expected, CompletionContext::expression($node)); - } - - /** - * @return Generator> - */ - public static function provideExpression(): Generator - { - yield 'not class clause' => [ - '', - false, - ]; - - yield 'not class clause 2' => [ - '', - false, - ]; - yield 'not class clause 3' => [ - '', - false, - ]; - yield 'not class clause 4' => [ - '', - false, - ]; - - yield 'not class clause on new line' => [ - "", - false, - ]; - - yield 'not class member body' => [ - '', - false, - ]; - - yield 'not class member body after property' => [ - '', - false, - ]; - yield 'not after method 1' => [ - ' }', - false, - ]; - yield 'not after method 2' => [ - ' public function baz() {}}', - false, - ]; - yield 'not after method 3' => [ - " }", - false, - ]; - - yield 'in class method body 1' => [ - ' }', - true - ]; - yield 'in class method body 2' => [ - ' } }', - true, - ]; - yield 'in foreach' => [ - ' } }', - true, - ]; - } - - #[DataProvider('provideClassMemberBody')] - public function testClassMemberBody(string $source, bool $expected): void - { - [$source, $offset] = ExtractOffset::fromSource($source); - $node = (new TolerantAstProvider())->parseString($source)->getDescendantNodeAtPosition((int)$offset); - self::assertEquals($expected, CompletionContext::classMembersBody($node)); - } - - /** - * @return Generator> - */ - public static function provideClassMemberBody(): Generator - { - yield 'property' => [ - ' }', - true - ]; - yield 'visibility 1' => [ - ' }', - true - ]; - yield 'visibility 2' => [ - ' }', - true - ]; - yield 'visibility 3' => [ - ' }', - true, - ]; - - // todo... - yield 'visibility 4' => [ - ' }', - true, - ]; - yield 'visibility 5' => [ - ' }', - false, - ]; - yield 'after class' => [ - '<>', - false, - ]; - yield 'const value' => [ - ' }', - false, - ]; - yield 'const value 2' => [ - ' }', - false, - ]; - yield 'attribute' => [ - ']public function bar(){}}', - false, - ]; - } - - #[DataProvider('provideClassClause')] - public function testClassClause(string $source, bool $expected): void - { - [$source, $offset] = ExtractOffset::fromSource($source); - $node = (new TolerantAstProvider())->parseString($source)->getDescendantNodeAtPosition((int)$offset); - self::assertEquals($expected, CompletionContext::classClause($node, ByteOffset::fromInt((int)$offset))); - } - - /** - * @return Generator> - */ - public static function provideClassClause(): Generator - { - yield 'clause' => [ - '', - true, - ]; - - yield 'clause 2' => [ - '', - true, - ]; - yield 'clause 3' => [ - '', - true, - ]; - yield 'clause 4' => [ - '', - true, - ]; - yield 'clause 5' => [ - '', - true, - ]; - } - - #[DataProvider('provideAttribute')] - public function testAttribute(string $source, bool $expected): void - { - [$source, $offset] = ExtractOffset::fromSource($source); - $node = (new TolerantAstProvider())->parseString($source)->getDescendantNodeAtPosition((int)$offset); - self::assertEquals($expected, CompletionContext::attribute($node)); - } - - /** - * @return Generator - */ - public static function provideAttribute(): Generator - { - yield 'not attribute' => [ - '', - false, - ]; - - yield 'in not mapped attribute' => [ - ']', - true, - ]; - - yield 'in not mapped attribute, empty name' => [ - ']', - true, - ]; - - yield 'in not mapped attribute, empty name of the second' => [ - ']', - true, - ]; - - yield 'in method attribute' => [ - '] public function x()', - true, - ]; - } - - #[DataProvider('provideAnonymousUse')] - public function testAnonymousUse(string $source, bool $expected): void - { - [$source, $offset] = ExtractOffset::fromSource($source); - $node = (new TolerantAstProvider())->parseString($source)->getDescendantNodeAtPosition((int)$offset); - self::assertEquals($expected, CompletionContext::anonymousUse($node)); - } - - /** - * @return Generator> - */ - public static function provideAnonymousUse(): Generator - { - yield [ - ') { ', - true, - ]; - yield [ - ') {}', - true, - ]; - yield [ - ') { ', - true, - ]; - yield [ - ') { ', - false, - ]; - yield [ - ') { ', - false, - ]; - yield [ - ') use ($foo) { ', - false, - ]; - } - - #[DataProvider('providePromotedProperty')] - public function testPromotedProperty(string $source, bool $expected): void - { - [$source, $offset] = ExtractOffset::fromSource($source); - $node = (new TolerantAstProvider())->parseString($source)->getDescendantNodeAtPosition((int)$offset); - self::assertEquals($expected, CompletionContext::promotedPropertyVisibility($node)); - } - - /** - * @return Generator> - */ - public static function providePromotedProperty(): Generator - { - yield [ - '', - true, - ]; - yield [ - ' }', - true, - ]; - yield [ - ' }', - true, - ]; - - yield [ - '', - false, - ]; - yield [ - '$a) { ', - false, - ]; - } - - #[DataProvider('provideMethodName')] - public function testMethodName(string $source, bool $expected): void - { - [$source, $offset] = ExtractOffset::fromSource($source); - $node = (new TolerantAstProvider())->parseString($source)->getDescendantNodeAtPosition((int)$offset); - self::assertEquals($expected, CompletionContext::methodName($node)); - } - - /** - * @return Generator> - */ - public static function provideMethodName(): Generator - { - yield [ - '', - false, - ]; - yield [ - '', - true, - ]; - } -} diff --git a/lib/Completion/Tests/Unit/Bridge/TolerantParser/Helper/NodeQueryTest.php b/lib/Completion/Tests/Unit/Bridge/TolerantParser/Helper/NodeQueryTest.php deleted file mode 100644 index ba18780198..0000000000 --- a/lib/Completion/Tests/Unit/Bridge/TolerantParser/Helper/NodeQueryTest.php +++ /dev/null @@ -1,68 +0,0 @@ -parser = new TolerantAstProvider(); - } - - #[DataProvider('provideFirstAncestorVia')] - public function testFirstAncestorVia(string $source, Closure $assertion): void - { - $node = $this->nodeFromSource($source); - $assertion($node); - } - - /** - * @return Generator - */ - public static function provideFirstAncestorVia(): Generator - { - yield [ - '));', - function (Node $node): void { - $node = NodeQuery::firstAncestorVia($node, ObjectCreationExpression::class, [ - ArgumentExpression::class, - ArgumentExpressionList::class, - ]); - self::assertNotNull($node); - self::assertInstanceOf(ObjectCreationExpression::class, $node); - self::assertEquals('Foobar', $node->classTypeDesignator->getText()); - } - ]; - - yield [ - ', [])));', - function (Node $node): void { - $node = NodeQuery::firstAncestorVia($node, ObjectCreationExpression::class, [ - ArgumentExpression::class, - ArgumentExpressionList::class, - ]); - self::assertNull($node); - } - ]; - } - - private function nodeFromSource(string $source): Node - { - [$source, $offset] = ExtractOffset::fromSource($source); - return $this->parser->parseString($source)->getDescendantNodeAtPosition($offset); - } -} diff --git a/lib/Completion/Tests/Unit/Bridge/TolerantParser/LimitingCompletorTest.php b/lib/Completion/Tests/Unit/Bridge/TolerantParser/LimitingCompletorTest.php deleted file mode 100644 index 15e71275cb..0000000000 --- a/lib/Completion/Tests/Unit/Bridge/TolerantParser/LimitingCompletorTest.php +++ /dev/null @@ -1,166 +0,0 @@ -innerCompletor = $this->prophesize(TolerantCompletor::class); - $this->node = $this->prophesize(Node::class); - } - - public function testNoSuggestions(): void - { - $this->innerCompletor->complete( - $this->node->reveal(), - $this->textDocument(self::EXAMPLE_SOURCE), - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - )->will(function () { - return true; - yield; - }); - - $suggestions = $this->create(10)->complete( - $this->node->reveal(), - $this->textDocument(self::EXAMPLE_SOURCE), - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - ); - - $this->assertCount(0, iterator_to_array($suggestions, false)); - $this->assertTrue($suggestions->getReturn()); - } - - public function testSomeSuggestions(): void - { - $suggestions = [ - $this->suggestion('foobar'), - $this->suggestion('barfoo'), - $this->suggestion('carfoo'), - ]; - - $this->primeInnerCompletor($suggestions); - - $suggestions = $this->create(10)->complete( - $this->node->reveal(), - $this->textDocument(self::EXAMPLE_SOURCE), - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - ); - - $this->assertCount(3, iterator_to_array($suggestions, false)); - $this->assertTrue($suggestions->getReturn()); - } - - public function testAppliesLimit(): void - { - $suggestions = [ - $this->suggestion('foobar'), - $this->suggestion('barfoo'), - $this->suggestion('carfoo'), - ]; - - $this->primeInnerCompletor($suggestions); - - $suggestions = $this->create(2)->complete( - $this->node->reveal(), - $this->textDocument(self::EXAMPLE_SOURCE), - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - ); - - $this->assertCount(2, iterator_to_array($suggestions, false)); - $this->assertFalse($suggestions->getReturn()); - } - - public function testIsNotCompleteWhenInnerCompleterIsNotComplete(): void - { - $suggestions = [ - $this->suggestion('foobar'), - $this->suggestion('barfoo'), - $this->suggestion('carfoo'), - ]; - - $this->primeInnerCompletor($suggestions, false); - - $suggestions = $this->create(10)->complete( - $this->node->reveal(), - $this->textDocument(self::EXAMPLE_SOURCE), - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - ); - - $this->assertCount(3, iterator_to_array($suggestions, false)); - $this->assertFalse($suggestions->getReturn()); - } - - public function testQualifiesNonQualifiableCompletors(): void - { - $completor = $this->create(10); - $node = $this->prophesize(Node::class); - - $qualified = $completor->qualifier()->couldComplete($node->reveal()); - $this->assertSame($node->reveal(), $qualified); - } - - public function testPassesThroughToInnerQualifier(): void - { - $node = $this->prophesize(Node::class); - $this->innerCompletor->willImplement(TolerantQualifiable::class); - $this->innerCompletor->qualifier()->willReturn(new AlwaysQualfifier())->shouldBeCalled(); - $completor = $this->create(10); - - $qualified = $completor->qualifier()->couldComplete($node->reveal()); - $this->assertSame($node->reveal(), $qualified); - } - - private function create(int $limit): LimitingCompletor - { - return new LimitingCompletor($this->innerCompletor->reveal(), $limit); - } - - private function suggestion(string $name): Suggestion - { - return Suggestion::create($name); - } - - /** - * @param array $suggestions - */ - private function primeInnerCompletor(array $suggestions, bool $isComplete = true): void - { - $this->innerCompletor->complete( - $this->node->reveal(), - $this->textDocument(self::EXAMPLE_SOURCE), - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - )->will(function () use ($suggestions, $isComplete) { - foreach ($suggestions as $suggestion) { - yield $suggestion; - } - return $isComplete; - }); - } - - private function textDocument(string $document): TextDocument - { - return TextDocumentBuilder::create($document)->build(); - } -} diff --git a/lib/Completion/Tests/Unit/Bridge/TolerantParser/NodeAtCursorProviderTest.php b/lib/Completion/Tests/Unit/Bridge/TolerantParser/NodeAtCursorProviderTest.php deleted file mode 100644 index 1bf5ab3c36..0000000000 --- a/lib/Completion/Tests/Unit/Bridge/TolerantParser/NodeAtCursorProviderTest.php +++ /dev/null @@ -1,142 +0,0 @@ -get( - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt($offset), - ); - $assertion = $assertion->bindTo($this); - $assertion($node); - } - - public static function provideProvide(): Generator - { - yield [ - ' }', - function (Node $node): void { - self::assertInstanceOf(CompoundStatementNode::class, $node); - self::assertInstanceOf(MethodDeclaration::class, $node->parent); - self::assertSame( - '__c', - $node->parent->name?->getText((string)$node->getFileContents()) - ); - } - ]; - yield [ - <<<'EOT' - bb<>new Foobar(); - } - - public function bbb() {} - public function ccc() {} - } - - EOT, - function (Node $node): void { - self::assertInstanceOf(MemberAccessExpression::class, $node); - self::assertEquals('bb', $node->memberName->getText($node->getFileContents())); - } - ]; - yield [ - '$bar)', - function (Node $node): void { - self::assertInstanceOf(Variable::class, $node); - } - ]; - - yield [ - <<<'PHP' - logger-><> - continue; - } - return 'foobar'; - } - } - PHP, - function (Node $node): void { - self::assertInstanceOf(MemberAccessExpression::class, $node); - self::assertEquals('', $node->memberName->getText($node->getFileContents())); - } - ]; - - yield [ - <<<'PHP' - <> - PHP, - function (Node $node): void { - self::assertInstanceOf(MemberAccessExpression::class, $node); - } - ]; - - yield [ - <<<'PHP' - } - PHP, - function (Node $node): void { - self::assertInstanceOf(CaseStatementNode::class, $node); - } - ]; - } - - public function testDoesNotCorruptOriginalAst(): void - { - $source = <<<'PHP' - he<>llo; - PHP; - - [$source, $offset] = ExtractOffset::fromSource($source); - $provider = new CachedAstProvider(new TolerantAstProvider()); - $document = TextDocumentBuilder::create($source)->build(); - - $node = (new NodeAtCursorProvider($provider))->get( - $document, - ByteOffset::fromInt($offset), - ); - - self::assertInstanceOf(MemberAccessExpression::class, $node); - - self::assertEquals('he', $node->memberName->getText($source)); - - $node = $provider->get($document)->getDescendantNodeAtPosition($offset); - self::assertInstanceOf(MemberAccessExpression::class, $node); - self::assertEquals('hello', $node->memberName->getText($source)); - } -} diff --git a/lib/Completion/Tests/Unit/Bridge/WorseReflection/Completor/ContextSensitiveCompletorTest.php b/lib/Completion/Tests/Unit/Bridge/WorseReflection/Completor/ContextSensitiveCompletorTest.php deleted file mode 100644 index 4009f4c1be..0000000000 --- a/lib/Completion/Tests/Unit/Bridge/WorseReflection/Completor/ContextSensitiveCompletorTest.php +++ /dev/null @@ -1,323 +0,0 @@ -parseString($source); - $node = $node->getDescendantNodeAtPosition($offset); - $reflector = ReflectorBuilder::create()->addSource($source)->build(); - $inner = new TolerantArrayCompletor(array_map( - fn (string $name) => Suggestion::createWithOptions($name, ['name_import' => $name]), - $suggestions - )); - $suggestions = iterator_to_array((new ContextSensitiveCompletor( - $inner, - $reflector - ))->complete( - $node, - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt($offset) - )); - self::assertEquals($expected, array_map( - fn (Suggestion $suggestion) => $suggestion->fqn(), - $suggestions - )); - } - /** - * @return Generator,string,array}> - */ - public static function provideComplete(): Generator - { - yield 'method call' => [ - [ - 'Bar\Foo', - 'Bar\Obj', - ], - <<<'EOT' - bar(new <>) - EOT, - [ - 'Bar\Obj', - ], - ]; - yield 'static call returns all' => [ - [ - 'Bar\Foo', - 'Bar\Obj', - ], - <<<'EOT' - bar(F<>) - EOT, - [ - 'Bar\Foo', - 'Bar\Obj', - ], - ]; - yield 'namespaced method call' => [ - [ - 'Bar\Foo', - 'Bar\Obj', - ], - <<<'EOT' - bar(new <>) - EOT, - [ - 'Bar\Obj', - ], - ]; - yield 'no namespace' => [ - [ - 'Foo', - 'Obj', - ], - <<<'EOT' - bar(new <>) - EOT, - [ - 'Obj', - ], - ]; - yield 'partial' => [ - [ - 'Foo', - 'Obj', - ], - <<<'EOT' - bar(new O<> - EOT, - [ - 'Obj', - ], - ]; - yield 'no type hint' => [ - [ - 'Foo', - 'Obj', - ], - <<<'EOT' - bar(new O<> - EOT, - [ - 'Foo', - 'Obj', - ], - ]; - yield '2nd arg' => [ - [ - 'Obj', - 'Baz', - ], - <<<'EOT' - bar(Obj::new(), new <>) - EOT, - [ - 'Baz', - ], - ]; - - yield '2nd arg partial' => [ - [ - 'Obj', - 'Baz', - ], - <<<'EOT' - bar(Obj::new(),new B<> - EOT, - [ - 'Baz', - ], - ]; - yield 'variadic' => [ - [ - 'Obj', - 'Baz', - ], - <<<'EOT' - bar(Obj::new(),new B<> - EOT, - [ - 'Baz', - ], - ]; - yield 'enum' => [ - [ - 'Obj', - 'Baz', - ], - <<<'EOT' - bar(new O<> - EOT, - [ - 'Obj', - ], - ]; - yield 'on static call' => [ - [ - 'Obj', - 'Baz', - ], - <<<'EOT' - - EOT, - [ - 'Obj', - ], - ]; - yield 'on variadic' => [ - [ - 'Obj', - 'Baz', - ], - <<<'EOT' - ) - EOT, - [ - 'Obj', - ], - ]; - yield 'unresolvable method' => [ - [ - 'Obj', - 'Baz', - ], - <<<'EOT' - bar(new O<> - EOT, - [ - 'Obj', - 'Baz', - ], - ]; - yield 'constructor argument' => [ - [ - 'Object1', - 'Object2', - ], - <<<'EOT' - ); - EOT, - [ - 'Object1', - ], - ]; - - yield 'within closure' => [ - [ - 'Object1', - 'Object2', - ], - <<<'EOT' - - }); - EOT, - [ - 'Object1', - 'Object2', - ], - ]; - } -} diff --git a/lib/Completion/Tests/Unit/Bridge/WorseReflection/Formatter/FunctionLikeSnippetFormatterTest.php b/lib/Completion/Tests/Unit/Bridge/WorseReflection/Formatter/FunctionLikeSnippetFormatterTest.php deleted file mode 100644 index 6535b41ba9..0000000000 --- a/lib/Completion/Tests/Unit/Bridge/WorseReflection/Formatter/FunctionLikeSnippetFormatterTest.php +++ /dev/null @@ -1,97 +0,0 @@ -assertEquals( - $expected, - $this->format($reflection) - ); - } - - public function provideReflectionToFormat(): iterable - { - yield 'Function without parameters' => [ - $this->reflectFunction('func()'), - 'func()', - ]; - - yield 'Function with mandatory parameters' => [ - $this->reflectFunction('func(string $test, int $i)'), - 'func(${1:\$test}, ${2:\$i})${0}' - ]; - - yield 'Function with mandatory and optional parameters' => [ - $this->reflectFunction('func(string $test, int $i = 1)'), - 'func(${1:\$test})${0}' - ]; - - yield 'Function with only optional parameters' => [ - $this->reflectFunction('func(?string $test = null, int $i = 1)'), - 'func(${1})${0}' - ]; - - yield 'Method without parameters' => [ - $this->reflectMethod('method()'), - 'method()' - ]; - - yield 'Method with mandatory parameters' => [ - $this->reflectMethod('method(string $test, int $i)'), - 'method(${1:\$test}, ${2:\$i})${0}' - ]; - - yield 'Method with mandatory and optional parameters' => [ - $this->reflectMethod('method(string $test, int $i = 1)'), - 'method(${1:\$test})${0}' - ]; - - yield 'Method with only optional parameters' => [ - $this->reflectMethod('method(?string $test = null, int $i = 1)'), - 'method(${1})${0}' - ]; - } - - private function format(ReflectionFunctionLike $reflection): string - { - return (new FunctionLikeSnippetFormatter()) - ->format(new ObjectFormatter([ - new ParametersSnippetFormatter() - ]), $reflection); - } - - private function reflectFunction(string $functionAsString): ReflectionFunction - { - return ReflectorBuilder::create() - ->build() - ->reflectFunctionsIn(TextDocumentBuilder::fromUnknown(\sprintf('first() - ; - } - - private function reflectMethod(string $methodAsString): ReflectionMethod - { - return ReflectorBuilder::create() - ->build() - ->reflectClassesIn(TextDocumentBuilder::fromUnknown(\sprintf('first() - ->methods() - ->first() - ; - } -} diff --git a/lib/Completion/Tests/Unit/CompletorTest.php b/lib/Completion/Tests/Unit/CompletorTest.php deleted file mode 100644 index 7e49d30f1d..0000000000 --- a/lib/Completion/Tests/Unit/CompletorTest.php +++ /dev/null @@ -1,148 +0,0 @@ - - */ - private ObjectProphecy $completor1; - - protected function setUp(): void - { - $this->completor1 = $this->prophesize(Completor::class); - } - - public function testEmptyGeneratorWithNoCompletors(): void - { - $completor = $this->create([]); - $suggestions = $completor->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)); - - $this->assertCount(0, iterator_to_array($suggestions, false)); - $this->assertTrue($suggestions->getReturn()); - } - - public function testReturnsEmptyGeneratorWhenCompletorCouldNotComplete(): void - { - $completor = $this->create([ - $this->completor1->reveal() - ]); - - $this->completor1->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)) - ->shouldBeCalled() - ->will(function () { - yield from []; - return true; - }); - - $suggestions = $completor->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)); - - $this->assertCount(0, iterator_to_array($suggestions, false)); - $this->assertTrue($suggestions->getReturn()); - } - - public function testReturnsSuggestionsFromCompletor(): void - { - $expected = [ - Suggestion::create('foobar') - ]; - - $completor = $this->create([ - $this->completor1->reveal() - ]); - - $this->completor1->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)) - ->shouldBeCalled() - ->will(function () use ($expected) { - yield from $expected; - return true; - }); - - $suggestions = $completor->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)); - - $this->assertEquals($expected, iterator_to_array($suggestions)); - $this->assertTrue($suggestions->getReturn()); - } - - public function testIsCompleteIfAllCompeltorsReturnedEverything(): void - { - $otherCompleter = $this->prophesize(Completor::class); - $completor = $this->create([ - $this->completor1->reveal(), - $otherCompleter->reveal() - ]); - - $this->completor1->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)) - ->shouldBeCalled() - ->will(function () { - yield from []; - return true; - }); - - $otherCompleter->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)) - ->shouldBeCalled() - ->will(function () { - yield from []; - return true; - }); - - $suggestions = $completor->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)); - - $this->assertTrue($suggestions->getReturn()); - } - - public function testIsNotCompleteIfAllCompeltorsDoesNotReturnEverything(): void - { - $otherCompleter = $this->prophesize(Completor::class); - $completor = $this->create([ - $this->completor1->reveal(), - $otherCompleter->reveal() - ]); - - $this->completor1->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)) - ->shouldBeCalled() - ->will(function () { - yield from []; - return false; - }); - - $otherCompleter->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)) - ->shouldBeCalled() - ->will(function () { - yield from []; - return true; - }); - - $suggestions = $completor->complete($this->textDocument(self::EXAMPLE_SOURCE), ByteOffset::fromInt(self::EXAMPLE_OFFSET)); - - $this->assertFalse($suggestions->getReturn()); - } - - /** - * @param Completor[] $completors - */ - public function create(array $completors): ChainCompletor - { - return new ChainCompletor($completors); - } - - private function textDocument(string $document): TextDocument - { - return TextDocumentBuilder::create($document)->build(); - } -} diff --git a/lib/Completion/Tests/Unit/Core/ChainSignatureHelperTest.php b/lib/Completion/Tests/Unit/Core/ChainSignatureHelperTest.php deleted file mode 100644 index 06df0be5e7..0000000000 --- a/lib/Completion/Tests/Unit/Core/ChainSignatureHelperTest.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ - private ObjectProphecy $logger; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $helper1; - - private TextDocument $document; - - private ByteOffset $offset; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $help; - - protected function setUp(): void - { - $this->logger = $this->prophesize(LoggerInterface::class); - $this->helper1 = $this->prophesize(SignatureHelper::class); - - $this->document = TextDocumentBuilder::create('foo')->uri('file:///foo')->language('php')->build(); - $this->offset = ByteOffset::fromInt(1); - $this->help = $this->prophesize(SignatureHelp::class); - } - - public function testNoHelpersThrowsException(): void - { - $this->expectException(CouldNotHelpWithSignature::class); - $this->create([])->signatureHelp($this->document, $this->offset); - } - - public function testHelperCouldNotHelp(): void - { - $this->expectException(CouldNotHelpWithSignature::class); - $this->helper1->signatureHelp($this->document, $this->offset)->willThrow(new CouldNotHelpWithSignature('Foobar')); - $this->logger->debug('Could not provide signature: "Foobar"')->shouldBeCalled(); - - $this->create([ - $this->helper1->reveal(), - ])->signatureHelp($this->document, $this->offset); - } - - public function testHelpersSignature(): void - { - $this->helper1->signatureHelp($this->document, $this->offset)->willReturn($this->help->reveal()); - - $help = $this->create([ - $this->helper1->reveal(), - ])->signatureHelp($this->document, $this->offset); - - $this->assertSame($this->help->reveal(), $help); - } - - private function create(array $helpers) - { - return new ChainSignatureHelper($this->logger->reveal(), $helpers); - } -} diff --git a/lib/Completion/Tests/Unit/Core/Completor/DedupeCompletorTest.php b/lib/Completion/Tests/Unit/Core/Completor/DedupeCompletorTest.php deleted file mode 100644 index f0c7f90487..0000000000 --- a/lib/Completion/Tests/Unit/Core/Completor/DedupeCompletorTest.php +++ /dev/null @@ -1,81 +0,0 @@ -build(); - $offset = ByteOffset::fromInt(10); - - $inner = new ArrayCompletor([ - Suggestion::create('foobar'), - Suggestion::create('barfoo'), - Suggestion::create('foobar'), - ]); - $dedupe = new DedupeCompletor($inner); - $suggestions = $dedupe->complete($source, $offset); - self::assertEquals([ - Suggestion::create('foobar'), - Suggestion::create('barfoo'), - ], iterator_to_array($suggestions)); - $this->assertTrue($suggestions->getReturn()); - } - - public function testDedupeWithSuggestionsOfDifferentTypes(): void - { - $source = TextDocumentBuilder::create('foobar')->build(); - $offset = ByteOffset::fromInt(10); - - $inner = new ArrayCompletor([ - Suggestion::createWithOptions('foobar', ['type' => Suggestion::TYPE_ENUM]), - Suggestion::create('barfoo'), - Suggestion::create('foobar'), - ]); - $dedupe = new DedupeCompletor($inner); - $suggestions = $dedupe->complete($source, $offset); - self::assertEquals([ - Suggestion::createWithOptions('foobar', ['type' => Suggestion::TYPE_ENUM]), - Suggestion::create('barfoo'), - Suggestion::create('foobar'), - ], iterator_to_array($suggestions)); - $this->assertTrue($suggestions->getReturn()); - } - - public function testDeduplicatesWithFqn(): void - { - $source = TextDocumentBuilder::create('foobar')->build(); - $offset = ByteOffset::fromInt(10); - - $inner = new ArrayCompletor([ - Suggestion::create('foobar'), - Suggestion::createWithOptions('barfoo', [ - 'name_import' => 'baf', - ]), - Suggestion::create('foobar'), - Suggestion::createWithOptions('barfoo', [ - 'name_import' => 'bosh', - ]), - ]); - $dedupe = new DedupeCompletor($inner, true); - $suggestions = $dedupe->complete($source, $offset); - self::assertEquals([ - Suggestion::create('foobar'), - Suggestion::createWithOptions('barfoo', [ - 'name_import' => 'baf', - ]), - Suggestion::createWithOptions('barfoo', [ - 'name_import' => 'bosh', - ]), - ], iterator_to_array($suggestions)); - $this->assertTrue($suggestions->getReturn()); - } -} diff --git a/lib/Completion/Tests/Unit/Core/Completor/LimitingCompletorTest.php b/lib/Completion/Tests/Unit/Core/Completor/LimitingCompletorTest.php deleted file mode 100644 index 5b0840ca61..0000000000 --- a/lib/Completion/Tests/Unit/Core/Completor/LimitingCompletorTest.php +++ /dev/null @@ -1,52 +0,0 @@ -build(); - $offset = ByteOffset::fromInt(10); - - $inner = new ArrayCompletor([ - Suggestion::create('foobar'), - Suggestion::create('foobar'), - Suggestion::create('foobar'), - Suggestion::create('foobar'), - Suggestion::create('foobar'), - ]); - $dedupe = new LimitingCompletor($inner, 2); - $suggestions = $dedupe->complete($source, $offset); - self::assertEquals([ - Suggestion::create('foobar'), - Suggestion::create('foobar'), - ], iterator_to_array($suggestions)); - $this->assertFalse($suggestions->getReturn()); - } - - public function testDoesNotLimitsResults(): void - { - $source = TextDocumentBuilder::create('foobar')->build(); - $offset = ByteOffset::fromInt(10); - - $inner = new ArrayCompletor([ - Suggestion::create('foobar'), - Suggestion::create('foobar'), - ]); - $dedupe = new LimitingCompletor($inner, 2); - $suggestions = $dedupe->complete($source, $offset); - self::assertEquals([ - Suggestion::create('foobar'), - Suggestion::create('foobar'), - ], iterator_to_array($suggestions)); - $this->assertTrue($suggestions->getReturn()); - } -} diff --git a/lib/Completion/Tests/Unit/Core/DocumentPrioritizer/ProximityPrioritizerTest.php b/lib/Completion/Tests/Unit/Core/DocumentPrioritizer/ProximityPrioritizerTest.php deleted file mode 100644 index 35fee1f01d..0000000000 --- a/lib/Completion/Tests/Unit/Core/DocumentPrioritizer/ProximityPrioritizerTest.php +++ /dev/null @@ -1,76 +0,0 @@ -priority($one, $two)); - } - - /** - * @return Generator - */ - public static function providePriority(): Generator - { - yield [ - null, - null, - Suggestion::PRIORITY_LOW - ]; - - yield [ - '/home/daniel/phpactor/vendor/symfony/foobar/lib/ClassOne.php', - '/home/daniel/phpactor/lib/ClassOne.php', - 218 - ]; - - yield 'further 1' => [ - '/home/daniel/phpactor/vendor/symfony/foobar/lib/ClassOne.php', - '/home/daniel/phpactor/lib/Further/Away/ClassOne.php', - 198 - ]; - - yield 'closer 1' => [ - '/home/daniel/phpactor/lib/ClassTwo.php', - '/home/daniel/phpactor/lib/Further/Away/ClassOne.php', - 223 - ]; - - yield 'closer 2' => [ - '/home/daniel/phpactor/lib/ClassTwo.php', - '/home/daniel/phpactor/lib/Away/ClassTwo.php', - 212 - ]; - - yield [ - '/home/daniel/phpactor/vendor/symfony/foobar/lib/ClassOne.php', - '/home/daniel/phpactor/vendor/symfony/foobar/lib/ClassOne.php', - Suggestion::PRIORITY_MEDIUM // exact match gives baseline of medium priority (127) - ]; - - yield 'closer 3' => [ - '/project/pipeline/Survey/GitSurvey.php', - '/project/pipeline/Task/ComposerBumpVersionIfPresentTask.php', - 191 - ]; - - yield 'further 3' => [ - '/project/vendor/dantleech/maestro/src/Composer/Extension/ComposerExtension.php', - '/project/pipeline/Survey/GitSurvey.php', - 216 - ]; - } -} diff --git a/lib/Completion/Tests/Unit/Core/DocumentPrioritizer/SimilarityResultPrioritizerTest.php b/lib/Completion/Tests/Unit/Core/DocumentPrioritizer/SimilarityResultPrioritizerTest.php deleted file mode 100644 index e6f42db18d..0000000000 --- a/lib/Completion/Tests/Unit/Core/DocumentPrioritizer/SimilarityResultPrioritizerTest.php +++ /dev/null @@ -1,58 +0,0 @@ -priority($one, $two)); - } - - /** - * @return Generator - */ - public static function providePriority(): Generator - { - yield [ - '/home/daniel/phpactor/vendor/symfony/foobar/lib/ClassOne.php', - '/home/daniel/phpactor/lib/ClassOne.php', - 169 - ]; - - yield 'further 1' => [ - '/home/daniel/phpactor/vendor/symfony/foobar/lib/ClassOne.php', - '/home/daniel/phpactor/lib/Further/Away/ClassOne.php', - 169 - ]; - - yield 'closer 1' => [ - '/home/daniel/phpactor/lib/ClassTwo.php', - '/home/daniel/phpactor/lib/Further/Away/ClassOne.php', - 175 - ]; - - yield 'closer 2' => [ - '/home/daniel/phpactor/lib/ClassTwo.php', - '/home/daniel/phpactor/lib/Further/Away/ClassTwo.php', - 159 - ]; - - yield [ - '/home/daniel/phpactor/vendor/symfony/foobar/lib/ClassOne.php', - '/home/daniel/phpactor/vendor/symfony/foobar/lib/ClassOne.php', - Suggestion::PRIORITY_MEDIUM // exact match gives baseline of medium priority (127) - ]; - } -} diff --git a/lib/Completion/Tests/Unit/Core/LabelFormatter/HelpfulLabelFormatterTest.php b/lib/Completion/Tests/Unit/Core/LabelFormatter/HelpfulLabelFormatterTest.php deleted file mode 100644 index 5b5a6ace71..0000000000 --- a/lib/Completion/Tests/Unit/Core/LabelFormatter/HelpfulLabelFormatterTest.php +++ /dev/null @@ -1,79 +0,0 @@ - $seen - */ - #[DataProvider('provideFormat')] - public function testFormat(string $name, array $seen, string $expected): void - { - $formatter = new HelpfulLabelFormatter(); - self::assertEquals($expected, $formatter->format($name, $seen)); - } - - /** - * @return Generator,string}> - */ - public static function provideFormat(): Generator - { - yield [ - 'Request', - [], - 'Request' - ]; - yield [ - 'Request', - [ - 'Request' => true, - ], - 'Request' - ]; - yield [ - 'Foo\Request', - [ - 'Request' => true, - ], - 'Request (Foo)' - ]; - yield [ - 'PhpParser\Node', - [ - 'Node' => true, - 'Node (Microsoft)' => true, - 'Node (Phpactor)' => true, - ], - 'Node (PhpParser)' - ]; - yield [ - 'Foo\Bar\Node', - [ - 'Node (Foo)' => true, - ], - 'Node (Foo\Bar)' - ]; - yield [ - 'Foo\Bar\Baz\Node', - [ - 'Node (Foo)' => true, - 'Node (Foo\Bar)' => true, - ], - 'Node (Foo\Bar\Baz)' - ]; - yield 'invalid case for 2 identically named classes' => [ - 'Foo\Bar\Node', - [ - 'Node (Foo)' => true, - 'Node (Foo\Bar)' => true, - ], - 'Node' - ]; - } -} diff --git a/lib/Completion/Tests/Unit/Core/SignatureInformationTest.php b/lib/Completion/Tests/Unit/Core/SignatureInformationTest.php deleted file mode 100644 index a1fbf62ffd..0000000000 --- a/lib/Completion/Tests/Unit/Core/SignatureInformationTest.php +++ /dev/null @@ -1,15 +0,0 @@ -parameters()); - } -} diff --git a/lib/Completion/Tests/Unit/Core/SuggestionTest.php b/lib/Completion/Tests/Unit/Core/SuggestionTest.php deleted file mode 100644 index f803e7d61f..0000000000 --- a/lib/Completion/Tests/Unit/Core/SuggestionTest.php +++ /dev/null @@ -1,71 +0,0 @@ -expectException(RuntimeException::class); - $this->expectExceptionMessage('Invalid options for suggestion: "foobar" valid options: "short_description", "documentation", "type"'); - - Suggestion::createWithOptions('foobar', ['foobar' => 'barfoo']); - } - - public function testCanBeCreatedWithOptionsAndProvidesAccessors(): void - { - $suggestion = Suggestion::createWithOptions('hello', [ - 'type' => 'class', - 'short_description' => 'Foobar', - 'class_import' => 'Namespace\\Foobar', - 'label' => 'hallo', - ]); - - $this->assertEquals('class', $suggestion->type()); - $this->assertEquals('hello', $suggestion->name()); - $this->assertEquals('hallo', $suggestion->label()); - $this->assertEquals('Foobar', $suggestion->shortDescription()); - $this->assertEquals('Namespace\\Foobar', $suggestion->nameImport()); - $this->assertEquals('Namespace\\Foobar', $suggestion->fqn()); - } - - public function testDefaults(): void - { - $suggestion = Suggestion::create('hello'); - $this->assertEquals('hello', $suggestion->name()); - $this->assertEquals('hello', $suggestion->label()); - } - - public function testCastsToArray(): void - { - $suggestion = Suggestion::createWithOptions('hello', [ - 'type' => Suggestion::TYPE_CLASS, - 'short_description' => 'Foobar', - 'class_import' => 'Namespace\\Foobar', - 'documentation' => 'foo', - 'label' => 'hallo', - 'fqn' => null, - 'range' => Range::fromStartAndEnd(1, 2), - 'snippet' => null, - ]); - - $this->assertEquals([ - 'type' => 'class', - 'short_description' => 'Foobar', - 'documentation' => 'foo', - 'class_import' => 'Namespace\\Foobar', - 'name' => 'hello', - 'label' => 'hallo', - 'range' => [1, 2], - 'info' => '', - 'snippet' => null, - 'name_import' => 'Namespace\\Foobar', - 'fqn' => null, - ], $suggestion->toArray()); - } -} diff --git a/lib/Completion/Tests/Unit/Core/TypedCompletorRegistryTest.php b/lib/Completion/Tests/Unit/Core/TypedCompletorRegistryTest.php deleted file mode 100644 index 539af0cae6..0000000000 --- a/lib/Completion/Tests/Unit/Core/TypedCompletorRegistryTest.php +++ /dev/null @@ -1,50 +0,0 @@ -prophesize(Completor::class); - $registry = new TypedCompletorRegistry([ - 'cucumber' => $completor->reveal(), - ]); - $completorForType = $registry->completorForType('cucumber'); - - $completor->complete( - TextDocumentBuilder::create('foo')->build(), - ByteOffset::fromInt(123) - )->shouldBeCalled(); - - $this->assertSame($completor->reveal(), $completorForType); - - iterator_to_array($completorForType->complete( - TextDocumentBuilder::create('foo')->build(), - ByteOffset::fromInt(123) - )); - } - - public function testEmptyChainCompletorWhenTypeNotConfigured(): void - { - $registry = new TypedCompletorRegistry([ - ]); - $completorForType = $registry->completorForType('cucumber'); - - $this->assertInstanceOf(ChainCompletor::class, $completorForType); - - iterator_to_array($completorForType->complete( - TextDocumentBuilder::create('foo')->build(), - ByteOffset::fromInt(123) - )); - } -} diff --git a/lib/Completion/Tests/Unit/Core/Util/OffsetHelperTest.php b/lib/Completion/Tests/Unit/Core/Util/OffsetHelperTest.php deleted file mode 100644 index 23603e5fd8..0000000000 --- a/lib/Completion/Tests/Unit/Core/Util/OffsetHelperTest.php +++ /dev/null @@ -1,64 +0,0 @@ -assertEquals( - $expectedOffset, - strlen(mb_substr($source, 0, $characterOffset)), - 'Character offset corresponds to correct byte offset' - ); - } - - /** - * @return Generator - */ - public static function provideReturnsLastNonWhitespaceOffset(): Generator - { - yield 'empty string' => [ - '', - ]; - - yield 'no extra whitespace' => [ - 'foobar<>', - ]; - - yield 'extra whitespace' => [ - 'foobar<> ', - ]; - - yield 'extra newline' => [ - 'foobar<>' . "\n", - ]; - - yield 'extra windows newline' => [ - "foobar<>\r\n", - ]; - - yield 'multi-byte chars' => [ - "fȯøbar<>\r\n", - ]; - - yield 'extra tab' => [ - "foobar<>\t", - ]; - - yield 'long string (about 6MB)' => [ - str_repeat('foobar', 2**20) . "<>\t", - 'this is actually unused' - ]; - } -} diff --git a/lib/Completion/Tests/Unit/Core/Util/Snippet/PlaceholderTest.php b/lib/Completion/Tests/Unit/Core/Util/Snippet/PlaceholderTest.php deleted file mode 100644 index 547fcd9857..0000000000 --- a/lib/Completion/Tests/Unit/Core/Util/Snippet/PlaceholderTest.php +++ /dev/null @@ -1,40 +0,0 @@ -assertEquals( - \sprintf('${%d%s}', $position, $text ? ":$text" : null), - Placeholder::raw($position, $text) - ); - } - - #[DataProvider('providePlaceholders')] - public function testEscape(int $position, ?string $text, string $expected): void - { - $this->assertEquals( - $expected, - Placeholder::escape($position, $text) - ); - } - /** - * @return Generator - */ - public static function providePlaceholders(): Generator - { - yield 'no text' => [1, null, '${1}']; - yield 'with text' => [3, 'default', '${3:default}']; - yield 'with a $' => [3, '$default', '${3:\$default}']; - yield 'with a \\' => [3, '\default', '${3:\\\default}']; - yield 'with a }' => [3, 'default}', '${3:default\}}']; - } -} diff --git a/lib/ComposerInspector/ComposerInspector.php b/lib/ComposerInspector/ComposerInspector.php deleted file mode 100644 index f7e4b3cd97..0000000000 --- a/lib/ComposerInspector/ComposerInspector.php +++ /dev/null @@ -1,87 +0,0 @@ - - */ - private array $packages = []; - - private string $vendorBinDir = self::DEFAULT_BIN_DIR; - - private bool $loaded = false; - - public function __construct( - private string $lockFile, - private string $composerFile, - ) { - } - - public function package(string $name): ?Package - { - $this->readFiles(); - if (!isset($this->packages[$name])) { - return null; - } - - return $this->packages[$name]; - } - - public function binDir(): string - { - $this->readFiles(); - return $this->vendorBinDir; - } - - private function readFiles(): void - { - if ($this->loaded) { - return; - } - - /** @var array{ - * packages?: array, - * "packages-dev"?: array - * } $lockContent - */ - $lockContent = $this->parseFile($this->lockFile); - foreach ($lockContent['packages'] ?? [] as $pkg) { - $this->packages[(string)$pkg['name']] = $this->forVersion($pkg['name'], $pkg['version'], false); - } - foreach ($lockContent['packages-dev'] ?? [] as $pkg) { - $this->packages[(string)$pkg['name']] = $this->forVersion($pkg['name'], $pkg['version'], true); - } - - /** @var array{"bin-dir"?:string} $composerContent */ - $composerContent = $this->parseFile($this->composerFile); - $this->vendorBinDir = $composerContent['bin-dir'] ?? self::DEFAULT_BIN_DIR; - - $this->loaded = true; - } - - /** @return array */ - private function parseFile(string $fileName): array - { - $contents = @file_get_contents($fileName); - if (false === $contents) { - return []; - } - - $result = json_decode($contents, associative: true); - - if (!is_array($result)) { - return []; - } - - return $result; - } - - private function forVersion(string $name, string $version, bool $isDev): Package - { - return new Package($name, $version, $isDev); - } -} diff --git a/lib/ComposerInspector/Package.php b/lib/ComposerInspector/Package.php deleted file mode 100644 index f83eb7410a..0000000000 --- a/lib/ComposerInspector/Package.php +++ /dev/null @@ -1,13 +0,0 @@ -workspace = new Workspace(__DIR__ . '/Workspace'); - } - - public function testReturnsPackage(): void - { - $this->putComposerLock('{"packages":[{"name":"phpstan/phpstan", "version": "^1.0"}]}'); - $package = $this->inspector()->package('phpstan/phpstan'); - self::assertNotNull($package); - self::assertEquals('phpstan/phpstan', $package->name); - self::assertEquals('^1.0', $package->version); - self::assertFalse($package->isDev); - } - - public function testReturnsDevPackage(): void - { - $this->putComposerLock('{"packages-dev":[{"name":"phpstan/phpstan", "version": "^1.0"}]}'); - $package = $this->inspector()->package('phpstan/phpstan'); - self::assertNotNull($package); - self::assertEquals('phpstan/phpstan', $package->name); - self::assertTrue($package->isDev); - } - - #[DataProvider('provideReturnsBinDirectory')] - public function testReturnsBinDirectory(string $composerContent, string $binPath): void - { - $this->putComposerLock('{"packages-dev":[{"name":"phpstan/phpstan", "version": "^1.0"}]}'); - $this->putComposer($composerContent); - - self::assertSame($binPath, $this->inspector()->binDir()); - } - - /** - * @return Generator - */ - public static function provideReturnsBinDirectory(): Generator - { - yield 'no bin directory' => ['', 'vendor/bin']; - yield 'bin directory specified' => ['{"bin-dir": "bin"}', 'bin']; - } - - private function inspector(): ComposerInspector - { - return (new ComposerInspector( - $this->workspace->path('composer.lock'), - $this->workspace->path('composer.json') - )); - } - - private function putComposer(string $contents): void - { - $this->workspace->put('composer.json', $contents); - } - - private function putComposerLock(string $contents): void - { - $this->workspace->put('composer.lock', $contents); - } -} diff --git a/lib/ConfigLoader/Adapter/Deserializer/JsonDeserializer.php b/lib/ConfigLoader/Adapter/Deserializer/JsonDeserializer.php deleted file mode 100644 index 86f41bff6c..0000000000 --- a/lib/ConfigLoader/Adapter/Deserializer/JsonDeserializer.php +++ /dev/null @@ -1,23 +0,0 @@ -parser->parse($contents); - } catch (ParseException $exception) { - throw new CouldNotDeserialize(sprintf( - 'Could not deserialize YAML, error from parser "%s"', - $exception->getMessage() - ), 0, $exception); - } - } -} diff --git a/lib/ConfigLoader/Adapter/PathCandidate/AbsolutePathCandidate.php b/lib/ConfigLoader/Adapter/PathCandidate/AbsolutePathCandidate.php deleted file mode 100644 index 4476b8b77e..0000000000 --- a/lib/ConfigLoader/Adapter/PathCandidate/AbsolutePathCandidate.php +++ /dev/null @@ -1,37 +0,0 @@ -absolutePath = $absolutePath; - - if (!Path::isAbsolute($absolutePath)) { - throw new RuntimeException(sprintf( - 'Path is not absolute "%s"', - $absolutePath - )); - } - } - - public function path(): string - { - return $this->absolutePath; - } - - public function loader(): string - { - return $this->loader; - } -} diff --git a/lib/ConfigLoader/Adapter/PathCandidate/XdgPathCandidate.php b/lib/ConfigLoader/Adapter/PathCandidate/XdgPathCandidate.php deleted file mode 100644 index a5c6035928..0000000000 --- a/lib/ConfigLoader/Adapter/PathCandidate/XdgPathCandidate.php +++ /dev/null @@ -1,28 +0,0 @@ -xdg->getHomeConfigDir(), $this->appName, $this->filename); - } - - public function loader(): string - { - return $this->loader; - } -} diff --git a/lib/ConfigLoader/ConfigLoaderBuilder.php b/lib/ConfigLoader/ConfigLoaderBuilder.php deleted file mode 100644 index dafbdabf77..0000000000 --- a/lib/ConfigLoader/ConfigLoaderBuilder.php +++ /dev/null @@ -1,64 +0,0 @@ -serializers[$name] = new JsonDeserializer(); - return $this; - } - - public function enableYamlDeserializer(string $name): self - { - $this->serializers[$name] = new YamlDeserializer(); - return $this; - } - - public function addXdgCandidate(string $appName, string $name, string $loader): self - { - $this->candidates[] = new XdgPathCandidate($appName, $name, $loader, new Xdg()); - return $this; - } - - public function addCandidate(string $absolutePath, string $loader): self - { - $this->candidates[] = new AbsolutePathCandidate($absolutePath, $loader); - return $this; - } - - public function loader(): ConfigLoader - { - return new ConfigLoader( - new Deserializers($this->serializers), - new PathCandidates($this->candidates) - ); - } -} diff --git a/lib/ConfigLoader/Core/ConfigLoader.php b/lib/ConfigLoader/Core/ConfigLoader.php deleted file mode 100644 index 4009866e8c..0000000000 --- a/lib/ConfigLoader/Core/ConfigLoader.php +++ /dev/null @@ -1,44 +0,0 @@ -candidates as $candidate) { - if (false === file_exists($candidate->path())) { - continue; - } - - $config = array_replace_recursive( - $config, - $this->deserializers->get($candidate->loader())->deserialize( - (string) file_get_contents($candidate->path()) - ) - ); - - if (null === $config) { - throw new RuntimeException( - 'Error occured in array_replace_recursive' - ); - } - } - - return $config; - } - - public function candidates(): PathCandidates - { - return $this->candidates; - } -} diff --git a/lib/ConfigLoader/Core/Deserializer.php b/lib/ConfigLoader/Core/Deserializer.php deleted file mode 100644 index 3f1356be8f..0000000000 --- a/lib/ConfigLoader/Core/Deserializer.php +++ /dev/null @@ -1,8 +0,0 @@ - $deserializer) { - $this->add($deserializerExtension, $deserializer); - } - } - - public function get(string $extension): Deserializer - { - if (!isset($this->deserializerMap[$extension])) { - throw new DeserializerNotFound(sprintf( - 'No deserializer registered for extension "%s", deserializers available for: "%s"', - $extension, - implode('", "', array_keys($this->deserializerMap)) - )); - } - - return $this->deserializerMap[$extension]; - } - - private function add($deserializerExtension, Deserializer $deserializer): void - { - $this->deserializerMap[$deserializerExtension] = $deserializer; - } -} diff --git a/lib/ConfigLoader/Core/Exception/CouldNotDeserialize.php b/lib/ConfigLoader/Core/Exception/CouldNotDeserialize.php deleted file mode 100644 index bc73f5c1a7..0000000000 --- a/lib/ConfigLoader/Core/Exception/CouldNotDeserialize.php +++ /dev/null @@ -1,9 +0,0 @@ -add($candidate); - } - } - - - public function getIterator(): Traversable - { - foreach ($this->candidates as $candidate) { - yield $candidate; - } - } - - private function add(PathCandidate $candidate): void - { - $this->candidates[] = $candidate; - } -} diff --git a/lib/ConfigLoader/Tests/Benchmark/ConfigLoaderBench.php b/lib/ConfigLoader/Tests/Benchmark/ConfigLoaderBench.php deleted file mode 100644 index 3fa2f7fd2b..0000000000 --- a/lib/ConfigLoader/Tests/Benchmark/ConfigLoaderBench.php +++ /dev/null @@ -1,108 +0,0 @@ -config1 = $this->workspace->path('config1.json'); - $this->config2 = $this->workspace->path('config2.json'); - $this->config1yaml = $this->workspace->path('config1.yaml'); - $this->config2yaml = $this->workspace->path('config2.yaml'); - file_put_contents($this->config1, json_encode(['one' => 'two'])); - file_put_contents($this->config2, json_encode(['two' => 'three'])); - file_put_contents($this->config1yaml, 'one: two'); - file_put_contents($this->config2yaml, 'two: three'); - } - - public function benchJsonLoadConfig(): void - { - $loader = new ConfigLoader( - new Deserializers([ - 'json' => new JsonDeserializer(), - ]), - new PathCandidates([ - new AbsolutePathCandidate($this->config1, 'json'), - new AbsolutePathCandidate($this->config2, 'json'), - ]) - ); - $loader->load(); - } - - public function benchJsonLoadConfigWithBuilder(): void - { - ConfigLoaderBuilder::create() - ->enableJsonDeserializer('json') - ->addCandidate($this->config1, 'json') - ->addCandidate($this->config2, 'json') - ->loader()->load(); - } - - public function benchJsonLoadConfigWithNonExistingYaml(): void - { - $loader = new ConfigLoader( - new Deserializers([ - 'json' => new JsonDeserializer(), - 'yaml' => new YamlDeserializer(), - ]), - new PathCandidates([ - new AbsolutePathCandidate($this->config1, 'json'), - new AbsolutePathCandidate('/path/to/yaml1', 'yaml'), - new AbsolutePathCandidate('/path/to/yaml2', 'yaml'), - new AbsolutePathCandidate($this->config2, 'json'), - ]) - ); - $loader->load(); - } - - public function benchJsonPlainPhp(): void - { - $config = array_merge( - json_decode(file_get_contents($this->config1), true), - json_decode(file_get_contents($this->config2), true) - ); - } - - public function benchYamlLoadConfig(): void - { - $loader = new ConfigLoader( - new Deserializers([ - 'yaml' => new YamlDeserializer(), - ]), - new PathCandidates([ - new AbsolutePathCandidate($this->config1yaml, 'yaml'), - new AbsolutePathCandidate($this->config2yaml, 'yaml'), - ]) - ); - $loader->load(); - } -} diff --git a/lib/ConfigLoader/Tests/Integration/ConfigLoaderTest.php b/lib/ConfigLoader/Tests/Integration/ConfigLoaderTest.php deleted file mode 100644 index b1322d783e..0000000000 --- a/lib/ConfigLoader/Tests/Integration/ConfigLoaderTest.php +++ /dev/null @@ -1,35 +0,0 @@ -createConfig('one.json', [ 'one' => [ 'two' => 'three' ] ]); - $path2 = $this->createConfig('two.json', [ 'one' => [ 'two' => 'four' ] ]); - - $config = ConfigLoaderBuilder::create() - ->enableJsonDeserializer('json') - ->addCandidate($path1, 'json') - ->addCandidate($path2, 'json') - ->loader()->load(); - - $this->assertEquals([ - 'one' => [ - 'two' => 'four' - ] - ], $config); - } - - private function createConfig(string $string, array $array): string - { - $path = $this->workspace->path($string); - file_put_contents($path, json_encode($array)); - - return $path; - } -} diff --git a/lib/ConfigLoader/Tests/TestCase.php b/lib/ConfigLoader/Tests/TestCase.php deleted file mode 100644 index 258bc43c11..0000000000 --- a/lib/ConfigLoader/Tests/TestCase.php +++ /dev/null @@ -1,17 +0,0 @@ -workspace = Workspace::create(__DIR__ . '/Workspace'); - $this->workspace->reset(); - } -} diff --git a/lib/ConfigLoader/Tests/Unit/Adapter/Deserializer/JsonDeserializerTest.php b/lib/ConfigLoader/Tests/Unit/Adapter/Deserializer/JsonDeserializerTest.php deleted file mode 100644 index dcac2f7ee1..0000000000 --- a/lib/ConfigLoader/Tests/Unit/Adapter/Deserializer/JsonDeserializerTest.php +++ /dev/null @@ -1,16 +0,0 @@ -expectException(CouldNotDeserialize::class); - (new JsonDeserializer())->deserialize('FOo BAR'); - } -} diff --git a/lib/ConfigLoader/Tests/Unit/Adapter/Deserializer/YamlDeserializerTest.php b/lib/ConfigLoader/Tests/Unit/Adapter/Deserializer/YamlDeserializerTest.php deleted file mode 100644 index 1b979aaf2c..0000000000 --- a/lib/ConfigLoader/Tests/Unit/Adapter/Deserializer/YamlDeserializerTest.php +++ /dev/null @@ -1,40 +0,0 @@ -expectException(CouldNotDeserialize::class); - (new YamlDeserializer())->deserialize( - <<<'EOT' - asd - \t - a - 1235 - 123 - EOT - ); - } - - public function testDeserialize(): void - { - $config = (new YamlDeserializer())->deserialize( - <<<'EOT' - one: - two: three - EOT - ); - - $this->assertEquals([ - 'one' => [ - 'two' => 'three', - ] - ], $config); - } -} diff --git a/lib/ConfigLoader/Tests/Unit/Adapter/PathCandidate/AbsolutePathCandidateTest.php b/lib/ConfigLoader/Tests/Unit/Adapter/PathCandidate/AbsolutePathCandidateTest.php deleted file mode 100644 index 2be48ceb73..0000000000 --- a/lib/ConfigLoader/Tests/Unit/Adapter/PathCandidate/AbsolutePathCandidateTest.php +++ /dev/null @@ -1,22 +0,0 @@ -expectException(RuntimeException::class); - new AbsolutePathCandidate('hello', 'foo'); - } - - public function testNormalizesWindowsPaths(): void - { - $path = new AbsolutePathCandidate('c:\hello', 'foo'); - self::assertEquals('c:/hello', $path->path()); - } -} diff --git a/lib/ConfigLoader/Tests/Unit/Adapter/PathCandidate/XdgPathCandidateTest.php b/lib/ConfigLoader/Tests/Unit/Adapter/PathCandidate/XdgPathCandidateTest.php deleted file mode 100644 index 562e9feadb..0000000000 --- a/lib/ConfigLoader/Tests/Unit/Adapter/PathCandidate/XdgPathCandidateTest.php +++ /dev/null @@ -1,17 +0,0 @@ -assertStringContainsString('phpactor', $candidate->path()); - $this->assertEquals('foo', $candidate->loader()); - } -} diff --git a/lib/ConfigLoader/Tests/Unit/Core/ConfigLoaderTest.php b/lib/ConfigLoader/Tests/Unit/Core/ConfigLoaderTest.php deleted file mode 100644 index 2de071cfe3..0000000000 --- a/lib/ConfigLoader/Tests/Unit/Core/ConfigLoaderTest.php +++ /dev/null @@ -1,144 +0,0 @@ - - */ - private ObjectProphecy $deserializer; - - public function setUp(): void - { - parent::setUp(); - $this->deserializer = $this->prophesize(Deserializer::class); - } - - public function testDoesNothingWhenEmpty(): void - { - $loader = new ConfigLoader(new Deserializers([]), new PathCandidates([])); - $config = $loader->load(); - $this->assertEquals([], $config); - } - - public function testIgnoresNotExistingConfigs(): void - { - $loader = new ConfigLoader(new Deserializers([]), new PathCandidates([ - new AbsolutePathCandidate($this->workspace->path('foobar'), 'nope') - ])); - $config = $loader->load(); - $this->assertEquals([], $config); - } - - public function testLoadsConfig(): void - { - $configFile = $this->workspace->path('foobar.test'); - file_put_contents($configFile, 'test'); - - $loader = new ConfigLoader( - new Deserializers([ - 'test' => $this->deserializer->reveal() - ]), - new PathCandidates([ - new AbsolutePathCandidate( - $configFile, - 'test' - ) - ]) - ); - - $this->deserializer->deserialize('test')->willReturn([ - 'one' => 'two' - ]); - - - $config = $loader->load(); - $this->assertEquals([ - 'one' => 'two', - ], $config); - } - - public function testMergesConfigsFromFirstToLast(): void - { - $loader = $this->createTwoFileLoader(); - - $this->deserializer->deserialize('test1')->willReturn([ - 'one' => 'two', - 'two' => 'three', - ]); - - $this->deserializer->deserialize('test2')->willReturn([ - 'one' => 'four', - 'two' => 'three', - ]); - - $config = $loader->load(); - $this->assertEquals([ - 'one' => 'four', - 'two' => 'three', - ], $config); - } - - public function testMergesNestedKeys(): void - { - $loader = $this->createTwoFileLoader(); - - $this->deserializer->deserialize('test1')->willReturn([ - 'one' => [ - 'two' => 'three', - ], - ]); - - $this->deserializer->deserialize('test2')->willReturn([ - 'one' => [ - 'two' => 'three', - 'three' => 'four', - ], - ]); - - $config = $loader->load(); - $this->assertEquals([ - 'one' => [ - 'two' => 'three', - 'three' => 'four', - ], - ], $config); - } - - private function createTwoFileLoader(): ConfigLoader - { - $configFile1 = $this->workspace->path('foobar.test'); - $configFile2 = $this->workspace->path('barfoo.test'); - file_put_contents($configFile1, 'test1'); - file_put_contents($configFile2, 'test2'); - - $loader = new ConfigLoader( - new Deserializers([ - 'test' => $this->deserializer->reveal() - ]), - new PathCandidates([ - new AbsolutePathCandidate( - $configFile1, - 'test' - ), - new AbsolutePathCandidate( - $configFile2, - 'test' - ), - ]) - ); - return $loader; - } -} diff --git a/lib/ConfigLoader/Tests/Unit/Core/DeserializersTest.php b/lib/ConfigLoader/Tests/Unit/Core/DeserializersTest.php deleted file mode 100644 index 6772b95816..0000000000 --- a/lib/ConfigLoader/Tests/Unit/Core/DeserializersTest.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ - private ObjectProphecy $deserializer; - - public function setUp(): void - { - $this->deserializer = $this->prophesize(Deserializer::class); - } - - public function testExceptionOnUnregisteredLoader(): void - { - $this->expectException(DeserializerNotFound::class); - $this->expectExceptionMessage('No deserializer registered'); - $deserializers = new Deserializers([ - 'xml' => $this->deserializer->reveal(), - 'json' => $this->deserializer->reveal(), - ]); - - $deserializers->get('asd'); - } -} diff --git a/lib/Configurator/Adapter/Phpactor/PhpactorConfigChange.php b/lib/Configurator/Adapter/Phpactor/PhpactorConfigChange.php deleted file mode 100644 index 30267248b9..0000000000 --- a/lib/Configurator/Adapter/Phpactor/PhpactorConfigChange.php +++ /dev/null @@ -1,31 +0,0 @@ - $keyValues - */ - public function __construct( - private string $prompt, - private Closure $keyValues - ) { - } - - public function prompt(): string - { - return $this->prompt; - } - - /** - * @return Closure(bool):array - */ - public function keyValues(): Closure - { - return $this->keyValues; - } -} diff --git a/lib/Configurator/Adapter/Phpactor/PhpactorConfigChangeApplicator.php b/lib/Configurator/Adapter/Phpactor/PhpactorConfigChangeApplicator.php deleted file mode 100644 index 8bf7e089f0..0000000000 --- a/lib/Configurator/Adapter/Phpactor/PhpactorConfigChangeApplicator.php +++ /dev/null @@ -1,27 +0,0 @@ -keyValues())($enable) as $key => $value) { - $this->maipulator->set($key, $value); - } - - return true; - } -} diff --git a/lib/Configurator/Adapter/Test/TestChangeSuggestor.php b/lib/Configurator/Adapter/Test/TestChangeSuggestor.php deleted file mode 100644 index 5099aef597..0000000000 --- a/lib/Configurator/Adapter/Test/TestChangeSuggestor.php +++ /dev/null @@ -1,22 +0,0 @@ -closure)(); - } -} diff --git a/lib/Configurator/Configurator.php b/lib/Configurator/Configurator.php deleted file mode 100644 index 5fce52bc54..0000000000 --- a/lib/Configurator/Configurator.php +++ /dev/null @@ -1,52 +0,0 @@ - $suggestors - * @param list $applicators - */ - public function __construct( - private array $suggestors, - private array $applicators - ) { - } - - public function suggestChanges(): Changes - { - $changes = []; - foreach ($this->suggestors as $suggestor) { - foreach ($suggestor->suggestChanges() as $change) { - $changes[] = $change; - } - } - - return new Changes($changes); - } - - public function apply(Change|Changes $changes, bool $enable): void - { - $changes = $changes instanceof Changes ? $changes : Changes::from([$changes]); - - foreach ($changes as $change) { - foreach ($this->applicators as $applicator) { - if ($applicator->apply($change, $enable)) { - continue 2; - } - } - - throw new RuntimeException(sprintf( - 'Could not find change applicator for "%s"', - $change::class - )); - } - } -} diff --git a/lib/Configurator/Model/Change.php b/lib/Configurator/Model/Change.php deleted file mode 100644 index fe36c9c6b5..0000000000 --- a/lib/Configurator/Model/Change.php +++ /dev/null @@ -1,8 +0,0 @@ - - */ -final class Changes implements IteratorAggregate, Countable -{ - /** - * @param list $changes - */ - public function __construct(private array $changes) - { - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->changes); - } - - public function count(): int - { - return count($this->changes); - } - - public static function none(): self - { - return new self([]); - } - - /** - * @param array $changes - */ - public static function from(array $changes): self - { - return new self($changes); - } -} diff --git a/lib/Configurator/Model/ConfigManipulator.php b/lib/Configurator/Model/ConfigManipulator.php deleted file mode 100644 index b54573e370..0000000000 --- a/lib/Configurator/Model/ConfigManipulator.php +++ /dev/null @@ -1,101 +0,0 @@ -configPath; - } - - public function initialize(): string - { - $json = $this->openConfig(); - $json->{'$schema'} = $this->schemaPath; - $this->writeConfig($json); - - return self::ACTION_UPDATED; - } - - /** - * @param mixed $value - */ - public function set(string $key, $value): void - { - $json = $this->openConfig(); - $json->{$key} = $value; - $this->writeConfig($json); - } - - public function delete(string $key): void - { - $json = $this->openConfig(); - unset($json->{$key}); - $this->writeConfig($json); - } - - private function createConfig(): string - { - $value = [ '$schema' => $this->schemaPath ]; - return json_encode($value, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR); - } - - private function openConfig(): stdClass - { - if (!file_exists($this->configPath)) { - if (false === file_put_contents($this->configPath, $this->createConfig())) { - throw new RuntimeException(sprintf( - 'Could not write Phpactor config file to "%s"', - $this->configPath - )); - } - } - - $config = file_get_contents($this->configPath); - if (false === $config) { - throw new RuntimeException(sprintf( - 'Could not read config file "%s"', - $this->configPath - )); - } - - $json = json_decode($config); - if (!$json instanceof stdClass) { - throw new RuntimeException(sprintf( - 'Could not decode JSON file "%s"', - $this->configPath - )); - } - - return $json; - } - - /** - * @param mixed $value - */ - private function writeConfig($value): void - { - file_put_contents($this->configPath, json_encode($value, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES) . "\n"); - } -} diff --git a/lib/Configurator/Model/JsonConfig.php b/lib/Configurator/Model/JsonConfig.php deleted file mode 100644 index 59ff1ddf5a..0000000000 --- a/lib/Configurator/Model/JsonConfig.php +++ /dev/null @@ -1,54 +0,0 @@ -object = new stdClass(); - } - - public function has(string $key): bool - { - $this->load(); - return isset($this->object->$key); - } - - public static function fromPath(string $path): JsonConfig - { - return new self($path); - } - - private function load(): void - { - if ($this->loaded) { - return; - } - $this->loaded = true; - - if (!file_exists($this->path)) { - return; - } - - $contents = file_get_contents($this->path); - - if (false === $contents) { - return; - } - - $obj = json_decode($contents); - - if (!$obj instanceof stdClass) { - return; - } - - $this->object = $obj; - } -} diff --git a/lib/Configurator/Tests/Integration/ConfiguratorTest.php b/lib/Configurator/Tests/Integration/ConfiguratorTest.php deleted file mode 100644 index a729a7251c..0000000000 --- a/lib/Configurator/Tests/Integration/ConfiguratorTest.php +++ /dev/null @@ -1,57 +0,0 @@ -workspace()->reset(); - } - - public function testConfigurator(): void - { - $configurator = new Configurator([ - new TestChangeSuggestor(function (): Changes { - return new Changes([ - new PhpactorConfigChange('Symfony detected: enable Symfony extension', fn (bool $enable) => [ - 'symfony.enable' => $enable, - 'indexer.ignore' => ['var'], - ]) - ]); - }), - new TestChangeSuggestor(function (): Changes { - return new Changes([ - new PhpactorConfigChange('PHPUnit detected: enable the PHPUnit extension', fn (bool $enable) => [ - 'phpunit.enable' => true, - ]) - ]); - }) - ], [ - new PhpactorConfigChangeApplicator(new ConfigManipulator( - 'schemaPath.json', - $this->workspace()->path('phpactor.json') - )) - ]); - - $changes = $configurator->suggestChanges(); - self::assertCount(2, $changes); - - $configurator->apply($changes, true); - - self::assertEquals([ - '$schema' => 'schemaPath.json', - 'symfony.enable' => true, - 'indexer.ignore' => ['var'], - 'phpunit.enable' => true, - ], json_decode($this->workspace()->getContents('phpactor.json'), true)); - } -} diff --git a/lib/Configurator/Tests/IntegrationTestCase.php b/lib/Configurator/Tests/IntegrationTestCase.php deleted file mode 100644 index 2ea75acb1d..0000000000 --- a/lib/Configurator/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,14 +0,0 @@ -workspace = new Workspace(__DIR__ . '/../../Workspace'); - $this->workspace->reset(); - } - - public function testCreateNewConfig(): void - { - self::assertFileDoesNotExist($this->workspace->path('.phpactor.json')); - - (new ConfigManipulator( - 'path/to/json.schema', - $this->workspace->path('.phpactor.json') - ))->initialize(); - - self::assertFileExists($this->workspace->path('.phpactor.json')); - self::assertJson($this->workspace->getContents('.phpactor.json')); - } -} diff --git a/lib/Configurator/Tests/Unit/Model/JsonConfigTest.php b/lib/Configurator/Tests/Unit/Model/JsonConfigTest.php deleted file mode 100644 index cc83d96a59..0000000000 --- a/lib/Configurator/Tests/Unit/Model/JsonConfigTest.php +++ /dev/null @@ -1,17 +0,0 @@ -workspace()->put('foo.json', '{"foo": "bar"}'); - $config = JsonConfig::fromPath($this->workspace()->path('foo.json')); - self::assertFalse($config->has('bar')); - self::assertTrue($config->has('foo')); - } -} diff --git a/lib/Container/BootableExtension.php b/lib/Container/BootableExtension.php deleted file mode 100644 index 69b238ee94..0000000000 --- a/lib/Container/BootableExtension.php +++ /dev/null @@ -1,8 +0,0 @@ -origLine = $chunk->getStart(); - } - - public function getOrigLine(): int - { - return $this->origLine; - } - - public function eat(): ?Line - { - if ($this->position >= count($this->chunk->getLines())) { - return null; - } - - $line = $this->chunk->getLines()[$this->position++]; - - if (in_array($line->getType(), [Line::REMOVED, Line::UNCHANGED])) { - $this->origLine++; - } - - return $line; - } - - /** - * @return Line[]|null - */ - public function eatWhileType(int $type): ?array - { - $lines = []; - - while (($line = $this->eat()) && $line->getType() === $type) { - $lines[] = $line; - } - - if ($line && $line->getType() !== $type) { - $this->rewind(); - } - - if (count($lines) === 0) { - return null; - } - - return $lines; - } - - /** - * @return Line[]|null - */ - public function eatUnchanged(): ?array - { - return $this->eatWhileType(Line::UNCHANGED); - } - - /** - * @return Line[]|null - */ - public function eatRemoved(): ?array - { - return $this->eatWhileType(Line::REMOVED); - } - - /** - * @return Line[]|null - */ - public function eatAdded(): ?array - { - return $this->eatWhileType(Line::ADDED); - } - - public function current(): ?Line - { - return $this->chunk->getLines()[$this->position] ?? null; - } - - private function rewind(): void - { - if ($this->position === 0) { - return; - }; - - $this->position--; - $line = $this->current(); - - if ($line && in_array($line->getType(), [Line::REMOVED, Line::UNCHANGED])) { - $this->origLine--; - } - } -} diff --git a/lib/Diff/DiffToTextEditsConverter.php b/lib/Diff/DiffToTextEditsConverter.php deleted file mode 100644 index 6c18e71bc9..0000000000 --- a/lib/Diff/DiffToTextEditsConverter.php +++ /dev/null @@ -1,79 +0,0 @@ -parser = new Parser(); - } - - /** - * @return TextEdit[] - */ - public function toTextEdits(string $diffText): array - { - $parsedDiffs = $this->parser->parse($diffText); - - $edits = []; - - foreach ($parsedDiffs as $diff) { - foreach ($diff->getChunks() as $chunk) { - $consumer = new DiffLinesConsumer($chunk); - - while ($consumer->current()) { - if ($consumer->eatUnchanged()) { - continue; - } - - $startLine = $consumer->getOrigLine() - 1; - - if (($added = $consumer->eatAdded()) !== null) { - $edits[] = new TextEdit( - new Range( - new Position($startLine, 0), - new Position($startLine, 0) - ), - $this->linesToString($added) - ); - }; - - if (($removed = $consumer->eatRemoved()) !== null) { - $added = $consumer->eatAdded(); - - $edits[] = new TextEdit( - new Range( - new Position($startLine, 0), - new Position($startLine + count($removed), 0) - ), - $this->linesToString($added) - ); - } - } - } - } - - return $edits; - } - - /** - * @param Line[]|null $lines - */ - private function linesToString(?array $lines): string - { - if ($lines === null || count($lines) === 0) { - return ''; - } - - return join("\n", array_map(fn (Line $line) => $line->getContent(), $lines))."\n"; - } -} diff --git a/lib/Diff/RangesForDiff.php b/lib/Diff/RangesForDiff.php deleted file mode 100644 index 7a80398eb6..0000000000 --- a/lib/Diff/RangesForDiff.php +++ /dev/null @@ -1,120 +0,0 @@ -getChunks() as $chunk) { - // diff is 1-indexed + in a line loop we update this number beforehand - $lineNo = $chunk->getStart() - 2; - - /** @var Line[] */ - $changedLines = []; - /** @var Line[]|null */ - $replacedLines = null; - /** @var int|null */ - $startLineNo = null; - - foreach ($chunk->getLines() as $index => $line) { - // increment orig file line number (added lines are not part of orig file) - if (in_array($line->getType(), [Line::UNCHANGED, Line::REMOVED])) { - $lineNo++; - } - - $lastChangedLine = end($changedLines); - - // consume same as previous line - if ($lastChangedLine && $line->getType() === $lastChangedLine->getType()) { - $changedLines[] = $line; - continue; - } - - // consume lines if previous were removed and now we getting a replacement ones - if ($lastChangedLine && $lastChangedLine->getType() === Line::REMOVED && $line->getType() === Line::ADDED) { - $replacedLines = $changedLines; - $changedLines = [$line]; - - continue; - } - - if ($lastChangedLine) { - if ($changedLines === [] || $startLineNo === null) { - throw new LogicException('Start line number was not resolved'); - } - - $startPos = new Position($startLineNo, 0); - $lineLength = strlen($lastChangedLine->getContent()); - $endPos = $lineLength - ? new Position($lineNo - 1, $lineLength) - : new Position($lineNo, 0); - - if ($replacedLines) { - $firstLineA = $replacedLines[0]->getContent(); - $firstLineB = $changedLines[0]->getContent(); - $lastLineA = end($replacedLines)->getContent(); - $lastLineB = end($changedLines)->getContent(); - - $startChars = StringSharedChars::startLength($firstLineA, $firstLineB); - $endChars = StringSharedChars::endPos($lastLineA, $lastLineB); - - $startPos = new Position($startLineNo, $startChars); - $endPos = new Position($lineNo - 1, $endChars); - } - - $ranges[] = new Range($startPos, $endPos); - - $startLineNo = null; - $changedLines = []; - } - - if ($line->getType() === Line::UNCHANGED) { - continue; - } - - if ($line->getType() === Line::REMOVED) { - $startLineNo = $lineNo; - $changedLines[] = $line; - - continue; - } - - $prevLine = $chunk->getLines()[$index - 1]; - - if ($prevLine->getContent() === "\ No newline at end of file") { - $contextLines = []; - - continue; - } - - if ($line->getType() === Line::ADDED - && $prevLine->getType() === Line::UNCHANGED - ) { - $ranges[] = new Range(new Position($lineNo, 0), new Position($lineNo, 1)); - $contextLines = []; - - continue; - } - } - } - - return $ranges; - } -} diff --git a/lib/Diff/StringSharedChars.php b/lib/Diff/StringSharedChars.php deleted file mode 100644 index 95cc16807c..0000000000 --- a/lib/Diff/StringSharedChars.php +++ /dev/null @@ -1,47 +0,0 @@ - $letter) { - if ($letter !== ($b[$index] ?? null)) { - return $index; - } - } - - return count($a); - } - - /** - * Counts number of shared characters on the end of a string - */ - public static function endLength(string $a, string $b): int - { - return self::startLength(strrev($a), strrev($b)); - } - - /** - * Gets the position of the shared ending string between args - */ - public static function endPos(string $a, string $b): int - { - $end = self::endLength($a, $b); - $strlen = strlen($a); - - return $end === $strlen - ? 0 - : $strlen - $end; - } -} diff --git a/lib/Diff/Tests/DiffToTextEditsConverterTest.php b/lib/Diff/Tests/DiffToTextEditsConverterTest.php deleted file mode 100644 index 83789646f0..0000000000 --- a/lib/Diff/Tests/DiffToTextEditsConverterTest.php +++ /dev/null @@ -1,63 +0,0 @@ -toTextEdits($diff); - - self::assertCount(3, $edits, '3 changes expected: removal of 2 first lines, adding extra line in middle, adding extra content on the end'); - - // first - removal of 2 first lines - self::assertEquals($edits[0]->range->start->line, 0); - self::assertEquals($edits[0]->range->end->line, 2); - self::assertEquals($edits[0]->newText, ''); - - // second - removes line, and replaces it with new text (with extra new line) - self::assertEquals($edits[1]->range->start->line, 3); - self::assertEquals($edits[1]->range->end->line, 4); - self::assertEquals($edits[1]->newText, "The named is the mother of all things.\n\n"); - - // third - adds lines on the end - self::assertEquals($edits[2]->range->start->line, 11); - self::assertEquals($edits[2]->range->end->line, 11); - self::assertEquals( - $edits[2]->newText, - <<rangesForDiff = new RangesForDiff(); - } - - public function testNoChanges(): void - { - $emptyDiff = new Diff('', ''); - $ranges = $this->rangesForDiff->createRangesForDiff($emptyDiff); - self::assertCount(0, $ranges); - } - - /** - * @param Range[] $expectedRanges - */ - #[DataProvider('diffProvider')] - public function testCreatingRanges(string $diff, array $expectedRanges): void - { - $parser = new Parser(); - $diffObject = $parser->parse($diff)[0]; - - $ranges = $this->rangesForDiff->createRangesForDiff($diffObject); - self::assertEquals($expectedRanges, $ranges); - } - - /** - * @return iterable - */ - public static function diffProvider(): iterable - { - yield 'multiple replacements' => [ - 'diff' => << [ - new Range( - new Position(3, 0), - new Position(5, 4) - ), - new Range( - new Position(7, 0), - new Position(7, 13) - ), - ], - ]; - - yield 'addition' => [ - 'diff' => << [ - new Range( - new Position(2, 0), - new Position(2, 1) - ) - ] - ]; - - yield 'deletion' => [ - 'diff' => << [ - new Range( - new Position(3, 0), - new Position(4, 0) - ) - ] - ]; - - yield 'change first line' => [ - 'diff' => << [ - new Range( - new Position(0, 5), - new Position(0, 5) - ) - ] - ]; - } -} diff --git a/lib/Diff/Tests/StringSharedCharsTest.php b/lib/Diff/Tests/StringSharedCharsTest.php deleted file mode 100644 index e53766d18b..0000000000 --- a/lib/Diff/Tests/StringSharedCharsTest.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class ArrayKeyValueList extends Node implements IteratorAggregate, Countable -{ - protected const CHILD_NAMES = [ - 'list' - ]; - - /** - * @param array $list - */ - public function __construct(public array $list) - { - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->list); - } - - public function count(): int - { - return count($this->list); - } - - /** - * @return ArrayKeyValueNode[] - */ - public function arrayKeyValues(): array - { - return array_filter($this->list, function (Element $element) { - return $element instanceof ArrayKeyValueNode; - }); - } -} diff --git a/lib/DocblockParser/Ast/ArrayKeyValueNode.php b/lib/DocblockParser/Ast/ArrayKeyValueNode.php deleted file mode 100644 index 8d6590ae3f..0000000000 --- a/lib/DocblockParser/Ast/ArrayKeyValueNode.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ - public ElementList $children; - - /** - * @param Element[] $children - */ - public function __construct(array $children) - { - $this->children = new ElementList($children); - } - - /** - * @param class-string $tagFqn - */ - public function hasTag(string $tagFqn): bool - { - foreach ($this->tags() as $tag) { - if ($tag instanceof $tagFqn) { - return true; - } - } - - return false; - } - - /** - * @return class-string[] - */ - public function tagTypes(): array - { - $types = []; - foreach ($this->tags() as $tag) { - if ($tag instanceof UnknownTag) { - continue; - } - $types[$tag::class] = true; - } - - return array_keys($types); - } - - /** - * @template T of TagNode - * @param class-string|null $tagFqn - * @return ($tagFqn is string ? Generator : Generator) - */ - public function tags(?string $tagFqn = null): Generator - { - foreach ($this->children as $child) { - if ($tagFqn && $child instanceof $tagFqn) { - yield $child; - continue; - } - if (!$tagFqn && $child instanceof TagNode) { - yield $child; - continue; - } - } - } - - public function phpDocOpen(): ?Token - { - foreach ($this->tokens() as $token) { - if ($token->type === Token::T_PHPDOC_OPEN) { - return $token; - } - } - - return null; - } - - public function prose(): string - { - $prose = []; - foreach ($this->descendantElements() as $child) { - if ($child instanceof TagNode) { - break; - } - if (!$child instanceof Token) { - continue; - } - - if (in_array($child->type, [ - Token::T_PHPDOC_OPEN, - Token::T_PHPDOC_CLOSE, - Token::T_ASTERISK - ])) { - continue; - } - - if ($child->type === Token::T_TAG) { - break; - } - $prose[] = $child->value; - } - - return implode("\n", array_map(trim(...), (explode("\n", implode('', $prose))))); - } - - public function lastMultilineContentToken(): ?Token - { - $hasLeading = false; - $lastToken = null; - foreach ($this->tokens() as $child) { - if ($child->type === Token::T_ASTERISK) { - $hasLeading = true; - } - if ($child->type === Token::T_PHPDOC_CLOSE && $hasLeading) { - return $lastToken; - } - $lastToken = $child; - } - - return null; - } - - public function indentationLevel(): int - { - $previous = null; - foreach ($this->children->elements as $child) { - if (!$child instanceof Token) { - continue; - } - if ($child->type === Token::T_ASTERISK) { - return $previous->length(); - } - if ($child->type === Token::T_PHPDOC_CLOSE) { - return $previous->length(); - } - $previous = $child; - } - - return 0; - } -} diff --git a/lib/DocblockParser/Ast/Element.php b/lib/DocblockParser/Ast/Element.php deleted file mode 100644 index d83c769f4e..0000000000 --- a/lib/DocblockParser/Ast/Element.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class ElementList extends Node implements IteratorAggregate -{ - protected const CHILD_NAMES = [ - 'elements', - ]; - - /** - * @param T[] $elements - */ - public function __construct(public array $elements) - { - } - - /** - * @return ArrayIterator - */ - public function getIterator(): Iterator - { - return new ArrayIterator($this->elements); - } - - /** - * @return Element[] - */ - public function toArray(): array - { - return $this->elements; - } -} diff --git a/lib/DocblockParser/Ast/Node.php b/lib/DocblockParser/Ast/Node.php deleted file mode 100644 index b855372f9e..0000000000 --- a/lib/DocblockParser/Ast/Node.php +++ /dev/null @@ -1,241 +0,0 @@ -length()); - ; - $start = $this->start(); - foreach ($this->tokens() as $token) { - $out = substr_replace($out, $token->value, $token->start() - $start, $token->length()); - } - - return $out; - } - - /** - * @return Generator - */ - public function tokens(): Generator - { - yield from $this->findTokens($this->children()); - } - - /** - * Return the short name of the node class (e.g. ParamTag) - */ - public function shortName(): string - { - return substr(get_class($this), strrpos(get_class($this), '\\') + 1); - } - - /** - * @return Generator - */ - public function selfAndDescendantElements(): Generator - { - yield $this; - yield from $this->traverseNodes($this->children()); - } - - /** - * @template T of Element - * @param class-string $elementFqn - * @return ($elementFqn is null ? Generator : Generator) - */ - public function descendantElements(?string $elementFqn = null): Generator - { - if (null === $elementFqn) { - yield from $this->traverseNodes($this->children()); - return; - } - - foreach ($this->traverseNodes($this->children()) as $element) { - if ($element instanceof $elementFqn) { - yield $element; - } - } - } - - /** - * @template T of Element - * @param class-string $elementFqn - */ - public function hasDescendant(string $elementFqn): bool - { - foreach ($this->descendantElements($elementFqn) as $element) { - return true; - } - - return false; - } - - /** - * @template T of Element - * @param class-string $elementFqn - * @return T|null - */ - public function firstDescendant(string $elementFqn): ?Element - { - foreach ($this->descendantElements($elementFqn) as $element) { - return $element; - } - - return null; - } - - /** - * @param class-string $elementFqn - * @return Generator - */ - public function children(?string $elementFqn = null): Generator - { - if (!$elementFqn) { - foreach (static::CHILD_NAMES as $name) { - $child = $this->$name; - if (null !== $child) { - yield $child; - } - } - - return; - } - - foreach (static::CHILD_NAMES as $name) { - $child = $this->$name; - if ($child instanceof $elementFqn) { - yield $child; - } - } - } - - /** - * Return the bytes offset for the start of this node. - */ - public function start(): int - { - return $this->startOf($this->children()); - } - - /** - * Return the bytes offset for the end of this node. - */ - public function end(): int - { - return $this->endOf(array_reverse(iterator_to_array($this->children(), false))); - } - - public function hasChild(string $elementFqn): bool - { - foreach ($this->children() as $child) { - if ($child instanceof $elementFqn) { - return true; - } - } - - return false; - } - - public function length(): int - { - return $this->end() - $this->start(); - } - - /** - * @param iterable> $nodes - * - * @return Generator - */ - private function traverseNodes(iterable $nodes): Generator - { - $result = []; - foreach ($nodes as $child) { - if (is_iterable($child)) { - yield from $this->traverseNodes($child); - continue; - } - - if ($child instanceof Node) { - yield from $child->selfAndDescendantElements(); - continue; - } - - if ($child instanceof Token) { - yield $child; - continue; - } - } - } - - /** - * @param iterable> $elements - */ - private function endOf(iterable $elements): int - { - foreach ($elements as $element) { - if (null === $element) { - continue; - } - - if (is_array($element)) { - return $this->endOf(array_reverse($element)); - } - - if ($element instanceof Traversable) { - return $this->endOf(array_reverse(iterator_to_array($element))); - } - - return $element->end(); - } - - return 0; - } - - /** - * @param iterable> $elements - */ - private function startOf(iterable $elements): int - { - foreach ($elements as $element) { - if ($element instanceof Element) { - return $element->start(); - } - if (is_iterable($element)) { - return $this->startOf($element); - } - } - - return 0; - } - - /** - * @return Generator - * @param iterable> $nodes - */ - private function findTokens(iterable $nodes): Generator - { - foreach ($nodes as $node) { - if ($node instanceof Token) { - yield $node; - continue; - } - - if ($node instanceof Node) { - yield from $node->tokens(); - } - - if (is_iterable($node)) { - yield from $this->findTokens($node); - } - } - } -} diff --git a/lib/DocblockParser/Ast/ParameterList.php b/lib/DocblockParser/Ast/ParameterList.php deleted file mode 100644 index fcd715496d..0000000000 --- a/lib/DocblockParser/Ast/ParameterList.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ -class ParameterList extends Node implements IteratorAggregate, Countable -{ - protected const CHILD_NAMES = [ - 'list' - ]; - - /** - * @param array $list - */ - public function __construct(public array $list) - { - } - - /** - * @return Generator - */ - public function parameters(): Generator - { - foreach ($this->list as $element) { - if ($element instanceof ParameterTag) { - yield $element; - } - } - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->list); - } - - public function count(): int - { - return count($this->list); - } -} diff --git a/lib/DocblockParser/Ast/Tag/AssertTag.php b/lib/DocblockParser/Ast/Tag/AssertTag.php deleted file mode 100644 index 0ca429522d..0000000000 --- a/lib/DocblockParser/Ast/Tag/AssertTag.php +++ /dev/null @@ -1,26 +0,0 @@ -text) { - return $this->text->toString(); - } - - return null; - } -} diff --git a/lib/DocblockParser/Ast/Tag/ExtendsTag.php b/lib/DocblockParser/Ast/Tag/ExtendsTag.php deleted file mode 100644 index 022368423f..0000000000 --- a/lib/DocblockParser/Ast/Tag/ExtendsTag.php +++ /dev/null @@ -1,21 +0,0 @@ - $tokensAndTypes - */ - public function __construct( - public Token $tag, - public array $tokensAndTypes = [] - ) { - } - - /** - * @return TypeNode[] - */ - public function types(): array - { - return array_filter($this->tokensAndTypes, function ($node) { - return $node instanceof TypeNode; - }); - } -} diff --git a/lib/DocblockParser/Ast/Tag/MethodTag.php b/lib/DocblockParser/Ast/Tag/MethodTag.php deleted file mode 100644 index ebb9c47857..0000000000 --- a/lib/DocblockParser/Ast/Tag/MethodTag.php +++ /dev/null @@ -1,44 +0,0 @@ -name) { - return null; - } - - return $this->name->toString(); - } -} diff --git a/lib/DocblockParser/Ast/Tag/MixinTag.php b/lib/DocblockParser/Ast/Tag/MixinTag.php deleted file mode 100644 index ff7700738c..0000000000 --- a/lib/DocblockParser/Ast/Tag/MixinTag.php +++ /dev/null @@ -1,26 +0,0 @@ -class; - } -} diff --git a/lib/DocblockParser/Ast/Tag/ParamTag.php b/lib/DocblockParser/Ast/Tag/ParamTag.php deleted file mode 100644 index 47df567cd2..0000000000 --- a/lib/DocblockParser/Ast/Tag/ParamTag.php +++ /dev/null @@ -1,51 +0,0 @@ -variable) { - return null; - } - - return $this->variable->name()->toString(); - } - - public function type(): ?TypeNode - { - return $this->type; - } - - public function variable(): ?VariableNode - { - return $this->variable; - } - - public function text(): ?TextNode - { - return $this->text; - } -} diff --git a/lib/DocblockParser/Ast/Tag/ParameterTag.php b/lib/DocblockParser/Ast/Tag/ParameterTag.php deleted file mode 100644 index 27ef33f92e..0000000000 --- a/lib/DocblockParser/Ast/Tag/ParameterTag.php +++ /dev/null @@ -1,43 +0,0 @@ -name) { - return null; - } - - return $this->name->name()->toString(); - } - - public function type(): ?TypeNode - { - return $this->type; - } - - public function default(): ?ValueNode - { - return $this->default; - } -} diff --git a/lib/DocblockParser/Ast/Tag/PropertyTag.php b/lib/DocblockParser/Ast/Tag/PropertyTag.php deleted file mode 100644 index 7f40a6bcc8..0000000000 --- a/lib/DocblockParser/Ast/Tag/PropertyTag.php +++ /dev/null @@ -1,32 +0,0 @@ -name) { - return null; - } - - return $this->name->toString(); - } -} diff --git a/lib/DocblockParser/Ast/Tag/ReturnTag.php b/lib/DocblockParser/Ast/Tag/ReturnTag.php deleted file mode 100644 index 2d9d3b13ed..0000000000 --- a/lib/DocblockParser/Ast/Tag/ReturnTag.php +++ /dev/null @@ -1,34 +0,0 @@ -type; - } - - public function text(): ?TextNode - { - return $this->text; - } -} diff --git a/lib/DocblockParser/Ast/Tag/TemplateTag.php b/lib/DocblockParser/Ast/Tag/TemplateTag.php deleted file mode 100644 index 94f75d0b21..0000000000 --- a/lib/DocblockParser/Ast/Tag/TemplateTag.php +++ /dev/null @@ -1,30 +0,0 @@ -placeholder ? $this->placeholder->toString() : ''; - } -} diff --git a/lib/DocblockParser/Ast/Tag/ThrowsTag.php b/lib/DocblockParser/Ast/Tag/ThrowsTag.php deleted file mode 100644 index 37b9108a01..0000000000 --- a/lib/DocblockParser/Ast/Tag/ThrowsTag.php +++ /dev/null @@ -1,24 +0,0 @@ -type; - } - - public function variable(): ?VariableNode - { - return $this->variable; - } - - public function name(): ?string - { - if (null === $this->variable) { - return null; - } - - return $this->variable->name()->toString(); - } -} diff --git a/lib/DocblockParser/Ast/TagNode.php b/lib/DocblockParser/Ast/TagNode.php deleted file mode 100644 index 092c561860..0000000000 --- a/lib/DocblockParser/Ast/TagNode.php +++ /dev/null @@ -1,7 +0,0 @@ -type, [ - Token::T_PHPDOC_OPEN, - Token::T_PHPDOC_CLOSE, - Token::T_ASTERISK, - ])) { - return false; - } - if (str_contains($token->value, "\n")) { - return ' '; - } - return $token->value; - }, $this->tokens)))); - } -} diff --git a/lib/DocblockParser/Ast/Token.php b/lib/DocblockParser/Ast/Token.php deleted file mode 100644 index 558fd6fa3b..0000000000 --- a/lib/DocblockParser/Ast/Token.php +++ /dev/null @@ -1,65 +0,0 @@ -value; - } - - public function start(): int - { - return $this->byteOffset; - } - - public function end(): int - { - return $this->byteOffset + strlen($this->value); - } - - public function length(): int - { - return $this->end() - $this->start(); - } -} diff --git a/lib/DocblockParser/Ast/Tokens.php b/lib/DocblockParser/Ast/Tokens.php deleted file mode 100644 index a7fcf341ba..0000000000 --- a/lib/DocblockParser/Ast/Tokens.php +++ /dev/null @@ -1,208 +0,0 @@ - - */ -final class Tokens implements IteratorAggregate -{ - public ?Token $current; - - private int $position = 0; - - /** - * @param Token[] $tokens - */ - public function __construct(private array $tokens) - { - if (count($tokens)) { - $this->current = $tokens[$this->position]; - } - } - - /** - * @return Token[] - */ - public function toArray(): array - { - return $this->tokens; - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->tokens); - } - - public function hasCurrent(): bool - { - return isset($this->tokens[$this->position]); - } - - public function hasAnother(): bool - { - return isset($this->tokens[$this->position + 1]); - } - - /** - * Return the current token and move the position ahead. - */ - public function chomp(?string $type = null): ?Token - { - if (!isset($this->tokens[$this->position])) { - return null; - } - - $token = $this->tokens[$this->position++]; - $this->current = @$this->tokens[$this->position]; - - if (null !== $type && $token->type !== $type) { - throw new RuntimeException(sprintf( - 'Expected type "%s" at position "%s": "%s" got "%s"', - $type, - $this->position, - implode('', array_map(function (Token $token) { - return $token->value; - }, $this->tokens)), - $token->type, - )); - } - - return $token; - } - - public function chompWhitespace(): void - { - while ($token = $this->chompIf(Token::T_WHITESPACE, Token::T_ASTERISK)) { - } - } - - /** - * Chomp only if the current node is the given type - */ - public function chompIf(string ...$types): ?Token - { - if ($this->current === null) { - return null; - } - - foreach ($types as $type) { - if ($this->current->type === $type) { - return $this->chomp($type); - } - } - - return null; - } - - public function ifNextIs(string $type): bool - { - $next = $this->next(); - if ($next && $next->type === $type) { - $this->current = @$this->tokens[++$this->position]; - return true; - } - - return false; - } - - public function ifOneOf(string ...$types): bool - { - foreach ($types as $type) { - if (true === $this->if($type)) { - return true; - } - } - - return false; - } - - /** - * If the current or next non-whitespace node matches, - * advance internal pointer and return true; - */ - public function if(string $type): bool - { - if (null === $this->current) { - return false; - } - - if ($this->current->type === $type) { - return true; - } - - $offset = 0; - while ($peek = $this->peek($offset)) { - if ( - $peek->type === Token::T_WHITESPACE || - $peek->type === Token::T_ASTERISK - ) { - $offset++; - continue; - } - if ($peek->type === $type) { - $this->current = $peek; - $this->position += $offset; - return true; - } - return false; - } - - return false; - } - - public function next(): ?Token - { - if (!isset($this->tokens[$this->position + 1])) { - return null; - } - - return $this->tokens[$this->position + 1]; - } - - public function peekIs(int $offset, string $type): bool - { - $token = $this->peek($offset); - - if ($token && $token->type === $type) { - return true; - } - - return false; - } - - public function peek(int $offset): ?Token - { - if (!isset($this->tokens[$this->position + $offset])) { - return null; - } - - return $this->tokens[$this->position + $offset]; - } - - public function mustGetCurrent(): Token - { - if (!$this->current) { - throw new RuntimeException( - 'There is no current token (current is NULL)' - ); - } - return $this->current; - } - - public function mustChomp(?string $type = null): Token - { - $chomped = $this->chomp($type); - if (null === $chomped) { - throw new RuntimeException('Could not chomp, nothing to chomp!'); - } - return $chomped; - } -} diff --git a/lib/DocblockParser/Ast/Type/ArrayNode.php b/lib/DocblockParser/Ast/Type/ArrayNode.php deleted file mode 100644 index 265b8f240c..0000000000 --- a/lib/DocblockParser/Ast/Type/ArrayNode.php +++ /dev/null @@ -1,17 +0,0 @@ -name; - } -} diff --git a/lib/DocblockParser/Ast/Type/ConstantNode.php b/lib/DocblockParser/Ast/Type/ConstantNode.php deleted file mode 100644 index b67a51e35d..0000000000 --- a/lib/DocblockParser/Ast/Type/ConstantNode.php +++ /dev/null @@ -1,22 +0,0 @@ - $parameters - */ - public function __construct( - public Token $open, - public TypeNode $type, - public TypeList $parameters, - public Token $close - ) { - } - - public function close(): Token - { - return $this->close; - } - - public function open(): Token - { - return $this->open; - } - - /** - * @return TypeList - */ - public function parameters(): TypeList - { - return $this->parameters; - } - - public function type(): TypeNode - { - return $this->type; - } -} diff --git a/lib/DocblockParser/Ast/Type/IntersectionNode.php b/lib/DocblockParser/Ast/Type/IntersectionNode.php deleted file mode 100644 index c72741da10..0000000000 --- a/lib/DocblockParser/Ast/Type/IntersectionNode.php +++ /dev/null @@ -1,17 +0,0 @@ -type; - } - - public function listChars(): Token - { - return $this->listChars; - } -} diff --git a/lib/DocblockParser/Ast/Type/ListNode.php b/lib/DocblockParser/Ast/Type/ListNode.php deleted file mode 100644 index 3218a1b590..0000000000 --- a/lib/DocblockParser/Ast/Type/ListNode.php +++ /dev/null @@ -1,17 +0,0 @@ -null; - } -} diff --git a/lib/DocblockParser/Ast/Type/NullableNode.php b/lib/DocblockParser/Ast/Type/NullableNode.php deleted file mode 100644 index 5dc2992701..0000000000 --- a/lib/DocblockParser/Ast/Type/NullableNode.php +++ /dev/null @@ -1,30 +0,0 @@ -nullable; - } - - public function type(): TypeNode - { - return $this->type; - } -} diff --git a/lib/DocblockParser/Ast/Type/ParenthesizedType.php b/lib/DocblockParser/Ast/Type/ParenthesizedType.php deleted file mode 100644 index 43c715c752..0000000000 --- a/lib/DocblockParser/Ast/Type/ParenthesizedType.php +++ /dev/null @@ -1,22 +0,0 @@ -name; - } -} diff --git a/lib/DocblockParser/Ast/Type/ThisNode.php b/lib/DocblockParser/Ast/Type/ThisNode.php deleted file mode 100644 index 853f22aadf..0000000000 --- a/lib/DocblockParser/Ast/Type/ThisNode.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ -class TypeList extends Node implements IteratorAggregate, Countable -{ - protected const CHILD_NAMES = [ - 'list' - ]; - - /** - * @param array $list - */ - public function __construct(public array $list) - { - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->list); - } - - public function count(): int - { - return count($this->list); - } - - public function types(): TypeNodes - { - return new TypeNodes(...array_filter($this->list, function (?Element $element) { - return $element instanceof TypeNode; - })); - } -} diff --git a/lib/DocblockParser/Ast/TypeNode.php b/lib/DocblockParser/Ast/TypeNode.php deleted file mode 100644 index fa31fbe27c..0000000000 --- a/lib/DocblockParser/Ast/TypeNode.php +++ /dev/null @@ -1,7 +0,0 @@ - - */ -class TypeNodes implements IteratorAggregate, Countable -{ - /** - * @var TypeNode[] - */ - private array $types; - - public function __construct(TypeNode ...$types) - { - $this->types = $types; - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->types); - } - - public function first(): TypeNode - { - foreach ($this->types as $type) { - return $type; - } - - throw new RuntimeException(sprintf( - 'List has no first element' - )); - } - - public function count(): int - { - return count($this->types); - } -} diff --git a/lib/DocblockParser/Ast/UnknownTag.php b/lib/DocblockParser/Ast/UnknownTag.php deleted file mode 100644 index c1f56e71e7..0000000000 --- a/lib/DocblockParser/Ast/UnknownTag.php +++ /dev/null @@ -1,13 +0,0 @@ -null; - } - - public function value() - { - return null; - } -} diff --git a/lib/DocblockParser/Ast/Value/UnkownValue.php b/lib/DocblockParser/Ast/Value/UnkownValue.php deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/DocblockParser/Ast/ValueNode.php b/lib/DocblockParser/Ast/ValueNode.php deleted file mode 100644 index 9d4f3ddadd..0000000000 --- a/lib/DocblockParser/Ast/ValueNode.php +++ /dev/null @@ -1,11 +0,0 @@ -name; - } -} diff --git a/lib/DocblockParser/DocblockParser.php b/lib/DocblockParser/DocblockParser.php deleted file mode 100644 index b7fde6de17..0000000000 --- a/lib/DocblockParser/DocblockParser.php +++ /dev/null @@ -1,33 +0,0 @@ -parser->parse($this->lexer->lex($docblock)); - if (!$node instanceof Docblock) { - throw new RuntimeException(sprintf( - 'Expected a Docblock node from parser, but got "%s"', - get_class($node) - )); - } - - return $node; - } -} diff --git a/lib/DocblockParser/Lexer.php b/lib/DocblockParser/Lexer.php deleted file mode 100644 index d0bc75baaa..0000000000 --- a/lib/DocblockParser/Lexer.php +++ /dev/null @@ -1,157 +0,0 @@ -', // brackets - '\$[a-zA-Z0-9_\x80-\xff]+', // variable - self::PATTERN_LABEL, // label - '"' . self::PATTERN_LABEL . '"', - '\'' . self::PATTERN_LABEL . '\'', - '[0-9]+\.[0-9]+', - '[0-9]+', - ]; - private const TOKEN_VALUE_MAP = [ - ']' => Token::T_BRACKET_SQUARE_CLOSE, - '[' => Token::T_BRACKET_SQUARE_OPEN, - '>' => Token::T_BRACKET_ANGLE_CLOSE, - '<' => Token::T_BRACKET_ANGLE_OPEN, - '{' => Token::T_BRACKET_CURLY_OPEN, - '}' => Token::T_BRACKET_CURLY_CLOSE, - '(' => Token::T_PAREN_OPEN, - ')' => Token::T_PAREN_CLOSE, - ',' => Token::T_COMMA, - '[]' => Token::T_LIST, - '?' => Token::T_NULLABLE, - '|' => Token::T_BAR, - '&' => Token::T_AMPERSAND, - '=' => Token::T_EQUALS, - ':' => Token::T_COLON, - '::' => Token::T_DOUBLE_COLON, - '!' => Token::T_BANG, - ]; - - /** - * @var string[] - */ - private const IGNORE_PATTERNS = [ - '\s+', - ]; - - private string $pattern; - - public function __construct() - { - $this->pattern = sprintf( - '{(%s)|%s}', - implode(')|(', self::PATTERNS), - implode('|', self::IGNORE_PATTERNS) - ); - } - - public function lex(string $docblock): Tokens - { - $chunks = preg_split( - $this->pattern, - $docblock, - -1, - PREG_SPLIT_NO_EMPTY - | PREG_SPLIT_DELIM_CAPTURE - | PREG_SPLIT_OFFSET_CAPTURE - ); - - if (false === $chunks) { - throw new RuntimeException( - 'Unexpected error from preg_split' - ); - } - - - $tokens = []; - foreach ((array)$chunks as $chunk) { - [ $value, $offset ] = $chunk; - $tokens[] = new Token( - $offset, - $this->resolveType($value), - $value - ); - } - - return new Tokens($tokens); - } - - private function resolveType(string $value): string - { - if ($value[0] === '`') { - return Token::T_INLINE_CODE; - } - - if (str_contains($value, '/*')) { - return Token::T_PHPDOC_OPEN; - } - - if (str_contains($value, '*/')) { - return Token::T_PHPDOC_CLOSE; - } - - if (trim($value) === '*') { - return Token::T_ASTERISK; - } - - if (array_key_exists($value, self::TOKEN_VALUE_MAP)) { - return self::TOKEN_VALUE_MAP[$value]; - } - - if (is_numeric($value)) { - if (str_contains($value, '.')) { - return Token::T_FLOAT; - } - return Token::T_INTEGER; - } - - if ($value[0] === '"' || $value[0] == '\'') { - return Token::T_QUOTED_STRING; - } - - if ($value[0] === '$') { - return Token::T_VARIABLE; - } - - if ($value[0] === '@') { - return Token::T_TAG; - } - if (trim($value) === '') { - return Token::T_WHITESPACE; - } - - if (ctype_alpha($value[0]) || $value[0] === '\\') { - return Token::T_LABEL; - } - - return Token::T_UNKNOWN; - } -} diff --git a/lib/DocblockParser/Parser.php b/lib/DocblockParser/Parser.php deleted file mode 100644 index d8d789907f..0000000000 --- a/lib/DocblockParser/Parser.php +++ /dev/null @@ -1,794 +0,0 @@ -tokens = $tokens; - - while ($tokens->hasCurrent()) { - /** @phpstan-ignore-next-line Above ensures it is not null */ - if ($tokens->current->type === Token::T_TAG) { - $children[] = $this->parseTag(); - continue; - } - $children[] = $tokens->chomp(); - } - - if (count($children) === 1) { - $node = reset($children); - if ($node instanceof Node) { - return $node; - } - } - - /** @phpstan-ignore-next-line */ - return new Docblock($children); - } - - private function parseTag(): TagNode - { - $token = $this->tokens->mustGetCurrent(); - $value = str_replace(['@psalm-', '@phpstan-'], '@', $token->value); - return match ($value) { - '@param' => $this->parseParam(), - '@var' => $this->parseVar(), - '@throws' => $this->parseThrows(), - '@deprecated' => $this->parseDeprecated(), - '@method' => $this->parseMethod(), - '@assert' => $this->parseAssert(), - '@type' => $this->parseTypeAlias(), - '@property', '@property-read' => $this->parseProperty(), - '@mixin' => $this->parseMixin(), - '@return' => $this->parseReturn(), - '@template' => $this->parseTemplate(), - '@template-covariant' => $this->parseTemplate(), - '@extends', '@template-extends' => $this->parseExtends(), - '@implements', '@template-implements' => $this->parseImplements(), - default => new UnknownTag($this->tokens->mustChomp()), - }; - } - - private function parseParam(): ParamTag - { - $type = $variable = $textNode = null; - $tag = $this->tokens->mustChomp(Token::T_TAG); - - if ($this->ifType()) { - $type = $this->parseTypes(); - } - - if ($this->tokens->ifNextIs(Token::T_VARIABLE)) { - $variable = $this->parseVariable(); - } - - return new ParamTag($tag, $type, $variable, $this->parseText() ?: new TextNode([])); - } - - private function parseVar(): VarTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $type = $variable = null; - if ($this->ifType()) { - $type = $this->parseTypes(); - } - if ($this->tokens->ifNextIs(Token::T_VARIABLE)) { - $variable = $this->parseVariable(); - } - - return new VarTag($tag, $type, $variable); - } - - private function parseThrows(): ThrowsTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $type = null; - - if ($this->tokens->if(Token::T_LABEL)) { - $type = $this->parseTypes(); - $this->tokens->chompWhitespace(); - } - - $text = $this->parseText(); - - return new ThrowsTag($tag, $type, $text); - } - - private function parseMethod(): MethodTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $type = $name = $parameterList = $open = $close = null; - $static = null; - - if ($this->tokens->ifNextIs(Token::T_LABEL)) { - if ($this->tokens->mustGetCurrent()->value === 'static') { - $static = $this->tokens->chomp(); - } - } - - if ($this->ifType() || $this->tokens->if(Token::T_VARIABLE)) { - $type = $this->parseTypes(); - } - - if ($this->tokens->if(Token::T_LABEL)) { - $name = $this->tokens->chomp(); - } - - if ($this->tokens->if(Token::T_PAREN_OPEN)) { - $open = $this->tokens->chomp(Token::T_PAREN_OPEN); - $parameterList = $this->parseParameterList(); - $close = $this->tokens->chompIf(Token::T_PAREN_CLOSE); - } - - return new MethodTag($tag, $type, $name, $static, $open, $parameterList, $close, $this->parseText()); - } - - private function parseProperty(): PropertyTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $type = $name = null; - if ($this->ifType()) { - $type = $this->parseTypes(); - } - if ($this->tokens->ifNextIs(Token::T_VARIABLE)) { - $name = $this->tokens->chomp(); - } - - return new PropertyTag($tag, $type, $name); - } - - private function parseTypes(): ?TypeNode - { - $type = $this->parseType(); - if (null === $type) { - return $type; - } - $elements = [$type]; - $mode = null; - - while (true) { - if ( - $this->tokens->if(Token::T_BAR) || - $this->tokens->if(Token::T_AMPERSAND) - ) { - $delimiter = $this->tokens->mustChomp(); - if (!$mode) { - $mode = $delimiter->type; - } - - if ($mode !== $delimiter->type) { - continue; - } - - $elements[] = $delimiter; - $type = $this->parseType(); - if (null !== $type) { - $elements[] = $type; - continue; - } - } - break; - } - - $list = new TypeList($elements); - - if (count($list->list) === 1) { - return $list->types()->first(); - } - - if ($mode && $mode === Token::T_AMPERSAND) { - return new IntersectionNode($list); - } - return new UnionNode($list); - } - - private function parseType(): ?TypeNode - { - if (null === $this->tokens->current) { - return null; - } - - if ($this->tokens->current->type === Token::T_VARIABLE) { - if ($this->tokens->current->value === '$this') { - $variable = $this->tokens->mustChomp(Token::T_VARIABLE); - return new ThisNode($variable); - } - return $this->parseConditionalType(); - } - - if ($this->tokens->current->type === Token::T_NULLABLE) { - $nullable = $this->tokens->mustChomp(); - $type = $this->parseType(); - if ($type === null) { - return null; - } - return new NullableNode($nullable, $type); - } - - if ($this->tokens->current->type === Token::T_PAREN_OPEN) { - $open = $this->tokens->mustChomp(); - $this->tokens->chompWhitespace(); - $type = $this->parseTypes(); - $this->tokens->chompWhitespace(); - $close = $this->tokens->chompIf(Token::T_PAREN_CLOSE); - - return new ParenthesizedType($open, $type, $close); - } - - $type = $this->tokens->mustChomp(); - - /** @phpstan-ignore-next-line It can be null*/ - if (null === $this->tokens->current && $type) { - return $this->createTypeFromToken($type); - } - - $isList = false; - - if ($this->tokens->current->type === Token::T_PAREN_OPEN) { - $open = $this->tokens->chomp(); - - $typeList = null; - if ($this->tokens->if(Token::T_LABEL)) { - $typeList = $this->parseTypeList(); - } - - $close = $this->tokens->chomp(); - $returnType = null; - $colon = null; - - if ($this->tokens->if(Token::T_COLON)) { - $colon = $this->tokens->chomp(); - if ($this->tokens->if(Token::T_LABEL)) { - $returnType = $this->parseTypes(); - } - } - - return new CallableNode( - $type, - $open, - $typeList, - $close, - $colon, - $returnType, - ); - } - - if ($this->tokens->current->type === Token::T_BRACKET_ANGLE_OPEN) { - $open = $this->tokens->mustChomp(); - $typeList = null; - $variance = null; - if ($this->tokens->if(Token::T_VARIABLE)) { - $typeList = $this->parseTypeList(); - } - if ($this->tokens->if(Token::T_QUOTED_STRING)) { - $typeList = $this->parseTypeList(); - } - if ($this->tokens->if(Token::T_LABEL)) { - $typeList = $this->parseTypeList(); - } - if ($this->tokens->if(Token::T_INTEGER)) { - $typeList = $this->parseTypeList(); - } - if ($this->tokens->if(Token::T_NULLABLE)) { - $typeList = $this->parseTypeList(); - } - - if (!$this->tokens->if(Token::T_BRACKET_ANGLE_CLOSE)) { - return null; - } - - if (!$typeList) { - return null; - } - - $type = new GenericNode( - $open, - $this->createTypeFromToken($type), - $typeList, - $this->tokens->mustChomp() - ); - return $this->parseDimensions($type); - } - - if ($this->tokens->current->type === Token::T_BRACKET_CURLY_OPEN) { - $open = $this->tokens->chomp(); - assert(!is_null($open)); - $keyValues = []; - $close = null; - if ($this->tokens->ifOneOf(Token::T_LABEL, Token::T_INTEGER)) { - $keyValues = $this->parseArrayKeyValues(); - } - if ($this->tokens->if(Token::T_BRACKET_CURLY_CLOSE)) { - $close = $this->tokens->chomp(); - } - - $type = new ArrayShapeNode($open, new ArrayKeyValueList( - $keyValues, - ), $close); - - return $this->parseDimensions($type); - } - - return $this->parseDimensions($this->createTypeFromToken($type)); - } - - private function parseDimensions(TypeNode $type): TypeNode - { - while ($this->tokens->if(Token::T_LIST)) { - $list = $this->tokens->mustChomp(); - $type = new ListBracketsNode($type, $list); - } - - - return $type; - } - - private function createTypeFromToken(Token $type): TypeNode - { - if (strtolower($type->value) === 'null') { - return new NullNode($type); - } - if (strtolower($type->value) === 'array') { - return new ArrayNode($type); - } - if (strtolower($type->value) === 'list') { - return new ListNode($type); - } - if (in_array($type->value, self::SCALAR_TYPES)) { - return new ScalarNode($type); - } - if ($type->type === Token::T_QUOTED_STRING) { - return new LiteralStringNode($type); - } - if ($type->type === Token::T_FLOAT) { - return new LiteralFloatNode($type); - } - if ($type->type === Token::T_INTEGER) { - return new LiteralIntegerNode($type); - } - if ($type->type !== Token::T_LABEL) { - return new UnsupportedNode($type); - } - - $classNode = new ClassNode($type); - - if ( - $this->tokens->peekIs(0, Token::T_DOUBLE_COLON) && - ($this->tokens->peekIs(1, Token::T_LABEL) || $this->tokens->peekIs(1, Token::T_ASTERISK)) - ) { - return new ConstantNode( - $classNode, - $this->tokens->mustChomp(), - $this->tokens->mustChomp(), - ); - } - - return $classNode; - } - - private function parseVariable(): ?VariableNode - { - if ($this->tokens->mustGetCurrent()->type !== Token::T_VARIABLE) { - return null; - } - - $name = $this->tokens->mustChomp(Token::T_VARIABLE); - - return new VariableNode($name); - } - - private function parseTypeList(string $delimiter = ','): TypeList - { - $types = []; - while (true) { - if ($this->tokens->if(Token::T_LABEL)) { - if (in_array($this->tokens->mustGetCurrent()->value, [ - 'covariant', - 'contravariant', - 'invariant', - 'bivariant' - ])) { - $types[] = $this->tokens->mustChomp(); - $this->tokens->chompWhitespace(); - } - - $types[] = $this->parseTypes(); - } elseif ($this->tokens->if(Token::T_NULLABLE)) { - $types[] = $this->parseTypes(); - } elseif ($this->tokens->if(Token::T_QUOTED_STRING)) { - $types[] = $this->parseTypes(); - } elseif ($this->tokens->if(Token::T_INTEGER)) { - $types[] = $this->parseTypes(); - } elseif ($this->tokens->if(Token::T_VARIABLE)) { - $types[] = $this->parseTypes(); - } - if ($this->tokens->if(Token::T_COMMA)) { - $types[] = $this->tokens->mustChomp(); - continue; - } - break; - } - - return new TypeList($types); - } - - private function parseParameterList(): ?ParameterList - { - if ($this->tokens->if(Token::T_PAREN_CLOSE)) { - return null; - } - - $parameters = []; - while (true) { - $parameters[] = $this->parseParameter(); - if ($this->tokens->if(Token::T_COMMA)) { - $parameters[] = $this->tokens->mustChomp(); - continue; - } - break; - } - - return new ParameterList($parameters); - } - - private function parseParameter(): ParameterTag - { - $type = $name = $default = null; - if ($this->tokens->if(Token::T_LABEL)) { - $type = $this->parseTypes(); - } - if ($this->tokens->if(Token::T_VARIABLE)) { - $name = $this->parseVariable(); - } - if ($this->tokens->if(Token::T_EQUALS)) { - $equals = $this->tokens->chomp(); - $default = $this->parseValue(); - } - return new ParameterTag($type, $name, $default); - } - - private function parseDeprecated(): DeprecatedTag - { - return new DeprecatedTag( - $this->tokens->mustChomp(Token::T_TAG), - $this->parseText() - ); - } - - private function parseMixin(): MixinTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $type = null; - - if ($this->tokens->if(Token::T_LABEL)) { - $type = $this->parseTypes(); - if (!$type instanceof ClassNode && !$type instanceof GenericNode) { - $type = null; - } - } - - return new MixinTag($tag, $type); - } - - private function parseReturn(): ReturnTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $type = null; - - if ($this->ifType()) { - $type = $this->parseTypes(); - } - - if ($this->tokens->if(Token::T_VARIABLE)) { - $variable = $this->tokens->mustChomp(Token::T_VARIABLE); - if ($variable->value === '$this') { - $type = new ThisNode($variable); - } - } - - return new ReturnTag($tag, $type, $this->parseText()); - } - - /** - * Parse text until the next tag - * - * This method assumes that any prose after a tag belongs to the tag. - */ - private function parseText(): ?TextNode - { - if (null === $this->tokens->current) { - return null; - } - - $text = []; - - while ($this->tokens->current) { - if ($this->tokens->current->type === Token::T_PHPDOC_CLOSE) { - break; - } - if ($this->tokens->current->type === Token::T_TAG) { - break; - } - $text[] = $this->tokens->mustChomp(); - } - - if ($text) { - return new TextNode($text); - } - - return null; - } - - private function ifType(): bool - { - return $this->tokens->if(Token::T_LABEL) || - $this->tokens->if(Token::T_NULLABLE) || - $this->tokens->if(Token::T_QUOTED_STRING) || - $this->tokens->if(Token::T_INTEGER) || - $this->tokens->if(Token::T_FLOAT) || - $this->tokens->if(Token::T_PAREN_OPEN); - } - - private function parseValue(): ?ValueNode - { - if ($this->tokens->if(Token::T_LABEL)) { - if (strtolower($this->tokens->mustGetCurrent()->value) === 'null') { - return new NullValue($this->tokens->mustChomp()); - } - } - - return null; - } - - private function parseTemplate(): TemplateTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $placeholder = null; - $of = null; - $type = null; - - if ($this->tokens->if(Token::T_LABEL)) { - $placeholder = $this->tokens->mustChomp(); - } - - if ($this->tokens->if(Token::T_LABEL)) { - $of = $this->tokens->mustChomp(); - if ($of->value === 'of') { - /** @phpstan-ignore-next-line */ - if ($this->tokens->if(Token::T_LABEL)) { - $type = $this->parseTypes(); - } - } else { - $of = null; - } - } - - return new TemplateTag($tag, $placeholder, $of, $type); - } - - private function parseExtends(): ExtendsTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $type = null; - - if ($this->tokens->if(Token::T_LABEL)) { - $type = $this->parseTypes(); - } - - return new ExtendsTag($tag, $type); - } - - private function parseImplements(): ImplementsTag - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $types = []; - - if ($this->tokens->if(Token::T_LABEL)) { - $types = $this->parseTypeList()->list; - } - - return new ImplementsTag($tag, $types); - } - - /** - * @return array - */ - private function parseArrayKeyValues(): array - { - if ($this->tokens->if(Token::T_BRACKET_CURLY_CLOSE)) { - return []; - } - - $list = []; - while (true) { - /** @phpstan-ignore-next-line Condition is not always false */ - if ($this->tokens->if(Token::T_BRACKET_CURLY_CLOSE)) { - break; - } - $list[] = $this->parseArrayKeyValue(); - if ($this->tokens->if(Token::T_COMMA)) { - $token = $this->tokens->chomp(); - if ($token) { - $list[] = $token; - } - continue; - } - break; - } - - return $list; - } - - private function parseArrayKeyValue(): ArrayKeyValueNode - { - $key = $colon = $type = null; - - if ( - $this->tokens->ifOneOf(Token::T_LABEL, Token::T_INTEGER) && - $this->tokens->peekIs(1, Token::T_COLON) - ) { - $key = $this->tokens->chomp(); - $colon = $this->tokens->chomp(); - } - - if ( - $this->tokens->ifOneOf(Token::T_LABEL, Token::T_INTEGER) && - $this->tokens->peekIs(1, Token::T_NULLABLE) && - $this->tokens->peekIs(2, Token::T_COLON) - ) { - $key = $this->tokens->chomp(); - $_ = $this->tokens->chomp(); - $colon = $this->tokens->chomp(); - } - - $optional = null; - if ($this->tokens->if(Token::T_NULLABLE)) { - $optional = $this->tokens->chomp(); - } - $type = null; - if ($this->tokens->ifOneOf(Token::T_LABEL, Token::T_INTEGER, Token::T_QUOTED_STRING)) { - $type = $this->parseTypes(); - } - - return new ArrayKeyValueNode($optional, $key, $colon, $type); - } - - private function parseConditionalType(): TypeNode - { - $variable = $this->parseVariable(); - if (!$variable) { - throw new RuntimeException('Expected a variable, this should not happen'); - } - if (!$this->tokens->if(Token::T_LABEL)) { - return new ConditionalNode($variable); - } - $is = $this->tokens->mustChomp(); - if ($is->toString() !== 'is') { - return new ConditionalNode($variable, $is); - } - $this->tokens->chompWhitespace(); - $isType = $this->parseType(); - $this->tokens->chompWhitespace(); - if (!$question = $this->tokens->chompIf(Token::T_NULLABLE)) { - return new ConditionalNode($variable, $is, $isType); - } - $this->tokens->chompWhitespace(); - $left = $this->parseTypes(); - $this->tokens->chompWhitespace(); - if (!$colon = $this->tokens->chompIf(Token::T_COLON)) { - return new ConditionalNode($variable, $is, $isType); - } - $this->tokens->chompWhitespace(); - $right = $this->parseTypes(); - - return new ConditionalNode($variable, $is, $isType, $question, $left, $colon, $right); - } - - private function parseTypeAlias(): TagNode - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $alias = $equals = $type = null; - - if ($this->tokens->if(Token::T_LABEL)) { - $alias = $this->parseType(); - } - - if ($this->tokens->if(Token::T_EQUALS)) { - $equals = $this->tokens->chomp(Token::T_EQUALS); - } - - if ($this->tokens->if(Token::T_LABEL)) { - $type = $this->parseTypes(); - } - - return new TypeAliasTag($tag, $alias, $equals, $type); - } - - private function parseAssert(): TagNode - { - $tag = $this->tokens->mustChomp(Token::T_TAG); - $paramName = $type = $negOrEquality = null; - - if ($this->tokens->if(Token::T_EQUALS)) { - $negOrEquality = $this->tokens->mustChomp(Token::T_EQUALS); - } - - if ($this->tokens->if(Token::T_BANG)) { - $negation = $this->tokens->mustChomp(Token::T_BANG); - $negOrEquality = $negation; - } - - if ($this->tokens->if(Token::T_LABEL)) { - $type = $this->parseType(); - } - - if ($this->tokens->if(Token::T_VARIABLE)) { - $paramName = $this->parseVariable(); - } - - return new AssertTag($tag, $negOrEquality, $type, $paramName); - } -} diff --git a/lib/DocblockParser/Printer.php b/lib/DocblockParser/Printer.php deleted file mode 100644 index a09bcff664..0000000000 --- a/lib/DocblockParser/Printer.php +++ /dev/null @@ -1,10 +0,0 @@ -indent++; - $out = sprintf('%s: = ', $node->shortName()); - foreach ($node->children() as $child) { - $out .= $this->printElement($child); - } - $this->indent--; - - return $out; - } - - /** - * @param Element|Element[] $element - */ - public function printElement($element): string - { - if ($element instanceof Token) { - return sprintf('%s', $element->value); - } - - if ($element instanceof Node) { - return $this->newLine() . $this->print($element); - } - - return implode('', array_map(function (Element $element) { - return $this->printElement($element); - }, (array)$element)); - } - - private function newLine(): string - { - return "\n".str_repeat(' ', $this->indent); - } -} diff --git a/lib/DocblockParser/Tests/Benchmark/AbstractParserBenchCase.php b/lib/DocblockParser/Tests/Benchmark/AbstractParserBenchCase.php deleted file mode 100644 index 0234663a09..0000000000 --- a/lib/DocblockParser/Tests/Benchmark/AbstractParserBenchCase.php +++ /dev/null @@ -1,37 +0,0 @@ -parse($doc); - } - - /** - * @Revs(5) - * @Iterations(10) - */ - public function benchAssert(): void - { - $this->parse((string)file_get_contents(__DIR__ . '/examples/assert.example')); - } - - abstract public function parse(string $doc): void; -} diff --git a/lib/DocblockParser/Tests/Benchmark/LexerBench.php b/lib/DocblockParser/Tests/Benchmark/LexerBench.php deleted file mode 100644 index 0ee842057d..0000000000 --- a/lib/DocblockParser/Tests/Benchmark/LexerBench.php +++ /dev/null @@ -1,56 +0,0 @@ -lex($docblock['docblock']); - } - - public function provideDocblock(): Generator - { - yield [ - 'docblock' => <<<'EOT' - /** - * This is some complicated method - * @since 5.2 - * - * @param Foobar $barfoo Does a barfoo and then returns - * @param Barfoo $foobar Performs a foobar and then runs away. - * - * @return Baz - */ - EOT - ]; - yield [ - 'docblock' => <<<'EOT' - /** - * Assert library. - * - * @author Benjamin Eberlei - * - * @method static bool allAlnum(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric for all values. - * @method static bool allBase64(string $value, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined for all values. - * @method static bool allBetween(mixed $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit for all values. - * @method static bool allBetweenExclusive(mixed $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater than a lower limit, and less than an upper limit for all values. - * @method static bool allBetweenLength(mixed $value, int $minLength, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string length is between min and max lengths for all values. - * @method static bool allBoolean(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is php boolean for all values. - * @method static bool allChoice(mixed $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices for all values. - * @method static bool allChoicesNotEmpty(array $values, array $choices, string|callable $message = null, string $propertyPath = null) Determines if the values array has every choice as key and that this choice has content for all values. - * @method static bool allClassExists(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the class exists for all values. - * @method static bool allContains(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string contains a sequence of chars for all values. - * @method static bool allCount(array|Countable|ResourceBundle|SimpleXMLElement $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the count of countable is equal to count for all values. - * @method static bool allDate(string $value, string $format, string|callable $message = null, string $propertyPath = null) Assert that date is valid and corresponds to the given format for all values. - EOT - ]; - } -} diff --git a/lib/DocblockParser/Tests/Benchmark/PhpactorParserBench.php b/lib/DocblockParser/Tests/Benchmark/PhpactorParserBench.php deleted file mode 100644 index d727f0ec14..0000000000 --- a/lib/DocblockParser/Tests/Benchmark/PhpactorParserBench.php +++ /dev/null @@ -1,24 +0,0 @@ -parser = new Parser(); - $this->lexer = new Lexer(); - } - - public function parse(string $doc): void - { - $this->parser->parse($this->lexer->lex($doc)); - } -} diff --git a/lib/DocblockParser/Tests/Benchmark/examples/assert.example b/lib/DocblockParser/Tests/Benchmark/examples/assert.example deleted file mode 100644 index ddbbb3450f..0000000000 --- a/lib/DocblockParser/Tests/Benchmark/examples/assert.example +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Assert library. - * - * @author Benjamin Eberlei - * - * @method static bool allAlnum(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric for all values. - * @method static bool allBase64(string $value, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined for all values. - * @method static bool allBetween(mixed $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit for all values. - * @method static bool allBetweenExclusive(mixed $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater than a lower limit, and less than an upper limit for all values. - * @method static bool allBetweenLength(mixed $value, int $minLength, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string length is between min and max lengths for all values. - * @method static bool allBoolean(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is php boolean for all values. - * @method static bool allChoice(mixed $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices for all values. - * @method static bool allChoicesNotEmpty(array $values, array $choices, string|callable $message = null, string $propertyPath = null) Determines if the values array has every choice as key and that this choice has content for all values. - * @method static bool allClassExists(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the class exists for all values. - * @method static bool allContains(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string contains a sequence of chars for all values. - * @method static bool allCount(array|Countable|ResourceBundle|SimpleXMLElement $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the count of countable is equal to count for all values. - * @method static bool allDate(string $value, string $format, string|callable $message = null, string $propertyPath = null) Assert that date is valid and corresponds to the given format for all values. - * @method static bool allDefined(mixed $constant, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined for all values. - * @method static bool allDigit(mixed $value, string|callable $message = null, string $propertyPath = null) Validates if an integer or integerish is a digit for all values. - * @method static bool allDirectory(string $value, string|callable $message = null, string $propertyPath = null) Assert that a directory exists for all values. - * @method static bool allE164(string $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number for all values. - * @method static bool allEmail(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL) for all values. - * @method static bool allEndsWith(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars for all values. - * @method static bool allEq(mixed $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==) for all values. - * @method static bool allEqArraySubset(mixed $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset for all values. - * @method static bool allExtensionLoaded(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded for all values. - * @method static bool allExtensionVersion(string $extension, string $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded and a specific version is installed for all values. - * @method static bool allFalse(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean False for all values. - * @method static bool allFile(string $value, string|callable $message = null, string $propertyPath = null) Assert that a file exists for all values. - * @method static bool allFloat(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php float for all values. - * @method static bool allGreaterOrEqualThan(mixed $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater or equal than given limit for all values. - * @method static bool allGreaterThan(mixed $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater than given limit for all values. - * @method static bool allImplementsInterface(mixed $class, string $interfaceName, string|callable $message = null, string $propertyPath = null) Assert that the class implements the interface for all values. - * @method static bool allInArray(mixed $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices. This is an alias of Assertion::choice() for all values. - * @method static bool allInteger(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer for all values. - * @method static bool allIntegerish(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer'ish for all values. - * @method static bool allInterfaceExists(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the interface exists for all values. - * @method static bool allIp(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 or IPv6 address for all values. - * @method static bool allIpv4(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address for all values. - * @method static bool allIpv6(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv6 address for all values. - * @method static bool allIsArray(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array for all values. - * @method static bool allIsArrayAccessible(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or an array-accessible object for all values. - * @method static bool allIsCallable(mixed $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is callable for all values. - * @method static bool allIsCountable(array|Countable|ResourceBundle|SimpleXMLElement $value, string|callable $message = null, string $propertyPath = null) Assert that value is countable for all values. - * @method static bool allIsInstanceOf(mixed $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is instance of given class-name for all values. - * @method static bool allIsJsonString(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid json string for all values. - * @method static bool allIsObject(mixed $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is an object for all values. - * @method static bool allIsResource(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a resource for all values. - * @method static bool allIsTraversable(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or a traversable object for all values. - * @method static bool allKeyExists(mixed $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array for all values. - * @method static bool allKeyIsset(mixed $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object using isset() for all values. - * @method static bool allKeyNotExists(mixed $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key does not exist in an array for all values. - * @method static bool allLength(mixed $value, int $length, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string has a given length for all values. - * @method static bool allLessOrEqualThan(mixed $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less or equal than given limit for all values. - * @method static bool allLessThan(mixed $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less than given limit for all values. - * @method static bool allMax(mixed $value, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that a number is smaller as a given limit for all values. - * @method static bool allMaxCount(array|Countable|ResourceBundle|SimpleXMLElement $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at most $count elements for all values. - * @method static bool allMaxLength(mixed $value, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string value is not longer than $maxLength chars for all values. - * @method static bool allMethodExists(string $value, mixed $object, string|callable $message = null, string $propertyPath = null) Determines that the named method is defined in the provided object for all values. - * @method static bool allMin(mixed $value, mixed $minValue, string|callable $message = null, string $propertyPath = null) Assert that a value is at least as big as a given limit for all values. - * @method static bool allMinCount(array|Countable|ResourceBundle|SimpleXMLElement $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at least $count elements for all values. - * @method static bool allMinLength(mixed $value, int $minLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that a string is at least $minLength chars long for all values. - * @method static bool allNoContent(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is empty for all values. - * @method static bool allNotBlank(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is not blank for all values. - * @method static bool allNotContains(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string does not contains a sequence of chars for all values. - * @method static bool allNotEmpty(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is not empty for all values. - * @method static bool allNotEmptyKey(mixed $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object and its value is not empty for all values. - * @method static bool allNotEq(mixed $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not equal (using ==) for all values. - * @method static bool allNotInArray(mixed $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is not in array of choices for all values. - * @method static bool allNotIsInstanceOf(mixed $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is not instance of given class-name for all values. - * @method static bool allNotNull(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is not null for all values. - * @method static bool allNotRegex(mixed $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value does not match a regex for all values. - * @method static bool allNotSame(mixed $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not the same (using ===) for all values. - * @method static bool allNull(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is null for all values. - * @method static bool allNumeric(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is numeric for all values. - * @method static bool allObjectOrClass(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the value is an object, or a class that exists for all values. - * @method static bool allPhpVersion(string $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert on PHP version for all values. - * @method static bool allPropertiesExist(mixed $value, array $properties, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the properties all exist for all values. - * @method static bool allPropertyExists(mixed $value, string $property, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the property exists for all values. - * @method static bool allRange(mixed $value, mixed $minValue, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that value is in range of numbers for all values. - * @method static bool allReadable(string $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something readable for all values. - * @method static bool allRegex(mixed $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value matches a regex for all values. - * @method static bool allSame(mixed $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are the same (using ===) for all values. - * @method static bool allSatisfy(mixed $value, callable $callback, string|callable $message = null, string $propertyPath = null) Assert that the provided value is valid according to a callback for all values. - * @method static bool allScalar(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a PHP scalar for all values. - * @method static bool allStartsWith(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string starts with a sequence of chars for all values. - * @method static bool allString(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a string for all values. - * @method static bool allSubclassOf(mixed $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is subclass of given class-name for all values. - * @method static bool allTrue(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean True for all values. - * @method static bool allUniqueValues(array $values, string|callable $message = null, string $propertyPath = null) Assert that values in array are unique (using strict equality) for all values. - * @method static bool allUrl(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an URL for all values. - * @method static bool allUuid(string $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid UUID for all values. - * @method static bool allVersion(string $version1, string $operator, string $version2, string|callable $message = null, string $propertyPath = null) Assert comparison of two versions for all values. - * @method static bool allWriteable(string $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something writeable for all values. - * @method static bool nullOrAlnum(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric or that the value is null. - * @method static bool nullOrBase64(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined or that the value is null. - * @method static bool nullOrBetween(mixed|null $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit or that the value is null. - * @method static bool nullOrBetweenExclusive(mixed|null $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater than a lower limit, and less than an upper limit or that the value is null. - * @method static bool nullOrBetweenLength(mixed|null $value, int $minLength, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string length is between min and max lengths or that the value is null. - * @method static bool nullOrBoolean(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is php boolean or that the value is null. - * @method static bool nullOrChoice(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices or that the value is null. - * @method static bool nullOrChoicesNotEmpty(array|null $values, array $choices, string|callable $message = null, string $propertyPath = null) Determines if the values array has every choice as key and that this choice has content or that the value is null. - * @method static bool nullOrClassExists(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the class exists or that the value is null. - * @method static bool nullOrContains(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string contains a sequence of chars or that the value is null. - * @method static bool nullOrCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the count of countable is equal to count or that the value is null. - * @method static bool nullOrDate(string|null $value, string $format, string|callable $message = null, string $propertyPath = null) Assert that date is valid and corresponds to the given format or that the value is null. - * @method static bool nullOrDefined(mixed|null $constant, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined or that the value is null. - * @method static bool nullOrDigit(mixed|null $value, string|callable $message = null, string $propertyPath = null) Validates if an integer or integerish is a digit or that the value is null. - * @method static bool nullOrDirectory(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a directory exists or that the value is null. - * @method static bool nullOrE164(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number or that the value is null. - * @method static bool nullOrEmail(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL) or that the value is null. - * @method static bool nullOrEndsWith(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars or that the value is null. - * @method static bool nullOrEq(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==) or that the value is null. - * @method static bool nullOrEqArraySubset(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset or that the value is null. - * @method static bool nullOrExtensionLoaded(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded or that the value is null. - * @method static bool nullOrExtensionVersion(string|null $extension, string $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded and a specific version is installed or that the value is null. - * @method static bool nullOrFalse(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean False or that the value is null. - * @method static bool nullOrFile(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a file exists or that the value is null. - * @method static bool nullOrFloat(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php float or that the value is null. - * @method static bool nullOrGreaterOrEqualThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater or equal than given limit or that the value is null. - * @method static bool nullOrGreaterThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater than given limit or that the value is null. - * @method static bool nullOrImplementsInterface(mixed|null $class, string $interfaceName, string|callable $message = null, string $propertyPath = null) Assert that the class implements the interface or that the value is null. - * @method static bool nullOrInArray(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices. This is an alias of Assertion::choice() or that the value is null. - * @method static bool nullOrInteger(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer or that the value is null. - * @method static bool nullOrIntegerish(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer'ish or that the value is null. - * @method static bool nullOrInterfaceExists(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the interface exists or that the value is null. - * @method static bool nullOrIp(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 or IPv6 address or that the value is null. - * @method static bool nullOrIpv4(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address or that the value is null. - * @method static bool nullOrIpv6(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv6 address or that the value is null. - * @method static bool nullOrIsArray(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or that the value is null. - * @method static bool nullOrIsArrayAccessible(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or an array-accessible object or that the value is null. - * @method static bool nullOrIsCallable(mixed|null $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is callable or that the value is null. - * @method static bool nullOrIsCountable(array|Countable|ResourceBundle|SimpleXMLElement|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is countable or that the value is null. - * @method static bool nullOrIsInstanceOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is instance of given class-name or that the value is null. - * @method static bool nullOrIsJsonString(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid json string or that the value is null. - * @method static bool nullOrIsObject(mixed|null $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is an object or that the value is null. - * @method static bool nullOrIsResource(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a resource or that the value is null. - * @method static bool nullOrIsTraversable(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or a traversable object or that the value is null. - * @method static bool nullOrKeyExists(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array or that the value is null. - * @method static bool nullOrKeyIsset(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object using isset() or that the value is null. - * @method static bool nullOrKeyNotExists(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key does not exist in an array or that the value is null. - * @method static bool nullOrLength(mixed|null $value, int $length, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string has a given length or that the value is null. - * @method static bool nullOrLessOrEqualThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less or equal than given limit or that the value is null. - * @method static bool nullOrLessThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less than given limit or that the value is null. - * @method static bool nullOrMax(mixed|null $value, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that a number is smaller as a given limit or that the value is null. - * @method static bool nullOrMaxCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at most $count elements or that the value is null. - * @method static bool nullOrMaxLength(mixed|null $value, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string value is not longer than $maxLength chars or that the value is null. - * @method static bool nullOrMethodExists(string|null $value, mixed $object, string|callable $message = null, string $propertyPath = null) Determines that the named method is defined in the provided object or that the value is null. - * @method static bool nullOrMin(mixed|null $value, mixed $minValue, string|callable $message = null, string $propertyPath = null) Assert that a value is at least as big as a given limit or that the value is null. - * @method static bool nullOrMinCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at least $count elements or that the value is null. - * @method static bool nullOrMinLength(mixed|null $value, int $minLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that a string is at least $minLength chars long or that the value is null. - * @method static bool nullOrNoContent(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is empty or that the value is null. - * @method static bool nullOrNotBlank(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not blank or that the value is null. - * @method static bool nullOrNotContains(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string does not contains a sequence of chars or that the value is null. - * @method static bool nullOrNotEmpty(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not empty or that the value is null. - * @method static bool nullOrNotEmptyKey(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object and its value is not empty or that the value is null. - * @method static bool nullOrNotEq(mixed|null $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not equal (using ==) or that the value is null. - * @method static bool nullOrNotInArray(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is not in array of choices or that the value is null. - * @method static bool nullOrNotIsInstanceOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is not instance of given class-name or that the value is null. - * @method static bool nullOrNotNull(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not null or that the value is null. - * @method static bool nullOrNotRegex(mixed|null $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value does not match a regex or that the value is null. - * @method static bool nullOrNotSame(mixed|null $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not the same (using ===) or that the value is null. - * @method static bool nullOrNull(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is null or that the value is null. - * @method static bool nullOrNumeric(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is numeric or that the value is null. - * @method static bool nullOrObjectOrClass(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is an object, or a class that exists or that the value is null. - * @method static bool nullOrPhpVersion(string|null $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert on PHP version or that the value is null. - * @method static bool nullOrPropertiesExist(mixed|null $value, array $properties, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the properties all exist or that the value is null. - * @method static bool nullOrPropertyExists(mixed|null $value, string $property, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the property exists or that the value is null. - * @method static bool nullOrRange(mixed|null $value, mixed $minValue, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that value is in range of numbers or that the value is null. - * @method static bool nullOrReadable(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something readable or that the value is null. - * @method static bool nullOrRegex(mixed|null $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value matches a regex or that the value is null. - * @method static bool nullOrSame(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are the same (using ===) or that the value is null. - * @method static bool nullOrSatisfy(mixed|null $value, callable $callback, string|callable $message = null, string $propertyPath = null) Assert that the provided value is valid according to a callback or that the value is null. - * @method static bool nullOrScalar(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a PHP scalar or that the value is null. - * @method static bool nullOrStartsWith(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string starts with a sequence of chars or that the value is null. - * @method static bool nullOrString(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a string or that the value is null. - * @method static bool nullOrSubclassOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is subclass of given class-name or that the value is null. - * @method static bool nullOrTrue(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean True or that the value is null. - * @method static bool nullOrUniqueValues(array|null $values, string|callable $message = null, string $propertyPath = null) Assert that values in array are unique (using strict equality) or that the value is null. - * @method static bool nullOrUrl(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an URL or that the value is null. - * @method static bool nullOrUuid(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid UUID or that the value is null. - * @method static bool nullOrVersion(string|null $version1, string $operator, string $version2, string|callable $message = null, string $propertyPath = null) Assert comparison of two versions or that the value is null. - * @method static bool nullOrWriteable(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something writeable or that the value is null. - */ - --- - diff --git a/lib/DocblockParser/Tests/Benchmark/examples/php_core.example b/lib/DocblockParser/Tests/Benchmark/examples/php_core.example deleted file mode 100644 index 32b500d3b8..0000000000 --- a/lib/DocblockParser/Tests/Benchmark/examples/php_core.example +++ /dev/null @@ -1,1043 +0,0 @@ - - * The argument offset. Function arguments are counted starting from - * zero. - *

- * @return mixed|false the specified argument, or false on error. - */ -#!---!# - -/** - * Returns an array comprising a function's argument list - * @link https://php.net/manual/en/function.func-get-args.php - * @return array an array in which each element is a copy of the corresponding - * member of the current user-defined function's argument list. - */ -#!---!# - -/** - * Get string length - * @link https://php.net/manual/en/function.strlen.php - * @param string $string

- * The string being measured for length. - *

- * @return int The length of the string on success, - * and 0 if the string is empty. - */ -#!---!# - -/** - * Binary safe string comparison - * @link https://php.net/manual/en/function.strcmp.php - * @param string $str1

- * The first string. - *

- * @param string $str2

- * The second string. - *

- * @return int < 0 if str1 is less than - * str2; > 0 if str1 - * is greater than str2, and 0 if they are - * equal. - */ -#!---!# - -/** - * Binary safe string comparison of the first n characters - * @link https://php.net/manual/en/function.strncmp.php - * @param string $str1

- * The first string. - *

- * @param string $str2

- * The second string. - *

- * @param int $len

- * Number of characters to use in the comparison. - *

- * @return int < 0 if str1 is less than - * str2; > 0 if str1 - * is greater than str2, and 0 if they are - * equal. - */ -#!---!# - -/** - * Binary safe case-insensitive string comparison - * @link https://php.net/manual/en/function.strcasecmp.php - * @param string $str1

- * The first string - *

- * @param string $str2

- * The second string - *

- * @return int < 0 if str1 is less than - * str2; > 0 if str1 - * is greater than str2, and 0 if they are - * equal. - */ -#!---!# - -/** - * Binary safe case-insensitive string comparison of the first n characters - * @link https://php.net/manual/en/function.strncasecmp.php - * @param string $str1

- * The first string. - *

- * @param string $str2

- * The second string. - *

- * @param int $len

- * The length of strings to be used in the comparison. - *

- * @return int < 0 if str1 is less than - * str2; > 0 if str1 is - * greater than str2, and 0 if they are equal. - */ -#!---!# - -/** - * The function returns {@see true} if the passed $haystack starts from the - * $needle string or {@see false} otherwise. - * - * @param string $haystack - * @param string $needle - * @return bool - * @since 8.0 - */ -#!---!# - -/** - * The function returns {@see true} if the passed $haystack ends with the - * $needle string or {@see false} otherwise. - * - * @param string $haystack - * @param string $needle - * @return bool - * @since 8.0 - */ -#!---!# - -/** - * Checks if $needle is found in $haystack and returns a boolean value - * (true/false) whether or not the $needle was found. - * - * @param string $haystack - * @param string $needle - * @return bool - * @since 8.0 - */ -#!---!# - -/** - * Return the current key and value pair from an array and advance the array cursor - * @link https://php.net/manual/en/function.each.php - * @param array|ArrayObject &$array

- * The input array. - *

- * @return array the current key and value pair from the array - * array. This pair is returned in a four-element - * array, with the keys 0, 1, - * key, and value. Elements - * 0 and key contain the key name of - * the array element, and 1 and value - * contain the data. - *

- *

- * If the internal pointer for the array points past the end of the - * array contents, each returns - * false. - * @deprecated 7.2 Use a foreach loop instead. - * @removed 8.0 - */ -#!---!# - -/** - * Sets which PHP errors are reported - * @link https://php.net/manual/en/function.error-reporting.php - * @param int $level [optional]

- * The new error_reporting - * level. It takes on either a bitmask, or named constants. Using named - * constants is strongly encouraged to ensure compatibility for future - * versions. As error levels are added, the range of integers increases, - * so older integer-based error levels will not always behave as expected. - *

- *

- * The available error level constants and the actual - * meanings of these error levels are described in the - * predefined constants. - * - * error_reporting level constants and bit values - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
valueconstant
1 - * E_ERROR - *
2 - * E_WARNING - *
4 - * E_PARSE - *
8 - * E_NOTICE - *
16 - * E_CORE_ERROR - *
32 - * E_CORE_WARNING - *
64 - * E_COMPILE_ERROR - *
128 - * E_COMPILE_WARNING - *
256 - * E_USER_ERROR - *
512 - * E_USER_WARNING - *
1024 - * E_USER_NOTICE - *
32767 - * E_ALL - *
2048 - * E_STRICT - *
4096 - * E_RECOVERABLE_ERROR - *
8192 - * E_DEPRECATED - *
16384 - * E_USER_DEPRECATED - *
- *

- * @return int the old error_reporting - * level or the current level if no level parameter is - * given. - */ -#!---!# - -/** - * Defines a named constant - * @link https://php.net/manual/en/function.define.php - * @param string $name

- * The name of the constant. - *

- * @param mixed $value

- * The value of the constant. - * In PHP 5, value must be a scalar value (integer, float, string, boolean, or null). - * In PHP 7, array values are also accepted. - * It is possible to define resource constants, - * however it is not recommended and may cause unpredictable behavior. - *

- * @param bool $case_insensitive [optional]

- * If set to true, the constant will be defined case-insensitive. - * The default behavior is case-sensitive; i.e. - * CONSTANT and Constant represent - * different values. - * Defining case-insensitive constants is deprecated as of PHP 7.3.0. - *

- *

- * Case-insensitive constants are stored as lower-case. - *

- * @return bool true on success or false on failure. - */ -#!---!# - -/** - * Checks whether a given named constant exists - * @link https://php.net/manual/en/function.defined.php - * @param string $name

- * The constant name. - *

- * @return bool true if the named constant given by name - * has been defined, false otherwise. - */ -#!---!# - -/** - * Returns the name of the class of an object - * @link https://php.net/manual/en/function.get-class.php - * @param object $object [optional]

- * The tested object. This parameter may be omitted when inside a class. - *

- * @return string|false

The name of the class of which object is an - * instance. Returns false if object is not an - * object. - *

- *

- * If object is omitted when inside a class, the - * name of that class is returned. - */ -#!---!# - -/** - * the "Late Static Binding" class name - * @link https://php.net/manual/en/function.get-called-class.php - * @return string|false The class name. Returns false if called from outside a class. - */ -#!---!# - -/** - * Retrieves the parent class name for object or class - * @link https://php.net/manual/en/function.get-parent-class.php - * @param mixed $object [optional]

- * The tested object or class name - *

- * @return string|false

The name of the parent class of the class of which - * object is an instance or the name. - *

- *

- * If the object does not have a parent false will be returned. - *

- *

- * If called without parameter outside object, this function returns false. - */ -#!---!# - -/** - * Checks if the class method exists - * @link https://php.net/manual/en/function.method-exists.php - * @param mixed $object

- * An object instance or a class name - *

- * @param string $method_name

- * The method name - *

- * @return bool true if the method given by method_name - * has been defined for the given object, false - * otherwise. - */ -#!---!# - -/** - * Checks if the object or class has a property - * @link https://php.net/manual/en/function.property-exists.php - * @param mixed $class

- * The class name or an object of the class to test for - *

- * @param string $property

- * The name of the property - *

- * @return bool true if the property exists, false if it doesn't exist or - * null in case of an error. - */ -#!---!# - -/** - * Checks if the trait exists - * @param string $traitname Name of the trait to check - * @param bool $autoload [optional] Whether to autoload if not already loaded. - * @return bool Returns TRUE if trait exists, FALSE if not, NULL in case of an error. - * @link https://secure.php.net/manual/en/function.trait-exists.php - * @since 5.4 - */ -#!---!# - -/** - * Checks if the class has been defined - * @link https://php.net/manual/en/function.class-exists.php - * @param string $class_name

- * The class name. The name is matched in a case-insensitive manner. - *

- * @param bool $autoload [optional]

- * Whether or not to call autoload by default. - *

- * @return bool true if class_name is a defined class, - * false otherwise. - */ -#!---!# - -/** - * Checks if the interface has been defined - * @link https://php.net/manual/en/function.interface-exists.php - * @param string $interface_name

- * The interface name - *

- * @param bool $autoload [optional]

- * Whether to call autoload or not by default. - *

- * @return bool true if the interface given by - * interface_name has been defined, false otherwise. - * @since 5.0.2 - */ -#!---!# - -/** - * Return true if the given function has been defined - * @link https://php.net/manual/en/function.function-exists.php - * @param string $function_name

- * The function name, as a string. - *

- * @return bool true if function_name exists and is a - * function, false otherwise. - *

- *

- * This function will return false for constructs, such as - * include_once and echo. - */ -#!---!# - -/** - * Creates an alias for a class - * @link https://php.net/manual/en/function.class-alias.php - * @param string $original The original class. - * @param string $alias The alias name for the class. - * @param bool $autoload [optional] Whether to autoload if the original class is not found. - * @return bool true on success or false on failure. - */ -#!---!# - -/** - * Returns an array with the names of included or required files - * @link https://php.net/manual/en/function.get-included-files.php - * @return string[] an array of the names of all files. - *

- *

- * The script originally called is considered an "included file," so it will - * be listed together with the files referenced by - * include and family. - *

- *

- * Files that are included or required multiple times only show up once in - * the returned array. - */ -#!---!# - -/** - * Alias of get_included_files - * @link https://php.net/manual/en/function.get-required-files.php - * @return string[] - */ -#!---!# - -/** - * Checks if the object has this class as one of its parents - * @link https://php.net/manual/en/function.is-subclass-of.php - * @param mixed $object

- * A class name or an object instance - *

- * @param string $class_name

- * The class name - *

- * @param bool $allow_string [optional]

- * If this parameter set to false, string class name as object is not allowed. - * This also prevents from calling autoloader if the class doesn't exist. - *

- * @return bool This function returns true if the object object, - * belongs to a class which is a subclass of - * class_name, false otherwise. - */ -#!---!# - -/** - * Checks if the object is of this class or has this class as one of its parents - * @link https://php.net/manual/en/function.is-a.php - * @param object|string $object

- * The tested object - *

- * @param string $class_name

- * The class name - *

- * @param bool $allow_string [optional]

- * If this parameter set to FALSE, string class name as object - * is not allowed. This also prevents from calling autoloader if the class doesn't exist. - *

- * @return bool TRUE if the object is of this class or has this class as one of - * its parents, FALSE otherwise. - */ -#!---!# - -/** - * Get the default properties of the class - * @link https://php.net/manual/en/function.get-class-vars.php - * @param string $class_name

- * The class name - *

- * @return array an associative array of declared properties visible from the - * current scope, with their default value. - * The resulting array elements are in the form of - * varname => value. - */ -#!---!# - -/** - * Gets the properties of the given object - * @link https://php.net/manual/en/function.get-object-vars.php - * @param object $object

- * An object instance. - *

- * @return array an associative array of defined object accessible non-static properties - * for the specified object in scope. If a property have - * not been assigned a value, it will be returned with a null value. - */ -#!---!# - -/** - * Gets the class methods' names - * @link https://php.net/manual/en/function.get-class-methods.php - * @param mixed $class_name

- * The class name or an object instance - *

- * @return array an array of method names defined for the class specified by - * class_name. In case of an error, it returns null. - */ -#!---!# - -/** - * Generates a user-level error/warning/notice message - * @link https://php.net/manual/en/function.trigger-error.php - * @param string $error_msg

- * The designated error message for this error. It's limited to 1024 - * characters in length. Any additional characters beyond 1024 will be - * truncated. - *

- * @param int $error_type [optional]

- * The designated error type for this error. It only works with the E_USER - * family of constants, and will default to E_USER_NOTICE. - *

- * @return bool This function returns false if wrong error_type is - * specified, true otherwise. - */ -#!---!# - -/** - * Alias of trigger_error - * @link https://php.net/manual/en/function.user-error.php - * @param string $message - * @param int $error_type [optional] - * @return bool This function returns false if wrong error_type is - * specified, true otherwise. - */ -#!---!# - -/** - * Sets a user-defined error handler function - * @link https://php.net/manual/en/function.set-error-handler.php - * @param callable|null $error_handler

- * The user function needs to accept two parameters: the error code, and a - * string describing the error. Then there are three optional parameters - * that may be supplied: the filename in which the error occurred, the - * line number in which the error occurred, and the context in which the - * error occurred (an array that points to the active symbol table at the - * point the error occurred). The function can be shown as: - *

- *

- * handler - * interrno - * stringerrstr - * stringerrfile - * interrline - * arrayerrcontext - * errno - * The first parameter, errno, contains the - * level of the error raised, as an integer. - * @param int $error_types [optional]

- * Can be used to mask the triggering of the - * error_handler function just like the error_reporting ini setting - * controls which errors are shown. Without this mask set the - * error_handler will be called for every error - * regardless to the setting of the error_reporting setting. - *

- * @return callable|null a string containing the previously defined error handler (if any). If - * the built-in error handler is used null is returned. null is also returned - * in case of an error such as an invalid callback. If the previous error handler - * was a class method, this function will return an indexed array with the class - * and the method name. - */ -#!---!# - -/** - * Restores the previous error handler function - * @link https://php.net/manual/en/function.restore-error-handler.php - * @return bool This function always returns true. - */ -#!---!# - -/** - * Sets a user-defined exception handler function - * @link https://php.net/manual/en/function.set-exception-handler.php - * @param callable|null $exception_handler

- * Name of the function to be called when an uncaught exception occurs. - * This function must be defined before calling - * set_exception_handler. This handler function - * needs to accept one parameter, which will be the exception object that - * was thrown. - * NULL may be passed instead, to reset this handler to its default state. - *

- * @return callable|null the name of the previously defined exception handler, or null on error. If - * no previous handler was defined, null is also returned. - */ -#!---!# - -/** - * Restores the previously defined exception handler function - * @link https://php.net/manual/en/function.restore-exception-handler.php - * @return bool This function always returns true. - */ -#!---!# - -/** - * Returns an array with the name of the defined classes - * @link https://php.net/manual/en/function.get-declared-classes.php - * @return array an array of the names of the declared classes in the current - * script. - *

- *

- * Note that depending on what extensions you have compiled or - * loaded into PHP, additional classes could be present. This means that - * you will not be able to define your own classes using these - * names. There is a list of predefined classes in the Predefined Classes section of - * the appendices. - */ -#!---!# - -/** - * Returns an array of all declared interfaces - * @link https://php.net/manual/en/function.get-declared-interfaces.php - * @return array an array of the names of the declared interfaces in the current - * script. - */ -#!---!# - -/** - * Returns an array of all declared traits - * @return array with names of all declared traits in values. Returns NULL in case of a failure. - * @link https://secure.php.net/manual/en/function.get-declared-traits.php - * @see class_uses() - * @since 5.4 - */ -#!---!# - -/** - * Returns an array of all defined functions - * @link https://php.net/manual/en/function.get-defined-functions.php - * @param bool $exclude_disabled [optional] Whether disabled functions should be excluded from the return value. - * @return array an multidimensional array containing a list of all defined - * functions, both built-in (internal) and user-defined. The internal - * functions will be accessible via $arr["internal"], and - * the user defined ones using $arr["user"] (see example - * below). - */ -#!---!# - -/** - * Returns an array of all defined variables - * @link https://php.net/manual/en/function.get-defined-vars.php - * @return array A multidimensional array with all the variables. - */ -#!---!# - -/** - * Create an anonymous (lambda-style) function - * @link https://php.net/manual/en/function.create-function.php - * @param string $args

- * The function arguments. - *

- * @param string $code

- * The function code. - *

- * @return string|false a unique function name as a string, or false on error. - * @deprecated 7.2 Use anonymous functions instead. - * @removed 8.0 - */ -#!---!# - -/** - * Returns the resource type - * @link https://php.net/manual/en/function.get-resource-type.php - * @param resource $handle

- * The evaluated resource handle. - *

- * @return string If the given handle is a resource, this function - * will return a string representing its type. If the type is not identified - * by this function, the return value will be the string - * Unknown. - *

- *

- * This function will return false and generate an error if - * handle is not a resource. - */ -#!---!# - -/** - * Returns an array with the names of all modules compiled and loaded - * @link https://php.net/manual/en/function.get-loaded-extensions.php - * @param bool $zend_extensions [optional]

- * Only return Zend extensions, if not then regular extensions, like - * mysqli are listed. Defaults to false (return regular extensions). - *

- * @return array an indexed array of all the modules names. - */ -#!---!# - -/** - * Find out whether an extension is loaded - * @link https://php.net/manual/en/function.extension-loaded.php - * @param string $name

- * The extension name. - *

- *

- * You can see the names of various extensions by using - * phpinfo or if you're using the - * CGI or CLI version of - * PHP you can use the -m switch to - * list all available extensions: - *

- * $ php -m
- * [PHP Modules]
- * xml
- * tokenizer
- * standard
- * sockets
- * session
- * posix
- * pcre
- * overload
- * mysql
- * mbstring
- * ctype
- * [Zend Modules]
- * 
- *

- * @return bool true if the extension identified by name - * is loaded, false otherwise. - */ -#!---!# - -/** - * Returns an array with the names of the functions of a module - * @link https://php.net/manual/en/function.get-extension-funcs.php - * @param string $module_name

- * The module name. - *

- *

- * This parameter must be in lowercase. - *

- * @return string[]|false an array with all the functions, or false if - * module_name is not a valid extension. - */ -#!---!# - -/** - * Returns an associative array with the names of all the constants and their values - * @link https://php.net/manual/en/function.get-defined-constants.php - * @param bool $categorize [optional]

- * Causing this function to return a multi-dimensional - * array with categories in the keys of the first dimension and constants - * and their values in the second dimension. - * - * define("MY_CONSTANT", 1); - * print_r(get_defined_constants(true)); - * - * The above example will output something similar to: - *

- * Array
- * (
- * [Core] => Array
- * (
- * [E_ERROR] => 1
- * [E_WARNING] => 2
- * [E_PARSE] => 4
- * [E_NOTICE] => 8
- * [E_CORE_ERROR] => 16
- * [E_CORE_WARNING] => 32
- * [E_COMPILE_ERROR] => 64
- * [E_COMPILE_WARNING] => 128
- * [E_USER_ERROR] => 256
- * [E_USER_WARNING] => 512
- * [E_USER_NOTICE] => 1024
- * [E_STRICT] => 2048
- * [E_RECOVERABLE_ERROR] => 4096
- * [E_DEPRECATED] => 8192
- * [E_USER_DEPRECATED] => 16384
- * [E_ALL] => 32767
- * [TRUE] => 1
- * )
- * [pcre] => Array
- * (
- * [PREG_PATTERN_ORDER] => 1
- * [PREG_SET_ORDER] => 2
- * [PREG_OFFSET_CAPTURE] => 256
- * [PREG_SPLIT_NO_EMPTY] => 1
- * [PREG_SPLIT_DELIM_CAPTURE] => 2
- * [PREG_SPLIT_OFFSET_CAPTURE] => 4
- * [PREG_GREP_INVERT] => 1
- * )
- * [user] => Array
- * (
- * [MY_CONSTANT] => 1
- * )
- * )
- * 
- *

- * @return array - */ -#!---!# - -/** - * Generates a backtrace - * @link https://php.net/manual/en/function.debug-backtrace.php - * @param int $options [optional]

- * As of 5.3.6, this parameter is a bitmask for the following options: - * - * debug_backtrace options - * - * - * - * - * - * - * - * - *
DEBUG_BACKTRACE_PROVIDE_OBJECT - * Whether or not to populate the "object" index. - *
DEBUG_BACKTRACE_IGNORE_ARGS - * Whether or not to omit the "args" index, and thus all the function/method arguments, - * to save memory. - *
- * Before 5.3.6, the only values recognized are true or false, which are the same as - * setting or not setting the DEBUG_BACKTRACE_PROVIDE_OBJECT option respectively. - *

- * @param int $limit [optional]

- * As of 5.4.0, this parameter can be used to limit the number of stack frames returned. - * By default (limit=0) it returns all stack frames. - *

- * @return array an array of associative arrays. The possible returned elements - * are as follows: - *

- *

- * - * Possible returned elements from debug_backtrace - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
&Name;&Type;Description
functionstring - * The current function name. See also - * __FUNCTION__. - *
lineinteger - * The current line number. See also - * __LINE__. - *
filestring - * The current file name. See also - * __FILE__. - *
classstring - * The current class name. See also - * __CLASS__ - *
objectobject - * The current object. - *
typestring - * The current call type. If a method call, "->" is returned. If a static - * method call, "::" is returned. If a function call, nothing is returned. - *
argsarray - * If inside a function, this lists the functions arguments. If - * inside an included file, this lists the included file name(s). - *
- */ -#!---!# - -/** - * Prints a backtrace - * @link https://php.net/manual/en/function.debug-print-backtrace.php - * @param int $options [optional]

- * As of 5.3.6, this parameter is a bitmask for the following options: - * - * debug_print_backtrace options - * - * - * - * - *
DEBUG_BACKTRACE_IGNORE_ARGS - * Whether or not to omit the "args" index, and thus all the function/method arguments, - * to save memory. - *
- *

- * @param int $limit [optional]

- * As of 5.4.0, this parameter can be used to limit the number of stack frames printed. - * By default (limit=0) it prints all stack frames. - *

- * @return void - */ -#!---!# - -/** - * Forces collection of any existing garbage cycles - * @link https://php.net/manual/en/function.gc-collect-cycles.php - * @return int number of collected cycles. - */ -#!---!# - -/** - * Returns status of the circular reference collector - * @link https://php.net/manual/en/function.gc-enabled.php - * @return bool true if the garbage collector is enabled, false otherwise. - */ -#!---!# - -/** - * Activates the circular reference collector - * @link https://php.net/manual/en/function.gc-enable.php - * @return void - */ -#!---!# - -/** - * Deactivates the circular reference collector - * @link https://php.net/manual/en/function.gc-disable.php - * @return void - */ -#!---!# - -/** - * Gets information about the garbage collector - * @link https://php.net/manual/en/function.gc-status.php - * @return array associative array with the following elements: - *
    - *
  • "runs"
  • - *
  • "collected"
  • - *
  • "threshold"
  • - *
  • "roots"
  • - *
- * @since 7.3 - */ -#!---!# - -/** - * Reclaims memory used by the Zend Engine memory manager - * @link https://php.net/manual/en/function.gc-mem-caches.php - * @return int Returns the number of bytes freed. - * @since 7.0 - */ -#!---!# - -/** - * Returns active resources - * @link https://php.net/manual/en/function.get-resources.php - * @param string $type [optional]

- * - * If defined, this will cause get_resources() to only return resources of the given type. A list of resource types is available. - * - * If the string Unknown is provided as the type, then only resources that are of an unknown type will be returned. - * - * If omitted, all resources will be returned. - *

- * @return array Returns an array of currently active resources, indexed by resource number. - * @since 7.0 - */ -#!---!# diff --git a/lib/DocblockParser/Tests/Unit/Ast/NodeTest.php b/lib/DocblockParser/Tests/Unit/Ast/NodeTest.php deleted file mode 100644 index 13aadc2ae8..0000000000 --- a/lib/DocblockParser/Tests/Unit/Ast/NodeTest.php +++ /dev/null @@ -1,201 +0,0 @@ - - */ - public function provideNode(): Generator - { - yield from $this->provideApiTest(); - yield from $this->provideDocblock(); - yield from $this->provideTags(); - yield from $this->provideTypes(); - } - - /** - * @return Generator - */ - private function provideApiTest(): Generator - { - yield [ - '@method static Baz\Bar bar(string $boo, string $baz)', - function (MethodTag $methodNode): void { - self::assertTrue($methodNode->hasChild(ClassNode::class)); - self::assertFalse($methodNode->hasChild(MethodTag::class)); - self::assertCount(7, iterator_to_array($methodNode->children())); - self::assertCount(1, iterator_to_array($methodNode->children(ClassNode::class))); - self::assertTrue($methodNode->hasDescendant(ScalarNode::class)); - /** @phpstan-ignore-next-line */ - self::assertFalse($methodNode->hasDescendant('NotExisting')); - self::assertCount(2, iterator_to_array($methodNode->descendantElements(ScalarNode::class))); - self::assertInstanceOf(ScalarNode::class, $methodNode->firstDescendant(ScalarNode::class)); - } - ]; - } - - /** - * @return Generator - */ - private function provideTags() - { - yield [ - '@method static Baz\Bar bar(string $boo, string $baz)', - function (MethodTag $methodNode): void { - self::assertEquals('@method static Baz\Bar bar(string $boo, string $baz)', $methodNode->toString()); - self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString()); - self::assertEquals('static', $methodNode->static->value); - self::assertEquals('Baz\Bar', $methodNode->type->toString()); - self::assertEquals('bar', $methodNode->name->toString()); - self::assertEquals('(', $methodNode->parenOpen->toString()); - self::assertEquals(')', $methodNode->parenClose->toString()); - self::assertTrue($methodNode->hasChild(ClassNode::class)); - self::assertFalse($methodNode->hasChild(MethodTag::class)); - } - ]; - yield [ - '@property Baz\Bar $foobar', - function (PropertyTag $property): void { - self::assertEquals('$foobar', $property->name->toString()); - } - ]; - - yield [ '@deprecated This is deprecated']; - yield 'deprecated' => [ - '/** @deprecated This is deprecated */', - function (Docblock $block): void { - self::assertTrue($block->hasTag(DeprecatedTag::class)); - } - ]; - - yield [ '/** This is docblock @deprecated Foo */']; - - yield [ - '/** `@deprecated` mentioned in prose is not a real tag */', - function (Docblock $block): void { - self::assertFalse($block->hasTag(DeprecatedTag::class)); - } - ]; - yield [ '@mixin Foo\Bar']; - yield [ '@param string $foo This is a parameter']; - yield ['@param Baz\Bar $foobar This is a parameter']; - yield ['@var Baz\Bar $foobar']; - yield ['@return Baz\Bar']; - yield ['@return $this']; - } - - /** - * @return Generator - */ - private function provideTypes(): Generator - { - yield 'scalar' => ['string']; - yield 'union' => [ - '@return string|int|bool|float|mixed', - function (ReturnTag $return): void { - $type = $return->type; - assert($type instanceof UnionNode); - self::assertInstanceOf(UnionNode::class, $type); - self::assertEquals('string', $type->types->types()->first()->toString()); - self::assertCount(5, $type->types->types()); - } - ]; - yield 'list' => [ - '@return Foo[]', - function (ReturnTag $return): void { - self::assertInstanceOf(ListBracketsNode::class, $return->type); - } - ]; - yield 'generic' => [ - '@return Foo, Baz|Bar>', - function (ReturnTag $return): void { - self::assertInstanceOf(GenericNode::class, $return->type); - } - ]; - } - - /** - * @return Generator - */ - private function provideDocblock(): Generator - { - yield 'docblock' => [ - <<<'EOT' - /** - * This is a docblock - * With some text - - * and maybe some - * ``` - * Markdown - * ``` - * @param This $should not be included - */ - EOT - , function (Docblock $docblock): void { - self::assertEquals(<<<'EOT' - - This is a docblock - With some text - - and maybe some - ``` - Markdown - ``` - - EOT - , $docblock->prose()); - } - ]; - - yield 'do not parse prose after first tag' => [ - <<<'EOT' - /** - * Applies the callback to the elements of the given arrays - * @link https://php.net/manual/en/function.array-map.php - * @param callable|null $callback - * Callback function to run for each element in each array. - */ - EOT - , function (Docblock $docblock): void { - self::assertEquals(<<<'EOT' - - Applies the callback to the elements of the given arrays - - EOT - , $docblock->prose()); - } - ]; - - yield 'parse open / closing HTML tags' => [ - <<<'EOT' - /** - * Applies the callback to the elements of the given arrays - * @link https://php.net/manual/en/function.array-map.php - * @param callable|null $callback

- * Callback function to run for each element in each array. - *

- */ - EOT - , function (Docblock $docblock): void { - self::assertEquals(<<<'EOT' -

Callback function to run for each element in each array.

- EOT - , $docblock->firstDescendant(ParamTag::class)->text()->toString()); - } - ]; - } -} diff --git a/lib/DocblockParser/Tests/Unit/Ast/NodeTestCase.php b/lib/DocblockParser/Tests/Unit/Ast/NodeTestCase.php deleted file mode 100644 index 37b5ba19c6..0000000000 --- a/lib/DocblockParser/Tests/Unit/Ast/NodeTestCase.php +++ /dev/null @@ -1,55 +0,0 @@ -parse($doc); - $nodes = iterator_to_array($node->selfAndDescendantElements(), false); - self::assertIsIterable($nodes); - self::assertEquals(0, $node->start(), 'Start offset'); - self::assertEquals(strlen($doc), $node->end(), 'End offset'); - self::assertGreaterThanOrEqual(0, $node->length(), 'Length is negative'); - - if ($assertion) { - $assertion($node); - } - } - - #[DataProvider('provideNode')] - public function testPartialParse(string $doc): void - { - $node = $this->parse($doc); - $partial = []; - foreach ($node->children() as $child) { - $partial[] = $child->toString(); - $node = $this->parse(implode(' ', $partial)); - self::assertInstanceOf(Element::class, $node); - } - } - - #[DataProvider('provideNode')] - public function testIsomorphism(string $doc): void - { - $one = $this->parse($doc); - $two = $this->parse($one->toString()); - self::assertEquals($one, $two, $one->toString()); - } - - private function parse(string $doc): Node - { - $node = (new Parser())->parse((new Lexer())->lex($doc)); - return $node; - } -} diff --git a/lib/DocblockParser/Tests/Unit/LexerTest.php b/lib/DocblockParser/Tests/Unit/LexerTest.php deleted file mode 100644 index 53862c5678..0000000000 --- a/lib/DocblockParser/Tests/Unit/LexerTest.php +++ /dev/null @@ -1,162 +0,0 @@ - $expectedTokens - */ - #[DataProvider('provideLex')] - public function testLex(string $lex, array $expectedTokens): void - { - $tokens = (new Lexer())->lex($lex)->toArray(); - - self::assertCount(count($expectedTokens), $tokens, 'Expected number of tokens'); - - foreach ($tokens as $index => $token) { - [$type, $value] = $expectedTokens[$index]; - $expectedToken = new Token($token->byteOffset, $type, $value); - self::assertEquals($expectedToken, $token); - } - } - - /** - * @return Generator}> - */ - public static function provideLex(): Generator - { - yield [ '', [] ]; - yield [ - <<<'EOT' - /** - * Hello this is - * Multi - */ - EOT - - ,[ - [Token::T_PHPDOC_OPEN, '/**'], - [Token::T_WHITESPACE, "\n"], - [Token::T_ASTERISK, ' * '], - [Token::T_LABEL, 'Hello'], - [Token::T_WHITESPACE, ' '], - [Token::T_LABEL, 'this'], - [Token::T_WHITESPACE, ' '], - [Token::T_LABEL, 'is'], - [Token::T_WHITESPACE, "\n"], - [Token::T_ASTERISK, ' * '], - [Token::T_LABEL, 'Multi'], - [Token::T_WHITESPACE, "\n"], - [Token::T_WHITESPACE, ' '], - [Token::T_PHPDOC_CLOSE, '*/'], - ] - ]; - - yield [ - 'Foobar', - [ - [Token::T_LABEL, 'Foobar'], - ] - ]; - yield [ - 'Foobar[]', - [ - [Token::T_LABEL, 'Foobar'], - [Token::T_LIST, '[]'], - ] - ]; - yield [ - 'Foobar', - [ - [Token::T_LABEL, 'Foobar'], - [Token::T_BRACKET_ANGLE_OPEN, '<'], - [Token::T_LABEL, 'Barfoo'], - [Token::T_BRACKET_ANGLE_CLOSE, '>'], - ] - ]; - yield [ - 'Foobar', - [ - [Token::T_LABEL, 'Foobar'], - [Token::T_BRACKET_ANGLE_OPEN, '<'], - [Token::T_LABEL, 'Barfoo'], - [Token::T_BRACKET_ANGLE_CLOSE, '>'], - ] - ]; - yield [ - 'Foobar{Barfoo, Foobar}', - [ - [Token::T_LABEL, 'Foobar'], - [Token::T_BRACKET_CURLY_OPEN, '{'], - [Token::T_LABEL, 'Barfoo'], - [Token::T_COMMA, ','], - [Token::T_WHITESPACE, ' '], - [Token::T_LABEL, 'Foobar'], - [Token::T_BRACKET_CURLY_CLOSE, '}'], - ] - ]; - yield [ - '"foobar"', - [ - [Token::T_QUOTED_STRING, '"foobar"'], - ] - ]; - yield [ - '123', - [ - [Token::T_INTEGER, '123'], - ] - ]; - - yield [ - '123.4', - [ - [Token::T_FLOAT, '123.4'], - ] - ]; - - yield [ - 'Foobar::FOOBAR_*', - [ - [Token::T_LABEL, 'Foobar'], - [Token::T_DOUBLE_COLON, '::'], - [Token::T_LABEL, 'FOOBAR_*'], - ] - ]; - yield [ - 'Foobar::*', - [ - [Token::T_LABEL, 'Foobar'], - [Token::T_DOUBLE_COLON, '::'], - [Token::T_ASTERISK, '*'], - ] - ]; - - yield 'inline code span is a single token, not a tag' => [ - '`@deprecated`', - [ - [Token::T_INLINE_CODE, '`@deprecated`'], - ] - ]; - - yield 'inline code span with prose does not leak a tag' => [ - 'See `@template-covariant T` for details', - [ - [Token::T_LABEL, 'See'], - [Token::T_WHITESPACE, ' '], - [Token::T_INLINE_CODE, '`@template-covariant T`'], - [Token::T_WHITESPACE, ' '], - [Token::T_LABEL, 'for'], - [Token::T_WHITESPACE, ' '], - [Token::T_LABEL, 'details'], - ] - ]; - } -} diff --git a/lib/DocblockParser/Tests/Unit/ParserTest.php b/lib/DocblockParser/Tests/Unit/ParserTest.php deleted file mode 100644 index ce39418c93..0000000000 --- a/lib/DocblockParser/Tests/Unit/ParserTest.php +++ /dev/null @@ -1,47 +0,0 @@ -parse((new Lexer())->lex($text)); - self::assertEquals($expected, $node); - } - - /** - * @return Generator - */ - public static function provideParse(): Generator - { - yield [ - '/** */', - new Docblock([ - new Token(0, Token::T_PHPDOC_OPEN, '/**'), - new Token(3, Token::T_WHITESPACE, ' '), - new Token(4, Token::T_PHPDOC_CLOSE, '*/'), - ]) - ]; - yield [ - '/** Hello */', - new Docblock([ - new Token(0, Token::T_PHPDOC_OPEN, '/**'), - new Token(3, Token::T_WHITESPACE, ' '), - new Token(4, Token::T_LABEL, 'Hello'), - new Token(9, Token::T_WHITESPACE, ' '), - new Token(10, Token::T_PHPDOC_CLOSE, '*/'), - ]) - ]; - } -} diff --git a/lib/DocblockParser/Tests/Unit/Printer/PrinterTest.php b/lib/DocblockParser/Tests/Unit/Printer/PrinterTest.php deleted file mode 100644 index 043c3c32c1..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/PrinterTest.php +++ /dev/null @@ -1,51 +0,0 @@ -markTestIncomplete(sprintf('No example given for "%s"', $path)); - } - - $tokens = (new Lexer())->lex($parts[0]); - $node = (new Parser())->parse($tokens); - $rendered = (new TestPrinter())->print($node); - - /** - * @phpstan-ignore-next-line - */ - if (!isset($parts[1]) || $update) { - file_put_contents($path, implode("---\n", [$parts[0], $rendered])); - $this->markTestSkipped('Generated output'); - } - - self::assertEquals(trim($parts[1]), trim($rendered)); - } - - /** - * @return Generator - */ - public static function provideExamples(): Generator - { - foreach ((array)glob(__DIR__ . '/examples/*.test') as $path) { - yield basename((string)$path) => [ (string) $path ]; - } - } -} diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/array1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/array1.test deleted file mode 100644 index 2962547ab9..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/array1.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @var array - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ArrayNode: = array - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/array2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/array2.test deleted file mode 100644 index 985c6925b9..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/array2.test +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @var array - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - GenericNode: = - ArrayNode: = array< - TypeList: = - ScalarNode: = string> - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/array3.test b/lib/DocblockParser/Tests/Unit/Printer/examples/array3.test deleted file mode 100644 index b43205fc34..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/array3.test +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @param array< - * array{ - * suites?:array< - * string, - * array{ - * contexts?:array|string> - * } - * > - * } - * > $config - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ArrayNode: = array< - TypeList: = - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = suites: - GenericNode: = - ArrayNode: = array< - TypeList: = - ScalarNode: = string, - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = contexts: - GenericNode: = - ArrayNode: = array< - TypeList: = - ScalarNode: = string, - UnionNode: = - TypeList: = - GenericNode: = - ArrayNode: = array< - TypeList: = - ScalarNode: = string>| - ScalarNode: = string>}>}> - VariableNode: = $config - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/array4.test b/lib/DocblockParser/Tests/Unit/Printer/examples/array4.test deleted file mode 100644 index 997dbcd615..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/array4.test +++ /dev/null @@ -1,11 +0,0 @@ -/** @param string[][][] $variable */ ---- -Docblock: = - ElementList: = /** - ParamTag: = @param - ListBracketsNode: = - ListBracketsNode: = - ListBracketsNode: = - ScalarNode: = string[][][] - VariableNode: = $variable - TextNode: = */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/array5.test b/lib/DocblockParser/Tests/Unit/Printer/examples/array5.test deleted file mode 100644 index 04e643fbef..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/array5.test +++ /dev/null @@ -1,11 +0,0 @@ -/** @var array[] $variable */ ---- -Docblock: = - ElementList: = /** - VarTag: = @var - ListBracketsNode: = - GenericNode: = - ArrayNode: = array< - TypeList: = - ScalarNode: = string>[] - VariableNode: = $variable */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/array6.test b/lib/DocblockParser/Tests/Unit/Printer/examples/array6.test deleted file mode 100644 index 2341881455..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/array6.test +++ /dev/null @@ -1,14 +0,0 @@ -/** @param array{first_name: string, last_name: string}[] $param */ ---- -Docblock: = - ElementList: = /** - ParamTag: = @param - ListBracketsNode: = - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = first_name: - ScalarNode: = string, - ArrayKeyValueNode: = last_name: - ScalarNode: = string}[] - VariableNode: = $param - TextNode: = */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/array_union_ints.test b/lib/DocblockParser/Tests/Unit/Printer/examples/array_union_ints.test deleted file mode 100644 index 26706f44ce..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/array_union_ints.test +++ /dev/null @@ -1,15 +0,0 @@ -/** @param array<12|13|14> $foobar */ ---- -Docblock: = - ElementList: = /** - ParamTag: = @param - GenericNode: = - ArrayNode: = array< - TypeList: = - UnionNode: = - TypeList: = - LiteralIntegerNode: = 12| - LiteralIntegerNode: = 13| - LiteralIntegerNode: = 14> - VariableNode: = $foobar - TextNode: = */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/array_union_strings.test b/lib/DocblockParser/Tests/Unit/Printer/examples/array_union_strings.test deleted file mode 100644 index c261a2168f..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/array_union_strings.test +++ /dev/null @@ -1,15 +0,0 @@ -/** @param array<'GET'|'POST'|'DELETE'> $foobar */ ---- -Docblock: = - ElementList: = /** - ParamTag: = @param - GenericNode: = - ArrayNode: = array< - TypeList: = - UnionNode: = - TypeList: = - LiteralStringNode: = 'GET'| - LiteralStringNode: = 'POST'| - LiteralStringNode: = 'DELETE'> - VariableNode: = $foobar - TextNode: = */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape1.test deleted file mode 100644 index 04c392437e..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape1.test +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @var array{foo:string,bar:int} - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = foo: - ScalarNode: = string, - ArrayKeyValueNode: = bar: - ScalarNode: = int} - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape2.test deleted file mode 100644 index e67a1aea42..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape2.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @var array{} - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ArrayShapeNode: = { - ArrayKeyValueList: = } - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape3.test b/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape3.test deleted file mode 100644 index c0b27c4dab..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape3.test +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @var array{int,int} - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = - ScalarNode: = int, - ArrayKeyValueNode: = - ScalarNode: = int} - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape4.test b/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape4.test deleted file mode 100644 index aa680e26f1..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape4.test +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @param array{ - * container_type?: Type|null, - * type?: Type|null, - * name?: Name|null, - * } $config - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = container_type: - UnionNode: = - TypeList: = - ClassNode: = Type| - NullNode: = null, - ArrayKeyValueNode: = type: - UnionNode: = - TypeList: = - ClassNode: = Type| - NullNode: = null, - ArrayKeyValueNode: = name: - UnionNode: = - TypeList: = - ClassNode: = Name| - NullNode: = null,} - VariableNode: = $config - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape5_opt.test b/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape5_opt.test deleted file mode 100644 index bb40c68220..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape5_opt.test +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @return Generator - */ ---- -Docblock: = - ElementList: = /** - * - ReturnTag: = @return - GenericNode: = - ClassNode: = Generator< - TypeList: = - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = 0: - ScalarNode: = string, - ArrayKeyValueNode: = ?1: - ClassNode: = Event, - ArrayKeyValueNode: = 2: - ScalarNode: = bool}> - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape6.test b/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape6.test deleted file mode 100644 index 566881861e..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/arrayshape6.test +++ /dev/null @@ -1,23 +0,0 @@ - /** - * @param array{0: 'w', 1: 'o', 2: 'r', 3: 'l', 4: 'd'} $input - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = 0: - LiteralStringNode: = 'w', - ArrayKeyValueNode: = 1: - LiteralStringNode: = 'o', - ArrayKeyValueNode: = 2: - LiteralStringNode: = 'r', - ArrayKeyValueNode: = 3: - LiteralStringNode: = 'l', - ArrayKeyValueNode: = 4: - LiteralStringNode: = 'd'} - VariableNode: = $input - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/assert-equality.test b/lib/DocblockParser/Tests/Unit/Printer/examples/assert-equality.test deleted file mode 100644 index 4e3ec31705..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/assert-equality.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @phpstan-assert =Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - AssertTag: = @phpstan-assert= - ClassNode: = Foobar - VariableNode: = $foobar - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/assert-negation.test b/lib/DocblockParser/Tests/Unit/Printer/examples/assert-negation.test deleted file mode 100644 index 2f32833247..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/assert-negation.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @phpstan-assert !Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - AssertTag: = @phpstan-assert! - ClassNode: = Foobar - VariableNode: = $foobar - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/assert1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/assert1.test deleted file mode 100644 index abdf95088c..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/assert1.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @phpstan-assert Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - AssertTag: = @phpstan-assert - ClassNode: = Foobar - VariableNode: = $foobar - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/callable1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/callable1.test deleted file mode 100644 index 439e1b1129..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/callable1.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @var callable(): string - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - CallableNode: = callable(): - ScalarNode: = string - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/callable2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/callable2.test deleted file mode 100644 index 07a2fb91f4..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/callable2.test +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @var callable(string,bool): string - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - CallableNode: = callable( - TypeList: = - ScalarNode: = string, - ScalarNode: = bool): - ScalarNode: = string - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/callable3.test b/lib/DocblockParser/Tests/Unit/Printer/examples/callable3.test deleted file mode 100644 index 70f5cbe9ca..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/callable3.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @var callable() - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - CallableNode: = callable() - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/class-string.test b/lib/DocblockParser/Tests/Unit/Printer/examples/class-string.test deleted file mode 100644 index 3e9193382f..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/class-string.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @var class-string - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ScalarNode: = class-string - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/classname1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/classname1.test deleted file mode 100644 index e107e06ef5..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/classname1.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @var Foobar\Barfoo - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ClassNode: = Foobar\Barfoo - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/classname2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/classname2.test deleted file mode 100644 index 3dab2b6bba..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/classname2.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @var \foo_bar\bar_foo - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ClassNode: = \foo_bar\bar_foo - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-incomplete.test b/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-incomplete.test deleted file mode 100644 index ccb6c93562..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-incomplete.test +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @return ($foo) - */ ---- -Docblock: = - ElementList: = /** - * - ReturnTag: = @return - ParenthesizedType: = ( - ConditionalNode: = - VariableNode: = $foo) - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-return1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-return1.test deleted file mode 100644 index 22c16597ad..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-return1.test +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @return ($foo is "foo" ? int : string) - */ ---- -Docblock: = - ElementList: = /** - * - ReturnTag: = @return - ParenthesizedType: = ( - ConditionalNode: = - VariableNode: = $foois - LiteralStringNode: = "foo"? - ScalarNode: = int: - ScalarNode: = string) - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-return2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-return2.test deleted file mode 100644 index 89599b2ba3..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/conditional-return2.test +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @return ($foo is "foo" ? ($bar is 123 ? Foo : Bar) : string) - */ ---- -Docblock: = - ElementList: = /** - * - ReturnTag: = @return - ParenthesizedType: = ( - ConditionalNode: = - VariableNode: = $foois - LiteralStringNode: = "foo"? - ParenthesizedType: = ( - ConditionalNode: = - VariableNode: = $baris - LiteralIntegerNode: = 123? - ClassNode: = Foo: - ClassNode: = Bar): - ScalarNode: = string) - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob.test b/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob.test deleted file mode 100644 index d9e031f34b..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @var Foobar::BAR_* - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ConstantNode: = - ClassNode: = Foobar::BAR_* - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob_bare.test b/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob_bare.test deleted file mode 100644 index fc6b9de788..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob_bare.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @var Foobar::* - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ConstantNode: = - ClassNode: = Foobar::* - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob_param.test b/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob_param.test deleted file mode 100644 index e4b0225aa7..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/constant_glob_param.test +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @param Foobar::BAR_* $param - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ConstantNode: = - ClassNode: = Foobar::BAR_* - VariableNode: = $param - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/deprecated1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/deprecated1.test deleted file mode 100644 index 674139d809..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/deprecated1.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @deprecated - */ ---- -Docblock: = - ElementList: = /** - * - DeprecatedTag: = @deprecated - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/deprecated2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/deprecated2.test deleted file mode 100644 index fd3755730d..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/deprecated2.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @deprecated This is because - */ ---- -Docblock: = - ElementList: = /** - * - DeprecatedTag: = @deprecated - TextNode: = This is because - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/extends1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/extends1.test deleted file mode 100644 index 2995821ca3..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/extends1.test +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @extends Foobar - */ ---- -Docblock: = - ElementList: = /** - * - ExtendsTag: = @extends - GenericNode: = - ClassNode: = Foobar< - TypeList: = - ClassNode: = Barfoo> - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/generic-bivariant-invariant.test b/lib/DocblockParser/Tests/Unit/Printer/examples/generic-bivariant-invariant.test deleted file mode 100644 index b4aab340ad..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/generic-bivariant-invariant.test +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @param Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ClassNode: = Foobar< - TypeList: = bivariant - ClassNode: = Barfoo,invariant - ClassNode: = Foo> - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/generic-contravariant.test b/lib/DocblockParser/Tests/Unit/Printer/examples/generic-contravariant.test deleted file mode 100644 index 8527ab20fd..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/generic-contravariant.test +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @param Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ClassNode: = Foobar< - TypeList: = covariant - ClassNode: = Barfoo,contravariant - ClassNode: = Barfoo> - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/generic-covariant.test b/lib/DocblockParser/Tests/Unit/Printer/examples/generic-covariant.test deleted file mode 100644 index e2bb964242..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/generic-covariant.test +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @param Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ClassNode: = Foobar< - TypeList: = covariant - ClassNode: = Barfoo> - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/generic1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/generic1.test deleted file mode 100644 index b3764b4515..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/generic1.test +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @param Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ClassNode: = Foobar< - TypeList: = - ListBracketsNode: = - ClassNode: = Barfoo[]> - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/generic2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/generic2.test deleted file mode 100644 index 6181fc6d76..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/generic2.test +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @param Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ClassNode: = Foobar< - TypeList: = - ListBracketsNode: = - ClassNode: = Barfoo[], - ScalarNode: = string> - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/generic3.test b/lib/DocblockParser/Tests/Unit/Printer/examples/generic3.test deleted file mode 100644 index d490b0ee8b..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/generic3.test +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @param Foobar,string> $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ClassNode: = Foobar< - TypeList: = - GenericNode: = - ClassNode: = Barfoo< - TypeList: = - ScalarNode: = int, - ScalarNode: = string>, - ScalarNode: = string> - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/generic4.test b/lib/DocblockParser/Tests/Unit/Printer/examples/generic4.test deleted file mode 100644 index e67747c23e..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/generic4.test +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @param Foobar,string, Baz> $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ClassNode: = Foobar< - TypeList: = - GenericNode: = - ClassNode: = Barfoo< - TypeList: = - ListBracketsNode: = - ScalarNode: = int[], - ListBracketsNode: = - ScalarNode: = int[]>, - ScalarNode: = string, - ClassNode: = Baz> - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/generic_this.test b/lib/DocblockParser/Tests/Unit/Printer/examples/generic_this.test deleted file mode 100644 index 4b66d0516f..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/generic_this.test +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @param Foobar<$this> $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ClassNode: = Foobar< - TypeList: = - ThisNode: = $this> - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/implements1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/implements1.test deleted file mode 100644 index 7eaf56bdd2..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/implements1.test +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @implements Foobar - */ ---- -Docblock: = - ElementList: = /** - * - ImplementsTag: = @implements - GenericNode: = - ClassNode: = Foobar< - TypeList: = - ClassNode: = Barfoo> - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/implements2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/implements2.test deleted file mode 100644 index 2a7e749647..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/implements2.test +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @implements Foobar, Bazboo - */ ---- -Docblock: = - ElementList: = /** - * - ImplementsTag: = @implements - GenericNode: = - ClassNode: = Foobar< - TypeList: = - ClassNode: = Barfoo>, - GenericNode: = - ClassNode: = Bazboo< - TypeList: = - ClassNode: = Bong> - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/inline_code_tag.test b/lib/DocblockParser/Tests/Unit/Printer/examples/inline_code_tag.test deleted file mode 100644 index 0c59324f55..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/inline_code_tag.test +++ /dev/null @@ -1,8 +0,0 @@ -/** - * A producer annotated `@template-covariant T` may accept a child value. - */ ---- -Docblock: = - ElementList: = /** - * A producer annotated `@template-covariant T` may accept a child value. - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/int-range-min.test b/lib/DocblockParser/Tests/Unit/Printer/examples/int-range-min.test deleted file mode 100644 index f046f44e01..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/int-range-min.test +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @var int - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - GenericNode: = - ScalarNode: = int< - TypeList: = - ClassNode: = min, - ClassNode: = max> - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/int-range.test b/lib/DocblockParser/Tests/Unit/Printer/examples/int-range.test deleted file mode 100644 index 85d49902d7..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/int-range.test +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @var int<0, max> - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - GenericNode: = - ScalarNode: = int< - TypeList: = - LiteralIntegerNode: = 0, - ClassNode: = max> - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/intersection1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/intersection1.test deleted file mode 100644 index 5367affb59..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/intersection1.test +++ /dev/null @@ -1,9 +0,0 @@ -/** @var Foo&Bar */ ---- -Docblock: = - ElementList: = /** - VarTag: = @var - IntersectionNode: = - TypeList: = - ClassNode: = Foo& - ClassNode: = Bar */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/list1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/list1.test deleted file mode 100644 index fbf1727b89..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/list1.test +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @param Foobar[] $foobar - * @param string[] $strings - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ListBracketsNode: = - ClassNode: = Foobar[] - VariableNode: = $foobar - TextNode: = - * - ParamTag: = @param - ListBracketsNode: = - ScalarNode: = string[] - VariableNode: = $strings - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/method1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/method1.test deleted file mode 100644 index 09b70a9328..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/method1.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @method Foobar foobar() - */ ---- -Docblock: = - ElementList: = /** - * - MethodTag: = @method - ClassNode: = Foobarfoobar() - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/method2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/method2.test deleted file mode 100644 index cb587cd351..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/method2.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @method static Foobar foobar() - */ ---- -Docblock: = - ElementList: = /** - * - MethodTag: = @methodstatic - ClassNode: = Foobarfoobar() - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/method3.test b/lib/DocblockParser/Tests/Unit/Printer/examples/method3.test deleted file mode 100644 index e1905c17eb..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/method3.test +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @method static bool allAlnum(mixed $value) Assert that value is alphanumeric for all values. - */ ---- -Docblock: = - ElementList: = /** - * - MethodTag: = @methodstatic - ScalarNode: = boolallAlnum( - ParameterList: = - ParameterTag: = - ScalarNode: = mixed - VariableNode: = $value) - TextNode: = Assert that value is alphanumeric for all values. - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/method4.test b/lib/DocblockParser/Tests/Unit/Printer/examples/method4.test deleted file mode 100644 index 79ba62a56a..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/method4.test +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @method bool is(string $value = null) - */ ---- -Docblock: = - ElementList: = /** - * - MethodTag: = @method - ScalarNode: = boolis( - ParameterList: = - ParameterTag: = - ScalarNode: = string - VariableNode: = $value - NullValue: = ) - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/method5.test b/lib/DocblockParser/Tests/Unit/Printer/examples/method5.test deleted file mode 100644 index 6e35b90e2a..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/method5.test +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @method static bool allAlnum(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric for all values. - */ ---- -Docblock: = - ElementList: = /** - * - MethodTag: = @methodstatic - ScalarNode: = boolallAlnum( - ParameterList: = - ParameterTag: = - ScalarNode: = mixed - VariableNode: = $value, - ParameterTag: = - UnionNode: = - TypeList: = - ScalarNode: = string| - ScalarNode: = callable - VariableNode: = $message - NullValue: = , - ParameterTag: = - ScalarNode: = string - VariableNode: = $propertyPath - NullValue: = ) - TextNode: = Assert that value is alphanumeric for all values. - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/method6.test b/lib/DocblockParser/Tests/Unit/Printer/examples/method6.test deleted file mode 100644 index f90f3edf76..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/method6.test +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @method static bool allIpv4(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address for all values. - */ ---- -Docblock: = - ElementList: = /** - * - MethodTag: = @methodstatic - ScalarNode: = boolallIpv4( - ParameterList: = - ParameterTag: = - ScalarNode: = string - VariableNode: = $value, - ParameterTag: = - ScalarNode: = int - VariableNode: = $flag - NullValue: = , - ParameterTag: = - UnionNode: = - TypeList: = - ScalarNode: = string| - ScalarNode: = callable - VariableNode: = $message - NullValue: = , - ParameterTag: = - ScalarNode: = string - VariableNode: = $propertyPath - NullValue: = ) - TextNode: = Assert that value is an IPv4 address for all values. - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/mixin-generic.test b/lib/DocblockParser/Tests/Unit/Printer/examples/mixin-generic.test deleted file mode 100644 index 2a0b7c2af6..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/mixin-generic.test +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @mixin Foobar - */ ---- -Docblock: = - ElementList: = /** - * - MixinTag: = @mixin - GenericNode: = - ClassNode: = Foobar< - TypeList: = - ClassNode: = Baz> - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/mixin1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/mixin1.test deleted file mode 100644 index a2fde0b2a6..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/mixin1.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @mixin Foobar - */ ---- -Docblock: = - ElementList: = /** - * - MixinTag: = @mixin - ClassNode: = Foobar - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/nullable-union.test b/lib/DocblockParser/Tests/Unit/Printer/examples/nullable-union.test deleted file mode 100644 index 63ba418698..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/nullable-union.test +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @return ?list - */ ---- -Docblock: = - ElementList: = /** - * - ReturnTag: = @return - NullableNode: = ? - GenericNode: = - ListNode: = list< - TypeList: = - UnionNode: = - TypeList: = - NullableNode: = ? - ScalarNode: = int| - NullableNode: = ? - ScalarNode: = bool| - NullableNode: = ? - ScalarNode: = string| - NullableNode: = ? - ScalarNode: = float> - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/nullable1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/nullable1.test deleted file mode 100644 index 806112b905..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/nullable1.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @var ?Foo - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - NullableNode: = ? - ClassNode: = Foo - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/param1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/param1.test deleted file mode 100644 index cd392d01ee..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/param1.test +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @param Foobar $foobar - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ClassNode: = Foobar - VariableNode: = $foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/param2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/param2.test deleted file mode 100644 index 989ad90d5a..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/param2.test +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @param Foobar $foobar - * @param string $barfoo - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ClassNode: = Foobar - VariableNode: = $foobar - TextNode: = - * - ParamTag: = @param - ScalarNode: = string - VariableNode: = $barfoo - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/param3.test b/lib/DocblockParser/Tests/Unit/Printer/examples/param3.test deleted file mode 100644 index 249b8ffe37..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/param3.test +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Hello World - * - * @param Foobar $foobar - * - * @param string $barfoo - * - * @param bool $bool - */ ---- -Docblock: = - ElementList: = /** - * Hello World - * - * - ParamTag: = @param - ClassNode: = Foobar - VariableNode: = $foobar - TextNode: = - * - * - ParamTag: = @param - ScalarNode: = string - VariableNode: = $barfoo - TextNode: = - * - * - ParamTag: = @param - ScalarNode: = bool - VariableNode: = $bool - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/param4.test b/lib/DocblockParser/Tests/Unit/Printer/examples/param4.test deleted file mode 100644 index 31a7224410..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/param4.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @param bool - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ScalarNode: = bool - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/param5.test b/lib/DocblockParser/Tests/Unit/Printer/examples/param5.test deleted file mode 100644 index f50b4321e8..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/param5.test +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @param bool $bool This is a boolean - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ScalarNode: = bool - VariableNode: = $bool - TextNode: = This is a boolean - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/param6.test b/lib/DocblockParser/Tests/Unit/Printer/examples/param6.test deleted file mode 100644 index d9c98125c7..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/param6.test +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @param bool $bool This is a boolean - * multiline comments not currently supported - * @param bool $bool This is a boolean - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - ScalarNode: = bool - VariableNode: = $bool - TextNode: = This is a boolean - * multiline comments not currently supported - * - ParamTag: = @param - ScalarNode: = bool - VariableNode: = $bool - TextNode: = This is a boolean - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/parenthesized.test b/lib/DocblockParser/Tests/Unit/Printer/examples/parenthesized.test deleted file mode 100644 index b6c18422d0..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/parenthesized.test +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @param array $arrayOfInt - * @param null|(callable(int):string) $callableOrNull - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ArrayNode: = array< - TypeList: = - ScalarNode: = int> - VariableNode: = $arrayOfInt - TextNode: = - * - ParamTag: = @param - UnionNode: = - TypeList: = - NullNode: = null| - ParenthesizedType: = ( - CallableNode: = callable( - TypeList: = - ScalarNode: = int): - ScalarNode: = string) - VariableNode: = $callableOrNull - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/parenthesized2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/parenthesized2.test deleted file mode 100644 index ef09523dbe..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/parenthesized2.test +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @param array $arrayOfInt - * @param null|(callable(int):string|string|int) $callableOrNull - */ ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ArrayNode: = array< - TypeList: = - ScalarNode: = int> - VariableNode: = $arrayOfInt - TextNode: = - * - ParamTag: = @param - UnionNode: = - TypeList: = - NullNode: = null| - ParenthesizedType: = ( - CallableNode: = callable( - TypeList: = - ScalarNode: = int): - UnionNode: = - TypeList: = - ScalarNode: = string| - ScalarNode: = string| - ScalarNode: = int) - VariableNode: = $callableOrNull - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/php_core1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/php_core1.test deleted file mode 100644 index 544e34f657..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/php_core1.test +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Sets a user-defined error handler function - * @link https://php.net/manual/en/function.set-error-handler.php - * @param callable|null $error_handler

- * The user function needs to accept two parameters... - *

- *

- * handler - * arrayerrcontext - * errno - * The first parameter, errno, contains the - * level of the error raised, as an integer. - * @param int $error_types [optional]

- * Can be used to mask the triggering of the - * error_handler function just like the error_reporting ini setting - * controls which errors are shown. Without this mask set the - * error_handler will be called for every error - * regardless to the setting of the error_reporting setting. - *

- * @return callable|null a string containing the previously defined error handler (if any). If - * the built-in error handler is used null is returned. null is also returned - * in case of an error such as an invalid callback. If the previous error handler - * was a class method, this function will return an indexed array with the class - * and the method name. - */ - --- -Docblock: = - ElementList: = /** - * Sets a user-defined error handler function - * - UnknownTag: = @link https://php.net/manual/en/function.set-error-handler.php - * - ParamTag: = @param - UnionNode: = - TypeList: = - ScalarNode: = callable| - NullNode: = null - VariableNode: = $error_handler - TextNode: =

- * The user function needs to accept two parameters... - *

- *

- * handler - * arrayerrcontext - * errno - * The first parameter, errno, contains the - * level of the error raised, as an integer. - * - ParamTag: = @param - ScalarNode: = int - VariableNode: = $error_types - TextNode: = [optional]

- * Can be used to mask the triggering of the - * error_handler function just like the error_reporting ini setting - * controls which errors are shown. Without this mask set the - * error_handler will be called for every error - * regardless to the setting of the error_reporting setting. - *

- * - ReturnTag: = @return - UnionNode: = - TypeList: = - ScalarNode: = callable| - NullNode: = null - TextNode: = a string containing the previously defined error handler (if any). If - * the built-in error handler is used null is returned. null is also returned - * in case of an error such as an invalid callback. If the previous error handler - * was a class method, this function will return an indexed array with the class - * and the method name. - */ - \ No newline at end of file diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/property1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/property1.test deleted file mode 100644 index 8842aa05a0..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/property1.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @property string $foo - */ ---- -Docblock: = - ElementList: = /** - * - PropertyTag: = @property - ScalarNode: = string$foo - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/propertyread1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/propertyread1.test deleted file mode 100644 index cfe6dbd6e2..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/propertyread1.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @property-read string $foo - */ ---- -Docblock: = - ElementList: = /** - * - PropertyTag: = @property-read - ScalarNode: = string$foo - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/return1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/return1.test deleted file mode 100644 index d1aad11fa3..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/return1.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @return Foobar - */ ---- -Docblock: = - ElementList: = /** - * - ReturnTag: = @return - ClassNode: = Foobar - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/return2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/return2.test deleted file mode 100644 index 1bacf372d2..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/return2.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @return $this - */ ---- -Docblock: = - ElementList: = /** - * - ReturnTag: = @return - ThisNode: = $this - TextNode: = - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/template1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/template1.test deleted file mode 100644 index 5f33eac09d..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/template1.test +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @template T - */ ---- -Docblock: = - ElementList: = /** - * - TemplateTag: = @templateT - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/template2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/template2.test deleted file mode 100644 index 4825caadf9..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/template2.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @template T of Foobar - */ ---- -Docblock: = - ElementList: = /** - * - TemplateTag: = @templateTof - ClassNode: = Foobar - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/text1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/text1.test deleted file mode 100644 index b87fca2c7a..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/text1.test +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Hello World - * - * This is some text - */ ---- -Docblock: = - ElementList: = /** - * Hello World - * - * This is some text - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/this.test b/lib/DocblockParser/Tests/Unit/Printer/examples/this.test deleted file mode 100644 index 117de19072..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/this.test +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @var $this - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - VariableNode: = $this - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/throw1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/throw1.test deleted file mode 100644 index 9476a93430..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/throw1.test +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @throws RuntimeException - */ ---- -Docblock: = - ElementList: = /** - * - ThrowsTag: = @throws - ClassNode: = RuntimeException*/ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/throw2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/throw2.test deleted file mode 100644 index 10660905a1..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/throw2.test +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @throws RuntimeException This happens if something breaks - */ ---- -Docblock: = - ElementList: = /** - * - ThrowsTag: = @throws - ClassNode: = RuntimeException - TextNode: = This happens if something breaks - */ - diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/type-alias-phpstan.test b/lib/DocblockParser/Tests/Unit/Printer/examples/type-alias-phpstan.test deleted file mode 100644 index c222e04f85..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/type-alias-phpstan.test +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @phpstan-type Foobar array{string,string} - */ ---- -Docblock: = - ElementList: = /** - * - TypeAliasTag: = @phpstan-type - ClassNode: = Foobar - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = - ScalarNode: = string, - ArrayKeyValueNode: = - ScalarNode: = string} - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/type-alias-psalm.test b/lib/DocblockParser/Tests/Unit/Printer/examples/type-alias-psalm.test deleted file mode 100644 index f52f87d2e2..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/type-alias-psalm.test +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @psalm-type Foobar = array{string,string} - */ ---- -Docblock: = - ElementList: = /** - * - TypeAliasTag: = @psalm-type - ClassNode: = Foobar= - ArrayShapeNode: = { - ArrayKeyValueList: = - ArrayKeyValueNode: = - ScalarNode: = string, - ArrayKeyValueNode: = - ScalarNode: = string} - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/union1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/union1.test deleted file mode 100644 index 3e648291f4..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/union1.test +++ /dev/null @@ -1,11 +0,0 @@ -/** @var string|bool|?Bar */ ---- -Docblock: = - ElementList: = /** - VarTag: = @var - UnionNode: = - TypeList: = - ScalarNode: = string| - ScalarNode: = bool| - NullableNode: = ? - ClassNode: = Bar */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/union_literals.test b/lib/DocblockParser/Tests/Unit/Printer/examples/union_literals.test deleted file mode 100644 index f7f1c211f7..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/union_literals.test +++ /dev/null @@ -1,10 +0,0 @@ -/** @var null|"always"|"auto" */ ---- -Docblock: = - ElementList: = /** - VarTag: = @var - UnionNode: = - TypeList: = - NullNode: = null| - LiteralStringNode: = "always"| - LiteralStringNode: = "auto" */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/union_literals2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/union_literals2.test deleted file mode 100644 index 394a7b9365..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/union_literals2.test +++ /dev/null @@ -1,9 +0,0 @@ -/** @var 123|123.3 */ ---- -Docblock: = - ElementList: = /** - VarTag: = @var - UnionNode: = - TypeList: = - LiteralIntegerNode: = 123| - LiteralFloatNode: = 123.3 */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/union_with_callable.test b/lib/DocblockParser/Tests/Unit/Printer/examples/union_with_callable.test deleted file mode 100644 index 798f3e16bf..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/union_with_callable.test +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @param array $arrayOfInt - * @param null|callable(int):string $callableOrNull - */ - ---- -Docblock: = - ElementList: = /** - * - ParamTag: = @param - GenericNode: = - ArrayNode: = array< - TypeList: = - ScalarNode: = int> - VariableNode: = $arrayOfInt - TextNode: = - * - ParamTag: = @param - UnionNode: = - TypeList: = - NullNode: = null| - CallableNode: = callable( - TypeList: = - ScalarNode: = int): - ScalarNode: = string - VariableNode: = $callableOrNull - TextNode: = - */ - diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/var1.test b/lib/DocblockParser/Tests/Unit/Printer/examples/var1.test deleted file mode 100644 index 947809a0c0..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/var1.test +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @var string[] - */ ---- -Docblock: = - ElementList: = /** - * - VarTag: = @var - ListBracketsNode: = - ScalarNode: = string[] - */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/var2.test b/lib/DocblockParser/Tests/Unit/Printer/examples/var2.test deleted file mode 100644 index 63e6b43b69..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/var2.test +++ /dev/null @@ -1,7 +0,0 @@ -/** @var string $foobar */ ---- -Docblock: = - ElementList: = /** - VarTag: = @var - ScalarNode: = string - VariableNode: = $foobar */ diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/var3.test b/lib/DocblockParser/Tests/Unit/Printer/examples/var3.test deleted file mode 100644 index 63e6b43b69..0000000000 --- a/lib/DocblockParser/Tests/Unit/Printer/examples/var3.test +++ /dev/null @@ -1,7 +0,0 @@ -/** @var string $foobar */ ---- -Docblock: = - ElementList: = /** - VarTag: = @var - ScalarNode: = string - VariableNode: = $foobar */ diff --git a/lib/Extension/Behat/Adapter/Symfony/SymfonyDiContextClassResolver.php b/lib/Extension/Behat/Adapter/Symfony/SymfonyDiContextClassResolver.php deleted file mode 100644 index 3664eaee87..0000000000 --- a/lib/Extension/Behat/Adapter/Symfony/SymfonyDiContextClassResolver.php +++ /dev/null @@ -1,61 +0,0 @@ - - */ - private ?array $index = null; - - public function __construct(private string $xmlPath) - { - } - - public function resolve(string $className): string - { - $this->loadIndex(); - - if (isset($this->index[$className])) { - return $this->index[$className]; - } - - throw new CouldNotResolverContextClass(sprintf( - 'Could not resolve context from Symfony container "%s"', - $this->xmlPath - )); - } - - private function loadIndex(): void - { - if ($this->index !== null) { - return; - } - - if (!file_exists($this->xmlPath)) { - throw new RuntimeException(sprintf( - 'Symfony DI XML file "%s" does not exist', - $this->xmlPath - )); - } - $dom = new DOMDocument('1.0'); - $dom->loadXML((string)file_get_contents($this->xmlPath)); - $query = new DOMXPath($dom); - $query->registerNamespace('s', 'http://symfony.com/schema/dic/services'); - /** @phpstan-ignore-next-line */ - foreach ($query->query('//s:service') as $serviceEl) { - if (!$serviceEl instanceof DOMElement) { - continue; - } - $this->index[(string)$serviceEl->getAttribute('id')] = (string)$serviceEl->getAttribute('class'); - } - } -} diff --git a/lib/Extension/Behat/Adapter/Worse/WorseContextClassResolver.php b/lib/Extension/Behat/Adapter/Worse/WorseContextClassResolver.php deleted file mode 100644 index eefe00cab3..0000000000 --- a/lib/Extension/Behat/Adapter/Worse/WorseContextClassResolver.php +++ /dev/null @@ -1,26 +0,0 @@ -reflector->reflectClass($className); - } catch (NotFound $notFound) { - throw new CouldNotResolverContextClass($notFound->getMessage(), 0, $notFound); - } - - return $className; - } -} diff --git a/lib/Extension/Behat/Adapter/Worse/WorseStepFactory.php b/lib/Extension/Behat/Adapter/Worse/WorseStepFactory.php deleted file mode 100644 index 6ed804411b..0000000000 --- a/lib/Extension/Behat/Adapter/Worse/WorseStepFactory.php +++ /dev/null @@ -1,50 +0,0 @@ -reflector->reflectClass($this->contextClassResolver->resolve($context->class())); - - /** @var ReflectionMethod $method */ - foreach ($class->methods() as $method) { - $steps = $parser->parseSteps($method->docblock()->raw()); - - if (!$steps) { - continue; - } - - foreach ($steps as $step) { - yield new Step( - $context, - $method->name(), - $step, - new Location($class->sourceCode()->uriOrThrow(), $method->position()) - ); - } - } - } - } -} diff --git a/lib/Extension/Behat/Behat/BehatConfig.php b/lib/Extension/Behat/Behat/BehatConfig.php deleted file mode 100644 index 0224f37da3..0000000000 --- a/lib/Extension/Behat/Behat/BehatConfig.php +++ /dev/null @@ -1,100 +0,0 @@ -findContexts($this->path) as $context) { - $contexts[] = $context; - } - return $contexts; - } - - /** - * @return Generator - */ - private function findContexts(string $path): Generator - { - $paths = [ - $path, - $path . '.dist' - ]; - - foreach ($paths as $path) { - if (!file_exists($path)) { - continue; - } - - yield from $this->readConfig($path); - } - } - - /** - * @return Generator - */ - private function readConfig(string $path): Generator - { - $contents = Yaml::parseFile($path); - - if (empty($contents)) { - return; - } - - if (isset($contents['imports'])) { - foreach ((array)$contents['imports'] as $importPath) { - yield from $this->readConfig(dirname($this->path) . '/' . $importPath); - } - } - - yield from $this->parseContexts($contents); - } - - /** - * @return Generator - * @param array< - * array{ - * suites?:array< - * string, - * array{ - * contexts?:array|string> - * } - * > - * } - * > $config - */ - private function parseContexts(array $config): Generator - { - foreach ($config as $profile) { - if (!isset($profile['suites'])) { - continue; - } - - foreach ($profile['suites'] as $suiteName => $suite) { - if (!isset($suite['contexts'])) { - continue; - } - foreach ($suite['contexts'] as $key => $context) { - // note this isn't tested - if (is_array($context)) { - $context = (string)key($context); - } - - yield new Context($suiteName, $context); - } - } - } - } -} diff --git a/lib/Extension/Behat/Behat/Context.php b/lib/Extension/Behat/Behat/Context.php deleted file mode 100644 index a570446f12..0000000000 --- a/lib/Extension/Behat/Behat/Context.php +++ /dev/null @@ -1,22 +0,0 @@ -class; - } - - public function suite(): string - { - return $this->suite; - } -} diff --git a/lib/Extension/Behat/Behat/ContextClassResolver.php b/lib/Extension/Behat/Behat/ContextClassResolver.php deleted file mode 100644 index c9db5e0466..0000000000 --- a/lib/Extension/Behat/Behat/ContextClassResolver.php +++ /dev/null @@ -1,8 +0,0 @@ -contextClassResolvers as $resolver) { - try { - return $resolver->resolve($className); - } catch (CouldNotResolverContextClass) { - } - } - - throw new CouldNotResolverContextClass(sprintf( - 'Could not resolve context class for "%s"', - $className - )); - } -} diff --git a/lib/Extension/Behat/Behat/Exception/CouldNotResolverContextClass.php b/lib/Extension/Behat/Behat/Exception/CouldNotResolverContextClass.php deleted file mode 100644 index 23ecf8d3e4..0000000000 --- a/lib/Extension/Behat/Behat/Exception/CouldNotResolverContextClass.php +++ /dev/null @@ -1,9 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Phpactor\Extension\Behat\Behat\Pattern; - -/** - * Defines a way to handle regex patterns. - * - * @author Konstantin Kudryashov - */ -final class RegexPatternPolicy implements PatternPolicy -{ - public function transformPatternToRegex($pattern): string - { - if (false === @preg_match($pattern, 'anything')) { - $error = error_get_last(); - $errorMessage = $error['message'] ?? ''; - - throw new InvalidPatternException(sprintf('The regex `%s` is invalid: %s', $pattern, $errorMessage)); - } - - return $pattern; - } -} diff --git a/lib/Extension/Behat/Behat/Pattern/TurnipPatternPolicy.php b/lib/Extension/Behat/Behat/Pattern/TurnipPatternPolicy.php deleted file mode 100644 index 76280acd10..0000000000 --- a/lib/Extension/Behat/Behat/Pattern/TurnipPatternPolicy.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Phpactor\Extension\Behat\Behat\Pattern; - -/** - * Defines a way to handle turnip patterns. - * - * @author Konstantin Kudryashov - */ -final class TurnipPatternPolicy implements PatternPolicy -{ - public const TOKEN_REGEX = "[\"']?(?P<%s>(?<=\")[^\"]*(?=\")|(?<=')[^']*(?=')|\-?[\w\.\,]+)['\"]?"; - public const PLACEHOLDER_REGEXP = "/\\\:(\w+)/"; - public const OPTIONAL_WORD_REGEXP = '/(\s)?\\\\\(([^\\\]+)\\\\\)(\s)?/'; - public const ALTERNATIVE_WORD_REGEXP = '/(\w+)\\\\\/(\w+)/'; - - /** - * @var string[] - */ - private array $regexCache = []; - - public function transformPatternToRegex($pattern): string - { - if (!isset($this->regexCache[$pattern])) { - $this->regexCache[$pattern] = $this->createTransformedRegex($pattern); - } - return $this->regexCache[$pattern]; - } - - /** - * @param string $pattern - */ - private function createTransformedRegex($pattern): string - { - $regex = preg_quote($pattern, '/'); - - $regex = $this->replaceTokensWithRegexCaptureGroups($regex); - $regex = $this->replaceTurnipOptionalEndingWithRegex($regex); - $regex = $this->replaceTurnipAlternativeWordsWithRegex($regex); - - return '/^' . $regex . '$/iu'; - } - - /** - * Replaces turnip tokens with regex capture groups. - */ - private function replaceTokensWithRegexCaptureGroups(string $regex): string - { - $tokenRegex = self::TOKEN_REGEX; - - return preg_replace_callback( - self::PLACEHOLDER_REGEXP, - $this->replaceTokenWithRegexCaptureGroup(...), - $regex - ); - } - - /** - * @param string[] $tokenMatch - */ - private function replaceTokenWithRegexCaptureGroup(array $tokenMatch): string - { - if (strlen($tokenMatch[1]) >= 32) { - throw new InvalidPatternException( - "Token name should not exceed 32 characters, but `{$tokenMatch[1]}` was used." - ); - } - - return sprintf(self::TOKEN_REGEX, $tokenMatch[1]); - } - - /** - * Replaces turnip optional ending with regex non-capturing optional group. - */ - private function replaceTurnipOptionalEndingWithRegex(string $regex): string - { - return preg_replace(self::OPTIONAL_WORD_REGEXP, '(?:\1)?(?:\2)?(?:\3)?', $regex); - } - - /** - * Replaces turnip alternative words with regex non-capturing alternating group. - */ - private function replaceTurnipAlternativeWordsWithRegex(string $regex): string - { - $regex = preg_replace(self::ALTERNATIVE_WORD_REGEXP, '(?:\1|\2)', $regex); - $regex = $this->removeEscapingOfAlternationSyntax($regex); - - return $regex; - } - - /** - * Removes escaping of alternation syntax from regex. - * - * This method removes those escaping backslashes from your slashes, so your steps - * could be matched against your escaped definitions. - */ - private function removeEscapingOfAlternationSyntax(string $regex): string - { - return str_replace('\\\/', '/', $regex); - } -} diff --git a/lib/Extension/Behat/Behat/Step.php b/lib/Extension/Behat/Behat/Step.php deleted file mode 100644 index f5ef252fdf..0000000000 --- a/lib/Extension/Behat/Behat/Step.php +++ /dev/null @@ -1,61 +0,0 @@ -context; - } - - public function method(): string - { - return $this->method; - } - - public function pattern(): string - { - return $this->pattern; - } - - public function matches(string $line): bool - { - $policies = [ - new TurnipPatternPolicy(), - new RegexPatternPolicy(), - ]; - - foreach ($policies as $policy) { - try { - $regex = $policy->transformPatternToRegex($this->pattern); - } catch (InvalidPatternException) { - continue; - } - - if (preg_match($regex, $line)) { - return true; - } - } - - return false; - } - - public function location(): Location - { - return $this->location; - } -} diff --git a/lib/Extension/Behat/Behat/StepFactory.php b/lib/Extension/Behat/Behat/StepFactory.php deleted file mode 100644 index 3bd8290ca6..0000000000 --- a/lib/Extension/Behat/Behat/StepFactory.php +++ /dev/null @@ -1,13 +0,0 @@ - - */ -class StepGenerator implements IteratorAggregate -{ - public function __construct( - private BehatConfig $config, - private StepFactory $factory, - private StepParser $parser - ) { - } - - public function getIterator(): Generator - { - yield from $this->factory->generate($this->parser, $this->config->contexts()); - } -} diff --git a/lib/Extension/Behat/Behat/StepParser.php b/lib/Extension/Behat/Behat/StepParser.php deleted file mode 100644 index 5f4e61f8b4..0000000000 --- a/lib/Extension/Behat/Behat/StepParser.php +++ /dev/null @@ -1,26 +0,0 @@ -extractSteps($keywords, $string); - } - - /** - * @return string[] - * @param string[] $keywords - */ - private function extractSteps(array $keywords, string $string): array - { - preg_match_all('{('.implode('|', $keywords).')\s*(.*)}', $string, $matches); - - return $matches[2] ?? []; - } -} diff --git a/lib/Extension/Behat/Behat/StepScorer.php b/lib/Extension/Behat/Behat/StepScorer.php deleted file mode 100644 index c230388dae..0000000000 --- a/lib/Extension/Behat/Behat/StepScorer.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @param Step[] $steps - */ - public function scoreSteps(array $steps, string $partial): array - { - $items = array_filter(array_map(trim(...), explode(' ', $partial))); - - $scored = []; - foreach ($steps as $step) { - $score = 0; - foreach ($items as $item) { - $score += substr_count($step->pattern(), $item); - } - - $scored[$step->pattern()] = $score; - } - - return $scored; - } -} diff --git a/lib/Extension/Behat/BehatExtension.php b/lib/Extension/Behat/BehatExtension.php deleted file mode 100644 index a6b574c7af..0000000000 --- a/lib/Extension/Behat/BehatExtension.php +++ /dev/null @@ -1,99 +0,0 @@ -register('behat.step_factory', function (Container $container) { - return new WorseStepFactory( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(ContextClassResolver::class) - ); - }); - - $container->register('behat.step_generator', function (Container $container) { - return new StepGenerator( - $container->get('behat.config'), - $container->get('behat.step_factory'), - $container->get('behat.step_parser') - ); - }); - - $container->register('behat.step_parser', function (Container $container) { - return new StepParser(); - }); - - $container->register('behat.config', function (Container $container) { - return new BehatConfig($container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER)->resolve($container->parameter(self::PARAM_CONFIG_PATH)->string())); - }); - - $container->register('behat.completion.feature_step_completor', function (Container $container) { - return new FeatureStepCompletor( - $container->get('behat.step_generator'), - $container->get('behat.step_parser') - ); - }, [ CompletionExtension::TAG_COMPLETOR => [ CompletionExtension::KEY_COMPLETOR_TYPES => [ 'cucumber' ]]]); - - $container->register('behat.reference_finder.step_definition_locator', function (Container $container) { - return new StepDefinitionLocator($container->get('behat.step_generator'), $container->get('behat.step_parser')); - }, [ ReferenceFinderExtension::TAG_DEFINITION_LOCATOR => []]); - - $container->register(ContextClassResolver::class, function (Container $container) { - $resolvers = [ - new WorseContextClassResolver( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR) - ) - ]; - - if (null !== $symfonyXmlPath = $container->getParameter(self::PARAM_SYMFONY_XML_PATH)) { - $resolvers[] = new SymfonyDiContextClassResolver($symfonyXmlPath); - } - - - return new ChainContextClassResolver($resolvers); - }); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_CONFIG_PATH => '%project_root%/behat.yml', - self::PARAM_SYMFONY_XML_PATH => null, - ]); - $schema->setDescriptions([ - self::PARAM_CONFIG_PATH => 'Path to the main behat.yml (including the filename behat.yml)', - self::PARAM_SYMFONY_XML_PATH => 'If using Symfony, set this path to the XML container dump to find contexts which are defined as services', - ]); - } - - public function name(): string - { - return 'behat'; - } -} diff --git a/lib/Extension/Behat/BehatSuggestExtension.php b/lib/Extension/Behat/BehatSuggestExtension.php deleted file mode 100644 index 677711be98..0000000000 --- a/lib/Extension/Behat/BehatSuggestExtension.php +++ /dev/null @@ -1,69 +0,0 @@ -register('behat.suggest', function (Container $container) { - $pathResolver = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - - return new PhpactorComposerSuggestor( - $container->expect(ConfigurationExtension::SERVICE_PHPACTOR_CONFIG_LOCAL, JsonConfig::class), - $container->get(ComposerInspector::class), - function (JsonConfig $config, ComposerInspector $inspector) use ($pathResolver) { - if ($config->has(BehatExtension::PARAM_ENABLED)) { - return Changes::none(); - } - - if (!$inspector->package('behat/behat')) { - return Changes::none(); - } - - $changes = [ - new PhpactorConfigChange('Behat BDD framework detected, enable Behat extension?', function (bool $enable) { - return [ - BehatExtension::PARAM_ENABLED => $enable, - ]; - }) - ]; - - $xmlPath = 'var/cache/test/App_KernelTestDebugContainer.xml'; - $fullXmlPath = $pathResolver->resolve('%project_root%/' . $xmlPath); - - if (!$config->has(BehatExtension::PARAM_SYMFONY_XML_PATH)) { - if (file_exists($fullXmlPath)) { - $changes[] = new PhpactorConfigChange('Enable Behat Symfony integration?', function (bool $enable) use ($xmlPath) { - return [ - BehatExtension::PARAM_SYMFONY_XML_PATH => $xmlPath - ]; - }); - } - } - - return Changes::from($changes); - } - ); - }, [ - ConfigurationExtension::TAG_SUGGESTOR => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/Behat/Completor/FeatureStepCompletor.php b/lib/Extension/Behat/Completor/FeatureStepCompletor.php deleted file mode 100644 index 0ce1b4645b..0000000000 --- a/lib/Extension/Behat/Completor/FeatureStepCompletor.php +++ /dev/null @@ -1,89 +0,0 @@ -lineForOffset($source->__toString(), $byteOffset->toInt()); - $parsed = $this->parser->parseSteps($currentLine); - - if ($parsed === []) { - return false; - } - $partial = $parsed[0]; - - $steps = iterator_to_array($this->generator); - - if ($partial) { - $scores = $this->stepSorter->scoreSteps($steps, $partial); - usort($steps, function (Step $step1, Step $step2) use ($scores) { - return $scores[$step2->pattern()] <=> $scores[$step1->pattern()]; - }); - } - - /** @var Step $step */ - foreach ($steps as $step) { - $suggestion = $step->pattern(); - - if (preg_match('{^' . $partial. '}i', $suggestion)) { - $suggestion = substr($suggestion, strlen($partial)); - } - - $startOffset = $byteOffset->toInt() - strlen($partial); - - yield Suggestion::createWithOptions($suggestion, [ - 'label' => $step->pattern(), - 'short_description' => $step->context()->class(), - 'type' => Suggestion::TYPE_SNIPPET, - 'range' => Range::fromStartAndEnd( - $startOffset, - $startOffset + strlen($partial) - ) - ]); - } - - return false; - } - - private function lineForOffset(string $source, int $byteOffset): string - { - $length = 0; - $last = ''; - $lines = preg_split('/$(\R?^)/m', $source, -1, PREG_SPLIT_OFFSET_CAPTURE); - if (false === $lines) { - return $source; - } - - foreach ($lines as $line) { - [ $line, $offset] = $line; - $offset = (int) $offset; - - if ($offset + strlen($line) >= $byteOffset) { - return $line; - } - } - - return ''; - } -} diff --git a/lib/Extension/Behat/ReferenceFinder/StepDefinitionLocator.php b/lib/Extension/Behat/ReferenceFinder/StepDefinitionLocator.php deleted file mode 100644 index d094aded00..0000000000 --- a/lib/Extension/Behat/ReferenceFinder/StepDefinitionLocator.php +++ /dev/null @@ -1,62 +0,0 @@ -language()->in(['cucumber', 'behat', 'gherkin'])) { - throw new UnsupportedDocument(sprintf('Language must be one of cucumber, behat or gherkin, got "%s"', $document->language())); - } - - $line = (new LineAtOffset())($document->__toString(), $byteOffset->toInt()); - $stepLines = $this->parser->parseSteps($line); - - if (empty($stepLines)) { - throw new CouldNotLocateDefinition(sprintf('Could not parse step line: "%s"', $line)); - } - - $line = reset($stepLines); - - $steps = $this->findSteps($line); - - return new TypeLocations(array_map(function (Step $step) { - return new TypeLocation(TypeFactory::class($step->context()->class()), $step->location()); - }, $steps)); - } - - /** - * @return array - */ - private function findSteps(string $line): array - { - $steps = []; - foreach ($this->generator as $step) { - if ($step->matches($line)) { - $steps[] = $step; - } - } - return $steps; - } -} diff --git a/lib/Extension/Behat/Tests/Integration/Adapter/Worse/TestContext.php b/lib/Extension/Behat/Tests/Integration/Adapter/Worse/TestContext.php deleted file mode 100644 index 0ba30c9955..0000000000 --- a/lib/Extension/Behat/Tests/Integration/Adapter/Worse/TestContext.php +++ /dev/null @@ -1,20 +0,0 @@ -addSource(TextDocumentBuilder::fromUri($path)->build())->build(); - $stepGenerator = new WorseStepFactory($reflector, new WorseContextClassResolver($reflector)); - $parser = new StepParser(); - $context = new Context('default', TestContext::class); - $steps = iterator_to_array($stepGenerator->generate($parser, [ $context ])); - - $this->assertEquals([ - new Step($context, 'givenThatThis', 'that I visit Berlin', Location::fromPathAndOffsets($path, 150, 199)), - new Step($context, 'shouldRun', 'I should run to Weisensee', Location::fromPathAndOffsets($path, 260, 305)), - ], $steps); - } -} diff --git a/lib/Extension/Behat/Tests/Integration/Behat/BehatConfigTest.php b/lib/Extension/Behat/Tests/Integration/Behat/BehatConfigTest.php deleted file mode 100644 index c158e81175..0000000000 --- a/lib/Extension/Behat/Tests/Integration/Behat/BehatConfigTest.php +++ /dev/null @@ -1,105 +0,0 @@ -workspace()->reset(); - $this->config = new BehatConfig($this->workspace()->path('/behat.yml')); - } - - public function testReturnsContexts(): void - { - $this->workspace()->put( - 'behat.yml', - <<<'EOT' - default: - suites: - default: - contexts: - - One - - Two - EOT - ); - - - $contexts = $this->config->contexts(); - self::assertCount(2, $contexts); - $context = reset($contexts); - assert($context instanceof Context); - self::assertEquals('One', $context->class()); - self::assertEquals('default', $context->suite()); - } - - public function testReturnsContextsFromImportedFiles(): void - { - $this->workspace()->put( - 'one.yml', - <<<'EOT' - default: - suites: - default: - contexts: - - One - - Two - EOT - ); - $this->workspace()->put( - 'two.yml', - <<<'EOT' - default: - suites: - default: - contexts: - - Three - - Four - EOT - ); - $this->workspace()->put( - 'behat.yml', - <<<'EOT' - imports: - - one.yml - - two.yml - EOT - ); - - - $contexts = $this->config->contexts(); - self::assertCount(4, $contexts); - } - - public function testDoesNotReturnContextsFromImportedFilesWithNoContexts(): void - { - $this->workspace()->put( - 'one.yml', - <<<'EOT' - EOT - ); - $this->workspace()->put( - 'two.yml', - <<<'EOT' - EOT - ); - $this->workspace()->put( - 'behat.yml', - <<<'EOT' - imports: - - one.yml - - two.yml - EOT - ); - - - $contexts = $this->config->contexts(); - self::assertCount(0, $contexts); - } -} diff --git a/lib/Extension/Behat/Tests/Integration/Completor/ExampleContext.php b/lib/Extension/Behat/Tests/Integration/Completor/ExampleContext.php deleted file mode 100644 index 0e45baa2a1..0000000000 --- a/lib/Extension/Behat/Tests/Integration/Completor/ExampleContext.php +++ /dev/null @@ -1,27 +0,0 @@ -> $expected - */ - #[DataProvider('provideComplete')] - public function testComplete(string $source, array $expected): void - { - [$source, $start, $end] = ExtractOffset::fromSource($source); - $suggestions = iterator_to_array($this->completor()->complete( - TextDocumentBuilder::create($source)->language('gherkin')->build(), - ByteOffset::fromInt((int)$end) - )); - - foreach ($expected as $index => $expectation) { - $this->assertArraySubset($expectation, $suggestions[$index]->toArray()); - } - } - - /** - * @return Generator>}> - */ - public static function provideComplete(): Generator - { - yield 'all' => [ - <<<'EOT' - Feature: Foobar - - Scenario: Hello - Given <><> - EOT - , [ - [ - 'type' => 'snippet', - 'name' => 'that I visit Berlin', - 'short_description' => ExampleContext::class, - 'range' => [ 51, 51], - ], - [ - 'type' => 'snippet', - 'name' => 'I should run to Weisensee', - 'short_description' => ExampleContext::class, - 'range' => [ 51, 51], - ], - ] - ]; - - yield 'partial match' => [ - <<<'EOT' - Feature: Foobar - - Scenario: Hello - Given <>that I visit<> - EOT - , [ - [ - 'type' => 'snippet', - 'name' => ' Berlin', - 'label' => 'that I visit Berlin', - 'short_description' => ExampleContext::class, - 'range' => [ 51, 63], - ], - ] - ]; - } - - private function completor(): Completor - { - $container = PhpactorContainer::fromExtensions([ - WorseReflectionExtension::class, - FilePathResolverExtension::class, - CompletionExtension::class, - BehatExtension::class, - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - LoggingExtension::class, - ], [ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ . '/../../../../../..', - BehatExtension::PARAM_CONFIG_PATH => __DIR__ .'/behat.yml', - ]); - - - return $container - ->expect(CompletionExtension::SERVICE_REGISTRY, TypedCompletorRegistry::class) - ->completorForType('cucumber'); - } -} diff --git a/lib/Extension/Behat/Tests/Integration/Completor/behat.yml b/lib/Extension/Behat/Tests/Integration/Completor/behat.yml deleted file mode 100644 index bf72c45e70..0000000000 --- a/lib/Extension/Behat/Tests/Integration/Completor/behat.yml +++ /dev/null @@ -1,6 +0,0 @@ -default: - suites: - default: - contexts: - - Phpactor\Extension\Behat\Tests\Integration\Completor\ExampleContext - diff --git a/lib/Extension/Behat/Tests/Integration/Completor/feature/some_feature.feature b/lib/Extension/Behat/Tests/Integration/Completor/feature/some_feature.feature deleted file mode 100644 index 295b83e78d..0000000000 --- a/lib/Extension/Behat/Tests/Integration/Completor/feature/some_feature.feature +++ /dev/null @@ -1,4 +0,0 @@ -Feature: Example Feature - - Scenario: Some Scenario - Given that I visit Berlin diff --git a/lib/Extension/Behat/Tests/IntegrationTestCase.php b/lib/Extension/Behat/Tests/IntegrationTestCase.php deleted file mode 100644 index 626853bf19..0000000000 --- a/lib/Extension/Behat/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,14 +0,0 @@ -resolve('app.behat.context.transform.shipping_method')); - } - - public function testExceptionWhenCannotLocate(): void - { - $this->expectException(CouldNotResolverContextClass::class); - (new SymfonyDiContextClassResolver(__DIR__ . '/example/example.xml'))->resolve('app.no'); - } - - public function testWhenFileNotFound(): void - { - $this->expectException(RuntimeException::class); - (new SymfonyDiContextClassResolver(__DIR__ . '/example/not.xml'))->resolve('app.no'); - } -} diff --git a/lib/Extension/Behat/Tests/Unit/Adapter/Symfony/example/example.xml b/lib/Extension/Behat/Tests/Unit/Adapter/Symfony/example/example.xml deleted file mode 100644 index af3b458dcd..0000000000 --- a/lib/Extension/Behat/Tests/Unit/Adapter/Symfony/example/example.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - en_GB - - - - en_GB - - - diff --git a/lib/Extension/Behat/Tests/Unit/Behat/StepParserTest.php b/lib/Extension/Behat/Tests/Unit/Behat/StepParserTest.php deleted file mode 100644 index ece159d423..0000000000 --- a/lib/Extension/Behat/Tests/Unit/Behat/StepParserTest.php +++ /dev/null @@ -1,54 +0,0 @@ - $expected - */ - #[DataProvider('provideSteps')] - public function testParsesPhpStepsDefinitions(string $docblock, array $expected): void - { - $parser = new StepParser(); - $steps = $parser->parseSteps($docblock); - $this->assertEquals($expected, $steps); - } - - /** - * @return Generator}> - */ - public static function provideSteps(): Generator - { - yield [ - '* @Given I visit Berlin', - [ - 'I visit Berlin' - ] - ]; - - yield [ - <<<'EOT' - /** - * @Given I visit Berlin - * @And I go to Alexanderplatz - * @When climb up the Fernsehturm - * @Then I will see things - * @But I will not know what they are - */ - EOT - , [ - 'I visit Berlin', - 'I go to Alexanderplatz', - 'climb up the Fernsehturm', - 'I will see things', - 'I will not know what they are', - ] - ]; - } -} diff --git a/lib/Extension/Behat/Tests/Unit/Behat/StepScorerTest.php b/lib/Extension/Behat/Tests/Unit/Behat/StepScorerTest.php deleted file mode 100644 index 8f556f8ac4..0000000000 --- a/lib/Extension/Behat/Tests/Unit/Behat/StepScorerTest.php +++ /dev/null @@ -1,85 +0,0 @@ - $expectedScores - * @param array $exampleSteps - */ - #[DataProvider('provideSortSteps')] - public function testSortSteps(array $exampleSteps, string $partial, array $expectedScores): void - { - $sort = new StepScorer(); - $score = $sort->scoreSteps($exampleSteps, $partial); - $this->assertEquals($expectedScores, $score); - } - - /** - * @return Generator,string,array}> - */ - public function provideSortSteps(): Generator - { - yield [ - [ - $this->createStep('midnight'), - $this->createStep('weary'), - ], - 'midnight', - [ - 'midnight' => 1, - 'weary' => 0, - ] - ]; - - yield [ - [ - $this->createStep('midnight'), - $this->createStep('weary'), - ], - 'mid', - [ - 'midnight' => 1, - 'weary' => 0, - ] - ]; - - yield [ - [ - $this->createStep('Once upon a midnight'), - $this->createStep('Once time'), - ], - 'Once mid', - [ - 'Once upon a midnight' => 2, - 'Once time' => 1, - ] - ]; - - yield [ - [ - $this->createStep('Once upon a midnight'), - ], - 'Once mid ', - [ - 'Once upon a midnight' => 2, - ] - ]; - } - - - private function createStep(string $string): Step - { - $context = new Context('foo', 'bar'); - return new Step($context, 'foo', $string, Location::fromPathAndOffsets('/path/to.php', 1, 5)); - } -} diff --git a/lib/Extension/Behat/Tests/Unit/BehatExtensionTest.php b/lib/Extension/Behat/Tests/Unit/BehatExtensionTest.php deleted file mode 100644 index 8be2e6a533..0000000000 --- a/lib/Extension/Behat/Tests/Unit/BehatExtensionTest.php +++ /dev/null @@ -1,44 +0,0 @@ - __DIR__ . '/../../../../..', - BehatExtension::PARAM_CONFIG_PATH => __DIR__ .'/../Integration/Completor/behat.yml', - ]); - - $locator = $container->get(ReferenceFinderExtension::SERVICE_DEFINITION_LOCATOR); - assert($locator instanceof DefinitionLocator); - $location = $locator->locateDefinition( - TextDocumentBuilder::fromUri(__DIR__. '/../Integration/Completor/feature/some_feature.feature')->language('cucumber')->build(), - ByteOffset::fromInt(69) - ); - - $this->assertStringContainsString('ExampleContext.php', $location->first()->location()->uri()->path()); - } -} diff --git a/lib/Extension/Behat/Tests/Unit/ReferenceFinder/StepDefinitionLocatorTest.php b/lib/Extension/Behat/Tests/Unit/ReferenceFinder/StepDefinitionLocatorTest.php deleted file mode 100644 index 7403271ad9..0000000000 --- a/lib/Extension/Behat/Tests/Unit/ReferenceFinder/StepDefinitionLocatorTest.php +++ /dev/null @@ -1,97 +0,0 @@ - - */ - private ObjectProphecy $generator; - - public function setUp(): void - { - $this->generator = $this->prophesize(StepGenerator::class); - $this->locator = new StepDefinitionLocator( - $this->generator->reveal(), - new StepParser() - ); - } - - #[DataProvider('provideLocateDefinition')] - public function testLocateDefinition(string $step): void - { - $this->generator->getIterator()->will(function () use ($step) { - yield new Step( - new Context('foo', 'bar'), - 'myMethod', - $step, - Location::fromPathAndOffsets(self::EXAMPLE_PATH, self::EXAMPLE_OFFSET, self::EXAMPLE_OFFSET_END) - ); - }); - - $text = <<<'EOT' - Feature: Hello - - Scenario: Something - Given I have a scenario step - And my <>name is "Daniel" - When I jump to it's definition - Then my cursor should be on the step definition - EOT - ; - - [ $text, $offset ] = ExtractOffset::fromSource($text); - - $document = TextDocumentBuilder::create($text)->language('cucumber')->build(); - $offset = ByteOffset::fromInt((int)$offset); - - $location = $this->locator->locateDefinition($document, $offset); - - $sourceLocation = $location->first()->location(); - self::assertEquals( - $sourceLocation, - Location::fromPathAndOffsets(self::EXAMPLE_PATH, self::EXAMPLE_OFFSET, self::EXAMPLE_OFFSET_END) - ); - } - - /** - * @return Generator - */ - public static function provideLocateDefinition(): Generator - { - yield [ - 'my name is ":name"', - ]; - - yield 'regex' => [ - '/my name is "\w+"/' - ]; - - yield 'turnip' => [ - 'my name(s) is ":name"' - ]; - } -} diff --git a/lib/Extension/ClassMover/Application/ClassCopy.php b/lib/Extension/ClassMover/Application/ClassCopy.php deleted file mode 100644 index 79bce2c2fa..0000000000 --- a/lib/Extension/ClassMover/Application/ClassCopy.php +++ /dev/null @@ -1,109 +0,0 @@ - classToFileConverter - public function __construct( - private ClassFileNormalizer $classFileNormalizer, - private ClassMoverFacade $classMover, - private Filesystem $filesystem - ) { - } - - /** - * Move - guess if moving by class name or file. - */ - public function copy(ClassCopyLogger $logger, string $src, string $dest) - { - $srcPath = $this->classFileNormalizer->normalizeToFile($src); - $destPath = $this->classFileNormalizer->normalizeToFile($dest); - - return $this->copyFile($logger, $srcPath, $destPath); - } - - public function copyClass(ClassCopyLogger $logger, string $srcName, string $destName) - { - return $this->copyFile( - $logger, - $this->classFileNormalizer->classToFile($srcName), - $this->classFileNormalizer->classToFile($destName) - ); - } - - public function copyFile(ClassCopyLogger $logger, string $srcPath, string $destPath): void - { - $srcPath = Phpactor::normalizePath($srcPath); - if (str_ends_with($destPath, '/')) { - $destPath = $destPath . basename($srcPath); - } - - if (false === Glob::isDynamic($srcPath) && !file_exists($srcPath)) { - throw new RuntimeException(sprintf( - 'File "%s" does not exist', - $srcPath - )); - } - - foreach (Glob::glob($srcPath) as $globPath) { - $globDest = $destPath; - // if the src is not the same as the globbed src, then it is a wildcard - // and we want to append the filename to the destination - if ($srcPath !== $globPath) { - $globDest = Path::join($destPath, basename($globPath)); - } - - try { - $this->doCopyFile($logger, $globPath, $globDest); - } catch (Exception $e) { - throw new RuntimeException(sprintf('Could not copy file "%s" to "%s"', $srcPath, $destPath), null, $e); - } - } - } - - private function doCopyFile(ClassCopyLogger $logger, string $srcPath, string $destPath): void - { - $destPath = Phpactor::normalizePath($destPath); - - $srcPath = $this->filesystem->createPath($srcPath); - $destPath = $this->filesystem->createPath($destPath); - - $report = $this->filesystem->copy($srcPath, $destPath); - $this->updateReferences($logger, $report); - $logger->copying($srcPath, $destPath); - } - - private function updateReferences(ClassCopyLogger $logger, CopyReport $copyReport): void - { - foreach ($copyReport->srcFiles() as $srcPath) { - $destPath = $copyReport->destFiles()->current(); - - $srcClassName = $this->classFileNormalizer->fileToClass($srcPath->path()); - $destClassName = $this->classFileNormalizer->fileToClass($destPath->path()); - - $source = $this->filesystem->getContents($srcPath); - $references = $this->classMover->findReferences($source, $srcClassName); - $logger->replacing($destPath, $references, FullyQualifiedName::fromString($destClassName)); - $edits = $this->classMover->replaceReferences( - $references, - $destClassName - ); - - $this->filesystem->writeContents($destPath, (string) $edits->apply($source)); - $copyReport->destFiles()->next(); - } - } -} diff --git a/lib/Extension/ClassMover/Application/ClassMemberReferences.php b/lib/Extension/ClassMover/Application/ClassMemberReferences.php deleted file mode 100644 index d3323b045c..0000000000 --- a/lib/Extension/ClassMover/Application/ClassMemberReferences.php +++ /dev/null @@ -1,210 +0,0 @@ -classFileNormalizer->normalizeToClass($class) : null; - $reflection = $className ? $this->reflector->reflectClassLike($className) : null; - $filesystem = $this->filesystemRegistry->get($scope); - - $filePaths = (new FileFinder())->filesFor($filesystem, $reflection, $memberName); - - $results = []; - foreach ($filePaths as $filePath) { - $references = $this->referencesInFile($filesystem, $filePath, $className, $memberName, $memberType, $replace, $dryRun); - - if ($references['references'] === [] && $references['risky_references'] === []) { - continue; - } - - $references['file'] = (string) $filePath; - $results[] = $references; - } - - return [ - 'references' => $results - ]; - } - - public function replaceInSource( - string $source, - string $class, - string $memberName, - string $memberType, - string $replacement - ):string { - $className = $class ? $this->classFileNormalizer->normalizeToClass($class) : null; - $query = $this->createQuery($className, $memberName, $memberType); - - $referenceList = $this->memberFinder->findMembers( - SourceCode::fromString($source), - $query - ); - return (string) $this->replaceReferencesInCode($source, $referenceList->withClasses(), $replacement); - } - - /** - * @return array{references: array, risky_references: array, replacements: array} - */ - private function referencesInFile( - Filesystem $filesystem, - $filePath, - ?string $className = null, - ?string $memberName = null, - ?string $memberType = null, - ?string $replace = null, - bool $dryRun = false - ): array { - $code = $filesystem->getContents($filePath); - - $query = $this->createQuery($className, $memberName, $memberType); - - $referenceList = $this->memberFinder->findMembers( - SourceCode::fromString($code), - $query - ); - $confidentList = $referenceList->withClasses(); - $riskyList = $referenceList->withoutClasses(); - - $result = [ - 'references' => [], - 'risky_references' => [], - 'replacements' => [], - ]; - - $result['references'] = $this->serializeReferenceList($code, $confidentList); - $result['risky_references'] = $this->serializeReferenceList($code, $riskyList); - - if ($replace) { - $updatedSource = $this->replaceReferencesInCode($code, $confidentList, $replace); - - if (false === $dryRun) { - file_put_contents($filePath, (string) $updatedSource); - } - - $query = $this->createQuery($className, $replace, $memberType); - - $replacedReferences = $this->memberFinder->findMembers( - SourceCode::fromString($updatedSource), - $query - ); - - $result['replacements'] = $this->serializeReferenceList((string) $updatedSource, $replacedReferences); - } - - return $result; - } - - /** - * @return list> - */ - private function serializeReferenceList(string $code, MemberReferences $referenceList): array - { - $references = []; - /** @var MemberReference $reference */ - foreach ($referenceList as $reference) { - $ref = $this->serializeReference($code, $reference); - - $references[] = $ref; - } - - return $references; - } - - /** - * @return array - */ - private function serializeReference(string $code, MemberReference $reference): array - { - [$lineNumber, $colNumber, $line] = $this->line($code, $reference->position()->start()); - return [ - 'start' => $reference->position()->start(), - 'end' => $reference->position()->end(), - 'line' => $line, - 'line_no' => $lineNumber, - 'col_no' => $colNumber, - 'reference' => (string) $reference->methodName(), - 'class' => $reference->hasClass() ? (string) $reference->class() : null, - ]; - } - - /** - * @return array{int, int, string} - */ - private function line(string $code, int $offset):array - { - $lines = explode("\n", $code); - $number = 0; - $startPosition = 0; - - foreach ($lines as $number => $line) { - $number = $number + 1; - $endPosition = $startPosition + strlen($line) + 1; - - if ($offset >= $startPosition && $offset <= $endPosition) { - $col = $offset - $startPosition; - return [ $number, $col, $line ]; - } - - $startPosition = $endPosition; - } - - return [$number, 0, '']; - } - - private function replaceReferencesInCode(string $code, MemberReferences $list, string $replace): SourceCode - { - $code = SourceCode::fromString($code); - - return $this->memberReplacer->replaceMembers($code, $list, $replace); - } - - private function createQuery(?string $className = null, ?string $memberName = null, $memberType = null): ClassMemberQuery - { - $query = ClassMemberQuery::create(); - - if ($className) { - $query = $query->withClass($className); - } - - if ($memberName) { - $query = $query->withMember($memberName); - } - - if ($memberType) { - $query = $query->withType($memberType); - } - - return $query; - } -} diff --git a/lib/Extension/ClassMover/Application/ClassMover.php b/lib/Extension/ClassMover/Application/ClassMover.php deleted file mode 100644 index e783d152fc..0000000000 --- a/lib/Extension/ClassMover/Application/ClassMover.php +++ /dev/null @@ -1,183 +0,0 @@ - - */ - public function getRelatedFiles(string $src): array - { - try { - return array_filter($this->pathFinder->destinationsFor($src), function (string $filePath) { - return (bool) file_exists($filePath); - }); - } catch (NoMatchingSourceException) { - // TODO: Make pathfinder return it's own exception here, this is the class-to-file exception - return []; - } - } - - /** - * Move - guess if moving by class name or file. - */ - public function move( - ClassMoverLogger $logger, - string $filesystemName, - string $src, - string $dest, - bool $moveRelatedFiles - ): void { - $srcPath = $this->classFileNormalizer->normalizeToFile($src); - $destPath = $this->classFileNormalizer->normalizeToFile($dest); - - $this->moveFile($logger, $filesystemName, $srcPath, $destPath, $moveRelatedFiles); - } - - public function moveClass(ClassMoverLogger $logger, string $filesystemName, string $srcName, string $destName, bool $moveRelatedFiles): void - { - $this->moveFile( - $logger, - $filesystemName, - $this->classFileNormalizer->classToFile($srcName), - $this->classFileNormalizer->classToFile($destName), - $moveRelatedFiles - ); - } - - public function moveFile(ClassMoverLogger $logger, string $filesystemName, string $srcPath, string $destPath, bool $moveRelatedFiles): void - { - $srcPath = Phpactor::normalizePath($srcPath); - foreach (FilesystemHelper::globSourceDestination($srcPath, $destPath) as $globSrc => $globDest) { - foreach ($this->expandRelatedPaths($globSrc, $globDest, $moveRelatedFiles) as $oldPath => $newPath) { - try { - $this->doMoveFile($logger, $filesystemName, $oldPath, $newPath); - } catch (Exception $e) { - throw new RuntimeException(sprintf('Could not move file "%s" to "%s"', $srcPath, $destPath), null, $e); - } - } - } - } - - private function doMoveFile(ClassMoverLogger $logger, string $filesystemName, string $srcPath, string $destPath): void - { - $filesystem = $this->filesystemRegistry->get($filesystemName); - if (str_ends_with($destPath, '/')) { - $destPath .= basename($srcPath); - } - - $destPath = Phpactor::normalizePath($destPath); - $srcPath = $filesystem->createPath($srcPath); - $destPath = $filesystem->createPath($destPath); - - if (!file_exists(dirname($destPath->path()))) { - mkdir(dirname($destPath->path()), 0777, true); - } - - $files = [[$srcPath, $destPath]]; - - if (is_dir($srcPath)) { - $files = $this->directoryMap($filesystem, $srcPath, $destPath); - } - - $this->replaceThoseReferences($logger, $filesystem, $files); - $logger->moving($srcPath, $destPath); - $filesystem->move($srcPath, $destPath); - } - - private function directoryMap(Filesystem $filesystem, FilePath $srcPath, FilePath $destPath) - { - $files = []; - foreach ($filesystem->fileList()->existing()->within($srcPath)->phpFiles() as $file) { - $suffix = substr($file->path(), strlen($srcPath->path())); - $files[] = [$file->path(), $filesystem->createPath($destPath.$suffix)]; - } - - return $files; - } - - private function replaceThoseReferences(ClassMoverLogger $logger, Filesystem $filesystem, array $files): void - { - foreach ($files as $paths) { - [$srcPath, $destPath] = $paths; - - $srcPath = $filesystem->createPath($srcPath); - $destPath = $filesystem->createPath($destPath); - - $srcClassName = $this->classFileNormalizer->fileToClass($srcPath->path()); - $destClassName = $this->classFileNormalizer->fileToClass($destPath->path()); - - $this->replaceReferences($logger, $filesystem, $srcClassName, $destClassName); - } - } - - private function replaceReferences(ClassMoverLogger $logger, Filesystem $filesystem, string $srcName, string $destName): void - { - foreach ($filesystem->fileList()->existing()->phpFiles() as $filePath) { - $source = $filesystem->getContents($filePath); - $references = $this->classMover->findReferences($source, $srcName); - - if ($references->references()->isEmpty()) { - continue; - } - - $logger->replacing($filePath, $references, FullyQualifiedName::fromString($destName)); - - $edits = $this->classMover->replaceReferences( - $references, - $destName - ); - - $filesystem->writeContents($filePath, $edits->apply($source)); - } - } - - /** - * @return array - */ - private function expandRelatedPaths(string $src, string $dest, bool $moveRelatedFiles): array - { - $paths = [ - $src => $dest - ]; - - if ($moveRelatedFiles) { - $oldPaths = $this->getRelatedFiles($src); - $newPaths = $this->pathFinder->destinationsFor($dest); - - foreach ($oldPaths as $oldType => $oldPath) { - if (!isset($newPaths[$oldType])) { - continue; - } - - $newPath = $newPaths[$oldType]; - $paths[$oldPath] = $newPath; - } - } - - return $paths; - } -} diff --git a/lib/Extension/ClassMover/Application/ClassReferences.php b/lib/Extension/ClassMover/Application/ClassReferences.php deleted file mode 100644 index 70f8fcf5a9..0000000000 --- a/lib/Extension/ClassMover/Application/ClassReferences.php +++ /dev/null @@ -1,189 +0,0 @@ -findOrReplaceReferences($filesystemName, $class, $replace, $dryRun); - } - - public function findReferences(string $filesystemName, string $class) - { - return $this->findOrReplaceReferences($filesystemName, $class); - } - - public function findOrReplaceReferences( - string $filesystemName, - string $class, - ?string $replace = null, - bool $dryRun = false - ) { - $classPath = $this->classFileNormalizerasd->normalizeToFile($class); - $classPath = Phpactor::normalizePath($classPath); - $className = $this->classFileNormalizerasd->normalizeToClass($class); - $filesystem = $this->filesystemRegistry->get($filesystemName); - - $results = []; - foreach ($filesystem->fileList()->phpFiles() as $filePath) { - $references = $this->fileReferences($filesystem, $filePath, $className, $replace, $dryRun); - - if ($references['references'] === []) { - continue; - } - - $references['file'] = (string) $filePath; - $results[] = $references; - } - - return [ - 'references' => $results - ]; - } - - public function replaceInSource(string $source, string $className, $replace): string - { - $referenceList = $this->refFinder - ->findIn(TextDocumentBuilder::create($source)->build()) - ->filterForName(FullyQualifiedName::fromString($className)); - $updatedSource = $this->replaceReferencesInCode($source, $referenceList, $className, $replace); - - return (string) $updatedSource; - } - - /** @return array{references: list, replacements: list} */ - private function fileReferences( - Filesystem $filesystem, - $filePath, - string $className, - ?string $replace = null, - bool $dryRun = false - ): array { - $code = $filesystem->getContents($filePath); - - $referenceList = $this->refFinder - ->findIn(TextDocumentBuilder::create($code)->build()) - ->filterForName(FullyQualifiedName::fromString($className)); - - $result = [ - 'references' => [], - 'replacements' => [], - ]; - - if ($referenceList->isEmpty()) { - return $result; - } - - $updatedSource = null; - if ($replace) { - $updatedSource = $this->replaceReferencesInCode($code, $referenceList, $className, $replace); - - if (false === $dryRun) { - file_put_contents($filePath, (string) $updatedSource); - } - } - - $result['references'] = $this->serializeReferenceList($code, $referenceList); - - if ($updatedSource && $replace) { - $newReferenceList = $this->refFinder - ->findIn(TextDocumentBuilder::create((string) $updatedSource)->build()) - ->filterForName(FullyQualifiedName::fromString($replace)); - - $result['replacements'] = $this->serializeReferenceList((string) $updatedSource, $newReferenceList); - } - - return $result; - } - - /** @return list */ - private function serializeReferenceList(string $code, NamespacedClassReferences $referenceList): array - { - $references = []; - - /** @var ClassReference $reference */ - foreach ($referenceList as $reference) { - $references[] = $this->serializeReference($code, $reference); - } - - return $references; - } - - /** @return ReferenceArray */ - private function serializeReference(string $code, ClassReference $reference): array - { - [$lineNumber, $colNumber, $line] = $this->line($code, $reference->position()->start()); - return [ - 'start' => $reference->position()->start(), - 'end' => $reference->position()->end(), - 'line' => $line, - 'line_no' => $lineNumber, - 'col_no' => $colNumber, - 'reference' => (string) $reference->name() - ]; - } - - /** @return array{int, int, string} */ - private function line(string $code, int $offset): array - { - $lines = explode("\n", $code); - $lineNumber = 0; - $startPosition = 0; - - foreach ($lines as $lineNumber => $line) { - $lineNumber = $lineNumber + 1; - $endPosition = $startPosition + strlen($line) + 1; - - if ($offset >= $startPosition && $offset <= $endPosition) { - $col = $offset - $startPosition; - return [ $lineNumber, $col, $line ]; - } - - $startPosition = $endPosition; - } - - return [$lineNumber, 0, '']; - } - - private function replaceReferencesInCode( - string $code, - NamespacedClassReferences $list, - string $class, - string $replace - ): string { - $class = FullyQualifiedName::fromString($class); - $replace = FullyQualifiedName::fromString($replace); - $code = TextDocumentBuilder::create($code)->build(); - - return $this->refReplacer->replaceReferences($code, $list, $class, $replace)->apply($code); - } -} diff --git a/lib/Extension/ClassMover/Application/Finder/FileFinder.php b/lib/Extension/ClassMover/Application/Finder/FileFinder.php deleted file mode 100644 index 3af4f3fd7d..0000000000 --- a/lib/Extension/ClassMover/Application/Finder/FileFinder.php +++ /dev/null @@ -1,142 +0,0 @@ -allPhpFiles($filesystem); - } - - $members = $reflection->members(); - if ($members->byName($memberName)->count() === 0) { - throw new RuntimeException(sprintf( - 'Class has no member named "%s", has the following member names: "%s"', - $memberName, - implode('", "', $members->keys()) - )); - } - - $publicMembers = $members->byName($memberName)->byVisibilities([ - Visibility::public() - ]); - - if ( - false === $reflection instanceof ReflectionClass || - $publicMembers->count() > 0 - ) { - // we have public members or a non-class, we need to search the - // whole tree, but we can discount any files which do not contain - // the member name string. - return $this->allPhpFiles($filesystem)->filter(function (SplFileInfo $file) use ($memberName): bool { - return preg_match('{' . $memberName . '}', file_get_contents($file->getPathname())) === 1; - }); - } - - /** @var ReflectionMember $member */ - $private = false; - foreach ($members as $member) { - if ($member->visibility() == Visibility::private()) { - $private = true; - } - } - - return $this->pathsFromReflectionClass($reflection, $private); - } - - private function pathsFromReflectionClass(ReflectionClass $reflection, bool $private): FileList - { - $path = $reflection->sourceCode()->uri()?->path(); - - if (!$path) { - throw new RuntimeException( - sprintf('Source class "%s" has no path associated with it', $reflection->name()), - ); - } - - $filePaths = [ $path ]; - $filePaths = $this->traitFilePaths($reflection, $filePaths); - - if ($private) { - return FileList::fromFilePaths($filePaths); - } - - $filePaths = $this->parentFilePaths($reflection, $filePaths); - $filePaths = $this->interfaceFilePaths($reflection, $filePaths); - - return FileList::fromFilePaths($filePaths); - } - - private function allPhpFiles(Filesystem $filesystem): FileList - { - return $filesystem->fileList()->existing()->phpFiles(); - } - - /** - * @param array $filePaths - * - * @return array - */ - private function parentFilePaths(ReflectionClass $reflection, array $filePaths): array - { - $context = $reflection->parent(); - while ($context) { - $path = $context->sourceCode()->uri()?->path(); - if ($path === null) { - continue; - } - $filePaths[] = $path; - $context = $context->parent(); - } - - return $filePaths; - } - - /** - * @param array $filePaths - * - * @return array - */ - private function traitFilePaths(ReflectionClass $reflection, array $filePaths): array - { - foreach ($reflection->traits() as $trait) { - $path = $trait->sourceCode()->uri()?->path(); - if ($path === null) { - continue; - } - $filePaths[] = $path; - } - return $filePaths; - } - - /** - * @param array $filePaths - * - * @return array - */ - private function interfaceFilePaths(ReflectionClass $reflection, array $filePaths): array - { - foreach ($reflection->interfaces() as $interface) { - $path = $interface->sourceCode()->uri()?->path(); - if ($path === null) { - continue; - } - $filePaths[] = $path; - } - - return $filePaths; - } -} diff --git a/lib/Extension/ClassMover/Application/Logger/ClassCopyLogger.php b/lib/Extension/ClassMover/Application/Logger/ClassCopyLogger.php deleted file mode 100644 index 2057760423..0000000000 --- a/lib/Extension/ClassMover/Application/Logger/ClassCopyLogger.php +++ /dev/null @@ -1,14 +0,0 @@ -registerClassMover($container); - $this->registerApplicationServices($container); - $this->registerConsoleCommands($container); - $this->registerRpc($container); - } - - private function registerRpc(ContainerBuilder $container): void - { - $container->register('class_mover.handler.class_references', function (Container $container) { - return new ReferencesHandler( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get('application.class_references'), - $container->get('application.method_references'), - $container->get(SourceCodeFilesystemExtension::SERVICE_REGISTRY) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ReferencesHandler::NAME] ]); - - $container->register('class_mover.handler.copy_class', function (Container $container) { - return new ClassCopyHandler( - $container->get('application.class_copy') - ); - }, [ 'rpc.handler' => ['name' => ClassCopyHandler::NAME] ]); - - $container->register('class_mover.handler.move_class', function (Container $container) { - return new ClassMoveHandler( - $container->get(ClassMoverApp::class), - SourceCodeFilesystemExtension::FILESYSTEM_GIT - ); - }, [ RpcExtension::TAG_RPC_HANDLER => [ 'name' => ClassMoveHandler::NAME ] ]); - } - - private function registerClassMover(ContainerBuilder $container): void - { - $container->register('class_mover.member_finder', function (Container $container) { - return new WorseTolerantMemberFinder( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR) - ); - }); - - $container->register('class_mover.member_replacer', function (Container $container) { - return new WorseTolerantMemberReplacer(); - }); - - $container->register('class_mover.ref_replacer', function (Container $container) { - return new TolerantClassReplacer($container->get(Updater::class)); - }); - } - - private function registerApplicationServices(ContainerBuilder $container): void - { - $container->register(ClassMoverApp::class, function (Container $container) { - return new ClassMoverApp( - $container->get('application.helper.class_file_normalizer'), - $container->get(ClassMover::class), - $container->get('source_code_filesystem.registry'), - $container->get(NavigationExtension::SERVICE_PATH_FINDER) - ); - }); - - $container->register('application.class_copy', function (Container $container) { - return new ClassCopy( - $container->get('application.helper.class_file_normalizer'), - $container->get(ClassMover::class), - $container->get('source_code_filesystem.registry')->get('git') - ); - }); - - $container->register('application.class_references', function (Container $container) { - return new ClassReferences( - $container->get('application.helper.class_file_normalizer'), - $container->get('class_mover.class_finder'), - $container->get('class_mover.ref_replacer'), - $container->get('source_code_filesystem.registry') - ); - }); - - $container->register('application.method_references', function (Container $container) { - return new ClassMemberReferences( - $container->get('application.helper.class_file_normalizer'), - $container->get('class_mover.member_finder'), - $container->get('class_mover.member_replacer'), - $container->get('source_code_filesystem.registry'), - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR) - ); - }); - } - - private function registerConsoleCommands(ContainerBuilder $container): void - { - $container->register('command.class_move', function (Container $container) { - return new ClassMoveCommand( - $container->get(ClassMoverApp::class), - $container->get('console.prompter') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'class:move' ]]); - - $container->register('command.class_copy', function (Container $container) { - return new ClassCopyCommand( - $container->get('application.class_copy'), - $container->get('console.prompter') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'class:copy' ]]); - - $container->register('command.class_references', function (Container $container) { - return new ReferencesClassCommand( - $container->get('application.class_references'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'references:class' ]]); - - $container->register('command.member_references', function (Container $container) { - return new ReferencesMemberCommand( - $container->get('application.method_references'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'references:member' ]]); - } -} diff --git a/lib/Extension/ClassMover/Command/ClassCopyCommand.php b/lib/Extension/ClassMover/Command/ClassCopyCommand.php deleted file mode 100644 index 9873ed0b0e..0000000000 --- a/lib/Extension/ClassMover/Command/ClassCopyCommand.php +++ /dev/null @@ -1,68 +0,0 @@ -setDescription('Copy class (path or FQN)'); - $this->addArgument('src', InputArgument::REQUIRED, 'Source path or FQN'); - $this->addArgument('dest', InputArgument::OPTIONAL, 'Destination path or FQN'); - $this->addOption('type', null, InputOption::VALUE_REQUIRED, sprintf( - 'Type of copy: "%s"', - implode('", "', [self::TYPE_AUTO, self::TYPE_CLASS, self::TYPE_FILE]) - ), self::TYPE_AUTO); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $type = $input->getOption('type'); - $logger = new SymfonyConsoleCopyLogger($output); - $src = $input->getArgument('src'); - $dest = $input->getArgument('dest'); - - if (null === $dest) { - $dest = $this->prompt->prompt('Move to: ', $src); - } - - switch ($type) { - case 'auto': - $this->copier->copy($logger, $src, $dest); - return 0; - case 'file': - $this->copier->copyFile($logger, $src, $dest); - return 0; - case 'class': - $this->copier->copyClass($logger, $src, $dest); - return 0; - } - - throw new InvalidArgumentException(sprintf( - 'Invalid type "%s", must be one of: "%s"', - $type, - implode('", "', [ self::TYPE_AUTO, self::TYPE_FILE, self::TYPE_CLASS ]) - )); - } -} diff --git a/lib/Extension/ClassMover/Command/ClassMoveCommand.php b/lib/Extension/ClassMover/Command/ClassMoveCommand.php deleted file mode 100644 index 14fc77b8d2..0000000000 --- a/lib/Extension/ClassMover/Command/ClassMoveCommand.php +++ /dev/null @@ -1,74 +0,0 @@ -setDescription('Move class (path or FQN) and update all references to it'); - $this->addArgument('src', InputArgument::REQUIRED, 'Source path or FQN'); - $this->addArgument('dest', InputArgument::OPTIONAL, 'Destination path or FQN'); - $this->addOption('type', null, InputOption::VALUE_REQUIRED, sprintf( - 'Type of move: "%s"', - implode('", "', [self::TYPE_AUTO, self::TYPE_CLASS, self::TYPE_FILE]) - ), self::TYPE_AUTO); - $this->addOption('related', null, InputOption::VALUE_NONE, 'Move related files (as defined by the patterns in navigator.destinations'); - FilesystemHandler::configure($this, SourceCodeFilesystemExtension::FILESYSTEM_GIT); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $type = $input->getOption('type'); - $logger = new SymfonyConsoleMoveLogger($output); - $src = $input->getArgument('src'); - $dest = $input->getArgument('dest'); - $filesystem = $input->getOption('filesystem'); - $related = (bool) $input->getOption('related'); - - if (null === $dest) { - $dest = $this->prompt->prompt('Move to: ', $src); - } - - switch ($type) { - case 'auto': - $this->mover->move($logger, $filesystem, $src, $dest, $related); - return 0; - case 'file': - $this->mover->moveFile($logger, $filesystem, $src, $dest, $related); - return 0; - case 'class': - $this->mover->moveClass($logger, $filesystem, $src, $dest, $related); - return 0; - } - - throw new InvalidArgumentException(sprintf( - 'Invalid type "%s", must be one of: "%s"', - $type, - implode('", "', [ self::TYPE_AUTO, self::TYPE_FILE, self::TYPE_CLASS ]) - )); - } -} diff --git a/lib/Extension/ClassMover/Command/Logger/SymfonyConsoleCopyLogger.php b/lib/Extension/ClassMover/Command/Logger/SymfonyConsoleCopyLogger.php deleted file mode 100644 index 072fbcd8a8..0000000000 --- a/lib/Extension/ClassMover/Command/Logger/SymfonyConsoleCopyLogger.php +++ /dev/null @@ -1,44 +0,0 @@ -output->writeln(sprintf( - '[COPY] %s => %s', - $srcPath->path(), - $destPath->path() - )); - } - - public function replacing(FilePath $path, FoundReferences $references, FullyQualifiedName $replacementName): void - { - if ($references->references()->isEmpty()) { - return; - } - - $this->output->writeln('[REPL] '.$path.''); - - foreach ($references->references() as $reference) { - $this->output->writeln(sprintf( - ' %s:%s %s => %s', - $reference->position()->start(), - $reference->position()->end(), - (string) $reference->name(), - (string) $reference->name()->transpose($replacementName) - )); - } - } -} diff --git a/lib/Extension/ClassMover/Command/Logger/SymfonyConsoleMoveLogger.php b/lib/Extension/ClassMover/Command/Logger/SymfonyConsoleMoveLogger.php deleted file mode 100644 index 9d8d7c1b6f..0000000000 --- a/lib/Extension/ClassMover/Command/Logger/SymfonyConsoleMoveLogger.php +++ /dev/null @@ -1,43 +0,0 @@ -output->writeln(sprintf( - '[MOVE] %s => %s', - $srcPath->path(), - $destPath->path() - )); - } - - public function replacing(FilePath $path, FoundReferences $references, FullyQualifiedName $replacementName): void - { - if ($references->references()->isEmpty()) { - return; - } - $this->output->writeln('[REPL] '.$path.''); - - foreach ($references->references() as $reference) { - $this->output->writeln(sprintf( - ' %s:%s %s => %s', - $reference->position()->start(), - $reference->position()->end(), - (string) $reference->name(), - (string) $reference->name()->transpose($replacementName) - )); - } - } -} diff --git a/lib/Extension/ClassMover/Command/ReferencesClassCommand.php b/lib/Extension/ClassMover/Command/ReferencesClassCommand.php deleted file mode 100644 index 8211556637..0000000000 --- a/lib/Extension/ClassMover/Command/ReferencesClassCommand.php +++ /dev/null @@ -1,116 +0,0 @@ -setDescription('Find and/or replace references for a given path or FQN'); - $this->addArgument('class', InputArgument::REQUIRED, 'Class path or FQN'); - $this->addOption('replace', null, InputOption::VALUE_REQUIRED, 'Replace with this Class FQN'); - $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not write changes to files'); - FormatHandler::configure($this); - FilesystemHandler::configure($this, SourceCodeFilesystemExtension::FILESYSTEM_GIT); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $class = $input->getArgument('class'); - $replace = $input->getOption('replace'); - $dryRun = $input->getOption('dry-run'); - $format = $input->getOption('format'); - $filesystem = $input->getOption('filesystem'); - - if ($replace && $dryRun) { - $output->writeln('# DRY RUN No files will be modified'); - } - - $results = $this->findOrReplaceReferences($filesystem, $class, $replace, $dryRun); - - if ($format) { - $this->dumperRegistry->get($format)->dump($output, $results); - return 0; - } - - $output->writeln('# References:'); - $count = $this->renderTable($output, $results, 'references', $output->isDecorated()); - - if ($replace) { - $output->write("\n"); - $output->writeln('# Replacements:'); - $this->renderTable($output, $results, 'replacements', $output->isDecorated()); - } - - $output->write("\n"); - $output->writeln(sprintf('%s reference(s)', $count)); - - return 0; - } - - private function findOrReplaceReferences($filesystem, $class, $replace, $dryRun) - { - if ($replace) { - return $this->referenceFinder->replaceReferences($filesystem, $class, $replace, $dryRun); - } - - return $this->referenceFinder->findReferences($filesystem, $class); - } - - private function renderTable(OutputInterface $output, array $results, string $type, bool $ansi) - { - $table = new Table($output); - $table->setHeaders([ - 'Path', - 'LN', - 'Line', - 'OS', - 'OE', - ]); - - $count = 0; - foreach ($results['references'] as $references) { - $filePath = $references['file']; - foreach ($references[$type] as $reference) { - $this->addReferenceRow($table, $filePath, $reference, $ansi); - $count++; - } - } - - $table->render(); - - return $count; - } - - private function addReferenceRow(Table $table, string $filePath, array $reference, bool $ansi): void - { - $table->addRow([ - Phpactor::relativizePath($filePath), - $reference['line_no'], - Highlight::highlightAtCol($reference['line'], $reference['reference'], $reference['col_no'], $ansi), - $reference['start'], - $reference['end'], - ]); - } -} diff --git a/lib/Extension/ClassMover/Command/ReferencesMemberCommand.php b/lib/Extension/ClassMover/Command/ReferencesMemberCommand.php deleted file mode 100644 index 4d40a53fe6..0000000000 --- a/lib/Extension/ClassMover/Command/ReferencesMemberCommand.php +++ /dev/null @@ -1,132 +0,0 @@ -setDescription('Find reference to a member'); - $this->addArgument('class', InputArgument::OPTIONAL, 'Class path or FQN'); - $this->addArgument('member', InputArgument::OPTIONAL, 'Method'); - $this->addOption('type', null, InputOption::VALUE_REQUIRED, 'Member type (constant, property or member)'); - $this->addOption('risky', null, InputOption::VALUE_NONE, 'Show risky references (matching member with unknown class'); - $this->addOption('replace', null, InputOption::VALUE_REQUIRED, 'Replace with this Class FQN (will not replace riskys)'); - $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not write changes to files'); - FormatHandler::configure($this); - FilesystemHandler::configure($this, SourceCodeFilesystemExtension::FILESYSTEM_GIT); - } - - public function execute(InputInterface $input, OutputInterface $output): int - { - $class = $input->getArgument('class'); - $member = $input->getArgument('member'); - $format = $input->getOption('format'); - $replace = $input->getOption('replace'); - $dryRun = (bool) $input->getOption('dry-run'); - $risky = (bool) $input->getOption('risky'); - $memberType = $input->getOption('type'); - $filesystem = $input->getOption('filesystem'); - - $results = $this->memberReferences->findOrReplaceReferences( - $filesystem, - $class, - $member, - $memberType, - $replace, - $dryRun - ); - - if ($replace && $dryRun) { - $output->writeln('# DRY RUN No files will be modified'); - } - - if ($format) { - $this->dumperRegistry->get($format)->dump($output, $results); - return 0; - } - - $output->writeln('# References:'); - $count = $this->renderTable($output, $results, 'references', $output->isDecorated()); - - if ($risky) { - $output->write("\n"); - $output->writeln('# Risky (unknown classes):'); - $riskyCount = $this->renderTable($output, $results, 'risky_references', $output->isDecorated()); - } else { - $riskyCount = array_reduce($results, function ($acc, $result) { - return $acc += array_reduce($result, function ($acc, $result) { - return $acc += count($result['risky_references']); - }, 0); - }, 0); - } - - if ($replace) { - $output->write("\n"); - $output->writeln('# Replacements:'); - $this->renderTable($output, $results, 'replacements', $output->isDecorated()); - } - - $output->write("\n"); - $output->writeln(sprintf('%s reference(s), %s risky references', $count, $riskyCount)); - - return 0; - } - - private function renderTable(OutputInterface $output, array $results, string $type, bool $ansi): int - { - $table = new Table($output); - $table->setHeaders([ - 'Path', - 'LN', - 'Line', - 'OS', - 'OE', - ]); - - $count = 0; - foreach ($results['references'] as $references) { - $filePath = $references['file']; - foreach ($references[$type] as $reference) { - $this->addReferenceRow($table, $filePath, $reference, $ansi); - $count++; - } - } - - $table->render(); - - return $count; - } - - private function addReferenceRow(Table $table, string $filePath, array $reference, bool $ansi): void - { - $table->addRow([ - Phpactor::relativizePath($filePath), - $reference['line_no'], - Highlight::highlightAtCol($reference['line'], $reference['reference'], $reference['col_no'], $ansi), - $reference['start'], - $reference['end'], - ]); - } -} diff --git a/lib/Extension/ClassMover/Rpc/ClassCopyHandler.php b/lib/Extension/ClassMover/Rpc/ClassCopyHandler.php deleted file mode 100644 index ea28330c1f..0000000000 --- a/lib/Extension/ClassMover/Rpc/ClassCopyHandler.php +++ /dev/null @@ -1,54 +0,0 @@ -setDefaults([ - self::PARAM_DEST_PATH => null, - ]); - $schema->setRequired([ - self::PARAM_SOURCE_PATH - ]); - } - - public function handle(array $arguments) - { - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_DEST_PATH, - 'Copy to: ', - $arguments[self::PARAM_SOURCE_PATH], - 'file' - )); - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $this->classCopy->copy(new NullLogger(), $arguments[self::PARAM_SOURCE_PATH], $arguments[self::PARAM_DEST_PATH]); - - return OpenFileResponse::fromPath($arguments[self::PARAM_DEST_PATH]); - } -} diff --git a/lib/Extension/ClassMover/Rpc/ClassMoveHandler.php b/lib/Extension/ClassMover/Rpc/ClassMoveHandler.php deleted file mode 100644 index 4a1142437c..0000000000 --- a/lib/Extension/ClassMover/Rpc/ClassMoveHandler.php +++ /dev/null @@ -1,103 +0,0 @@ -setDefaults([ - self::PARAM_DEST_PATH => null, - self::PARAM_CONFIRMED => null, - self::PARAM_ADDITIONAL_MOVE_CONFIRM => null, - ]); - $resolver->setRequired([ - self::PARAM_SOURCE_PATH - ]); - } - - public function handle(array $arguments) - { - if (false === $arguments[self::PARAM_CONFIRMED]) { - return EchoResponse::fromMessage('Cancelled'); - } - - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_DEST_PATH, - 'Move to: ', - $arguments[self::PARAM_SOURCE_PATH], - 'file' - )); - - if ( - null !== $arguments[self::PARAM_DEST_PATH] && - null === $arguments[self::PARAM_CONFIRMED] - ) { - $this->requireInput(ConfirmInput::fromNameAndLabel( - self::PARAM_CONFIRMED, - 'WARNING: This command will move the class and update ALL references in the git tree.' . "\n" . - ' It is not guaranteed to succeed. COMMIT YOUR WORK FIRST!' . "\n" . - 'Are you sure? :' - )); - } - - if ( - null === $arguments[self::PARAM_ADDITIONAL_MOVE_CONFIRM] && - $arguments[self::PARAM_DEST_PATH] && - $related = $this->classMove->getRelatedFiles($arguments[self::PARAM_SOURCE_PATH]) - ) { - $this->requireInput(ConfirmInput::fromNameAndLabel( - self::PARAM_ADDITIONAL_MOVE_CONFIRM, - sprintf( - "This class has the following related files:\n\n - %s\n\nMove these too? ", - implode("\n - ", $related) - ) - )); - } - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $this->classMove->move( - new NullLogger(), - $this->defaultFilesystem, - $arguments[self::PARAM_SOURCE_PATH], - $arguments[self::PARAM_DEST_PATH], - $arguments[self::PARAM_ADDITIONAL_MOVE_CONFIRM] ?? false - ); - - return CollectionResponse::fromActions([ - OpenFileResponse::fromPath($arguments[self::PARAM_DEST_PATH]), - CloseFileResponse::fromPath($arguments[self::PARAM_SOURCE_PATH]) - ]); - } -} diff --git a/lib/Extension/ClassMover/Rpc/ReferencesHandler.php b/lib/Extension/ClassMover/Rpc/ReferencesHandler.php deleted file mode 100644 index 8a319fd48b..0000000000 --- a/lib/Extension/ClassMover/Rpc/ReferencesHandler.php +++ /dev/null @@ -1,321 +0,0 @@ -setDefaults([ - self::PARAMETER_MODE => self::MODE_FIND, - self::PARAMETER_FILESYSTEM => $this->defaultFilesystem, - self::PARAMETER_REPLACEMENT => null, - ]); - $resolver->setRequired([ - self::PARAMETER_PATH, - self::PARAMETER_OFFSET, - self::PARAMETER_SOURCE, - ]); - } - - public function handle(array $arguments) - { - $offset = $this->reflector->reflectOffset( - TextDocumentBuilder::create( - $arguments[self::PARAMETER_SOURCE] - )->uri($arguments[self::PARAMETER_PATH])->build(), - ByteOffset::fromInt($arguments[self::PARAMETER_OFFSET]) - ); - $nodeContext = $offset->nodeContext(); - - if (null === $arguments[self::PARAMETER_FILESYSTEM]) { - $this->requireInput(ChoiceInput::fromNameLabelChoicesAndDefault( - self::PARAMETER_FILESYSTEM, - sprintf('%s "%s" in:', ucfirst($nodeContext->symbol()->symbolType()), $nodeContext->symbol()->name()), - array_combine($this->registry->names(), $this->registry->names()), - $this->defaultFilesystem - )); - } - - if ($arguments[self::PARAMETER_MODE] === self::MODE_REPLACE) { - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAMETER_REPLACEMENT, - 'Replacement: ', - $this->defaultReplacement($nodeContext) - )); - } - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - return match ($arguments[self::PARAMETER_MODE]) { - self::MODE_FIND => $this->findReferences($nodeContext, $arguments['filesystem']), - self::MODE_REPLACE => $this->replaceReferences( - $nodeContext, - $arguments['filesystem'], - $arguments[self::PARAMETER_REPLACEMENT], - $arguments[self::PARAMETER_PATH], - $arguments[self::PARAMETER_SOURCE] - ), - default => throw new InvalidArgumentException(sprintf( - 'Unknown references mode "%s"', - $arguments['mode'] - )), - }; - } - - private function findReferences(NodeContext $nodeContext, string $filesystem) - { - [$source, $references] = $this->performFindOrReplaceReferences($nodeContext, $filesystem); - - if (count($references) === 0) { - return EchoResponse::fromMessage(self::MESSAGE_NO_REFERENCES_FOUND); - } - - $references = array_filter($references, function (array $referenceList) { - return $referenceList['references'] !== []; - }); - - return CollectionResponse::fromActions([ - $this->echoMessage('Found', $nodeContext, $filesystem, $references), - FileReferencesResponse::fromArray($references), - ]); - } - - private function replaceReferences( - NodeContext $nodeContext, - string $filesystem, - string $replacement, - string $path, - string $source - ) { - $originalSource = $source; - [$source, $references] = $this->performFindOrReplaceReferences( - $nodeContext, - $filesystem, - $source, - $replacement - ); - - if (count($references) === 0) { - return EchoResponse::fromMessage(self::MESSAGE_NO_REFERENCES_FOUND); - } - - $actions = [ - $this->echoMessage('Replaced', $nodeContext, $filesystem, $references), - ]; - - if ($source) { - // renaming methods modifies files on disk. some editors track if - // the file has been modified on the disk and issue a warning if - // the open file is not in sync. below we reload the file before - // applying changes (the changes from the rename operation, - // including any changes made after the file was last saved). - if (file_exists($path)) { - $actions[] = OpenFileResponse::fromPath($path)->withForcedReload(true); - $originalSource = file_get_contents($path); - } - $actions[] = UpdateFileSourceResponse::fromPathOldAndNewSource($path, $originalSource, $source); - } - - if (count($references)) { - $actions[] = FileReferencesResponse::fromArray($references); - } - - return CollectionResponse::fromActions($actions); - } - - private function classReferences(string $filesystem, NodeContext $nodeContext, ?string $source = null, ?string $replacement = null) - { - $classType = (string) $nodeContext->type(); - $references = $this->classReferences->findOrReplaceReferences($filesystem, $classType, $replacement); - - $updatedSource = null; - if ($source) { - $updatedSource = $this->classReferences->replaceInSource( - $source, - $classType, - $replacement - ); - } - - - return [$updatedSource, $references['references']]; - } - - /** @return array{string|null, mixed} */ - private function memberReferences( - string $filesystem, - NodeContext $nodeContext, - string $memberType, - ?string $source = null, - ?string $replacement = null - ): array { - $classType = (string) $nodeContext->containerType(); - - $references = $this->classMemberReferences->findOrReplaceReferences( - scope: $filesystem, - class: $classType, - memberName: $nodeContext->symbol()->name(), - memberType: $memberType, - replace: $replacement - ); - - $updatedSource = null; - if ($source && $replacement) { - $updatedSource = $this->classMemberReferences->replaceInSource( - $source, - $classType, - $nodeContext->symbol()->name(), - $memberType, - $replacement - ); - } - - return [$updatedSource, $references['references']]; - } - - private function performFindOrReplaceReferences( - NodeContext $nodeContext, - string $filesystem, - ?string $source = null, - ?string $replacement = null - ) { - [$source, $references] = $this->doPerformFindOrReplaceReferences( - $nodeContext, - $filesystem, - $source, - $replacement, - ); - - return [$source, $this->sortReferences($references)]; - } - - private function doPerformFindOrReplaceReferences(NodeContext $nodeContext, string $filesystem, ?string $source = null, ?string $replacement = null) - { - return match ($nodeContext->symbol()->symbolType()) { - Symbol::CLASS_ => $this->classReferences($filesystem, $nodeContext, $source, $replacement), - Symbol::METHOD => $this->memberReferences($filesystem, $nodeContext, ClassMemberQuery::TYPE_METHOD, $source, $replacement), - Symbol::PROPERTY => $this->memberReferences($filesystem, $nodeContext, ClassMemberQuery::TYPE_PROPERTY, $source, $replacement), - Symbol::CONSTANT => $this->memberReferences($filesystem, $nodeContext, ClassMemberQuery::TYPE_CONSTANT, $source, $replacement), - default => throw new RuntimeException(sprintf( - 'Cannot find references for symbol type "%s"', - $nodeContext->symbol()->symbolType() - )), - }; - } - - private function sortReferences(array $fileReferences): array - { - // Sort the references for each file - array_walk($fileReferences, function (array &$fileReference): void { - if (empty($fileReference['references'])) { - return; // Do nothing if there is no references - } - - usort($fileReference['references'], function (array $first, array $second) { - return $first['start'] - $second['start']; - }); - }); - - // Sort the list by file - usort($fileReferences, function (array $first, array $second) { - return strcmp($first['file'], $second['file']); - }); - - return $fileReferences; - } - - private function echoMessage(string $action, NodeContext $nodeContext, string $filesystem, array $references): EchoResponse - { - $count = array_reduce($references, function ($count, $result) { - $count += count($result['references']); - return $count; - }, 0); - - $riskyCount = array_reduce($references, function ($count, $result) { - if (!isset($result['risky_references'])) { - return $count; - } - $count += count($result['risky_references']); - return $count; - }, 0); - - $risky = ''; - if ($riskyCount > 0) { - $risky = sprintf(' (%s risky references not listed)', $riskyCount); - } - - return EchoResponse::fromMessage(sprintf( - '%s %s literal references to %s "%s" using FS "%s"%s', - $action, - $count, - $nodeContext->symbol()->symbolType(), - $nodeContext->symbol()->name(), - $filesystem, - $risky - )); - } - - private function defaultReplacement(NodeContext $nodeContext): string - { - $type = $nodeContext->type()->expandTypes()->classLike()->firstOrNull(); - if ($type instanceof ClassType) { - return $type->name()->__toString(); - } - - return $nodeContext->symbol()->name(); - } -} diff --git a/lib/Extension/ClassToFile/ClassToFileExtension.php b/lib/Extension/ClassToFile/ClassToFileExtension.php deleted file mode 100644 index fcdc44a17c..0000000000 --- a/lib/Extension/ClassToFile/ClassToFileExtension.php +++ /dev/null @@ -1,76 +0,0 @@ -setDefaults([ - self::PARAM_PROJECT_ROOT => '%project_root%', - self::PARAM_BRUTE_FORCE_CONVERSION => true, - ]); - $schema->setDescriptions([ - self::PARAM_PROJECT_ROOT => 'Root path of the project (e.g. where composer.json is)', - self::PARAM_BRUTE_FORCE_CONVERSION => 'If composer not found, fallback to scanning all files (very time consuming depending on project size)', - ]); - } - - - public function load(ContainerBuilder $container): void - { - $container->register(self::SERVICE_CONVERTER, function (Container $container) { - return new ClassToFileFileToClass( - $container->get('class_to_file.class_to_file'), - $container->get('class_to_file.file_to_class') - ); - }); - - $container->register('class_to_file.class_to_file', function (Container $container) { - $classToFiles = []; - foreach ($container->get(self::PARAM_CLASS_LOADERS) as $classLoader) { - $classToFiles[] = new ComposerClassToFile($classLoader); - } - - if ($container->parameter(self::PARAM_BRUTE_FORCE_CONVERSION)->bool() && $classToFiles === []) { - $projectDir = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER)->resolve($container->parameter(self::PARAM_PROJECT_ROOT)->string()); - $classToFiles[] = new SimpleClassToFile($projectDir); - } - - return new ChainClassToFile($classToFiles); - }); - - $container->register('class_to_file.file_to_class', function (Container $container) { - $fileToClasses = []; - foreach ($container->get(ComposerAutoloaderExtension::SERVICE_AUTOLOADERS) as $classLoader) { - $fileToClasses[] = new ComposerFileToClass($classLoader); - } - - if ($fileToClasses === []) { - $fileToClasses[] = new SimpleFileToClass(); - } - - return new ChainFileToClass($fileToClasses); - }); - } -} diff --git a/lib/Extension/ClassToFile/Tests/Unit/ClassToFileExtensionTest.php b/lib/Extension/ClassToFile/Tests/Unit/ClassToFileExtensionTest.php deleted file mode 100644 index dbe0e508df..0000000000 --- a/lib/Extension/ClassToFile/Tests/Unit/ClassToFileExtensionTest.php +++ /dev/null @@ -1,57 +0,0 @@ -createConverter(); - $candidates = $converter->classToFileCandidates(ClassName::fromString(__CLASS__)); - $file = $candidates->best(); - $candidates = $converter->fileToClassCandidates($file); - $this->assertEquals('ClassToFileExtensionTest', $candidates->best()->name()); - } - - public function testCreatesConverterWithoutComposer(): void - { - $converter = $this->createConverter([ - ComposerAutoloaderExtension::PARAM_AUTOLOADER_PATH => __DIR__ . '/autoload.php', - FilePathResolverExtension::PARAM_PROJECT_ROOT => __DIR__ - ]); - $candidates = $converter->classToFileCandidates(ClassName::fromString(__CLASS__)); - $this->assertCount(1, $candidates); - $file = $candidates->best(); - $candidates = $converter->fileToClassCandidates($file); - $this->assertEquals('ClassToFileExtensionTest', $candidates->best()->name()); - } - - private function create(array $params): Container - { - return PhpactorContainer::fromExtensions([ - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - FilePathResolverExtension::class, - LoggingExtension::class, - ], $params); - } - - private function createConverter(array $config = []): ClassToFileFileToClass - { - $converter = $this->create($config)->get(ClassToFileExtension::SERVICE_CONVERTER); - return $converter; - } -} diff --git a/lib/Extension/ClassToFileExtra/Application/FileInfo.php b/lib/Extension/ClassToFileExtra/Application/FileInfo.php deleted file mode 100644 index 2e7de72a8e..0000000000 --- a/lib/Extension/ClassToFileExtra/Application/FileInfo.php +++ /dev/null @@ -1,39 +0,0 @@ -filesystem->createPath($sourcePath); - $classCandidates = $this->classToFileConverter->fileToClassCandidates(FilePath::fromString((string) $path)); - $return = [ - 'class' => null, - 'class_name' => null, - 'class_namespace' => null, - ]; - - if ($classCandidates->noneFound()) { - return $return; - } - - $best = $classCandidates->best(); - - return [ - 'class' => (string) $best, - 'class_name' => $best->name(), - 'class_namespace' => $best->namespace(), - ]; - } -} diff --git a/lib/Extension/ClassToFileExtra/ClassToFileExtraExtension.php b/lib/Extension/ClassToFileExtra/ClassToFileExtraExtension.php deleted file mode 100644 index c8fb6a4c13..0000000000 --- a/lib/Extension/ClassToFileExtra/ClassToFileExtraExtension.php +++ /dev/null @@ -1,42 +0,0 @@ -register('command.file_info', function (Container $container) { - return new FileInfoCommand( - $container->get('application.file_info'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'file:info' ]]); - - $container->register('application.file_info', function (Container $container) { - return new FileInfo( - $container->get('class_to_file.converter'), - $container->get('source_code_filesystem.simple') - ); - }); - - $container->register('class_to_file_extra.rpc.handler.file_info', function (Container $container) { - return new FileInfoHandler($container->get('application.file_info')); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => FileInfoHandler::NAME] ]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/ClassToFileExtra/Command/FileInfoCommand.php b/lib/Extension/ClassToFileExtra/Command/FileInfoCommand.php deleted file mode 100644 index 9db39d4c98..0000000000 --- a/lib/Extension/ClassToFileExtra/Command/FileInfoCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -setDescription('Return information about given file'); - $this->addArgument('path', InputArgument::REQUIRED, 'Source path or FQN'); - FormatHandler::configure($this); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $info = $this->infoForOffset->infoForFile( - $input->getArgument('path') - ); - - $format = $input->getOption('format'); - $this->dumperRegistry->get($format)->dump($output, $info); - - return 0; - } -} diff --git a/lib/Extension/ClassToFileExtra/Rpc/FileInfoHandler.php b/lib/Extension/ClassToFileExtra/Rpc/FileInfoHandler.php deleted file mode 100644 index db3b65506f..0000000000 --- a/lib/Extension/ClassToFileExtra/Rpc/FileInfoHandler.php +++ /dev/null @@ -1,37 +0,0 @@ -setRequired([ - self::PARAM_PATH, - ]); - } - - public function handle(array $arguments) - { - $fileInfo = $this->fileInfo->infoForFile($arguments[self::PARAM_PATH]); - - return ReturnResponse::fromValue($fileInfo); - } -} diff --git a/lib/Extension/CodeTransform/CodeTransformExtension.php b/lib/Extension/CodeTransform/CodeTransformExtension.php deleted file mode 100644 index 7f694b6b05..0000000000 --- a/lib/Extension/CodeTransform/CodeTransformExtension.php +++ /dev/null @@ -1,580 +0,0 @@ -setDefaults([ - self::PARAM_NEW_CLASS_VARIANTS => [], - self::PARAM_TEMPLATE_PATHS => [ // Ordered by priority - '%project_config%/templates', - '%config%/templates', - ], - self::PARAM_INDENTATION => ' ', - self::PARAM_GENERATE_ACCESSOR_PREFIX => '', - self::PARAM_GENERATE_ACCESSOR_UPPER_CASE_FIRST => false, - self::PARAM_GENERATE_MUTATOR_PREFIX => 'set', - self::PARAM_GENERATE_MUTATOR_UPPER_CASE_FIRST => true, - self::PARAM_GENERATE_MUTATOR_FLUENT => false, - self::PARAM_IMPORT_GLOBALS => false, - self::PARAM_OBJECT_FILL_HINT => true, - self::PARAM_OBJECT_FILL_NAMED => true, - ]); - $schema->setDescriptions([ - self::PARAM_NEW_CLASS_VARIANTS => 'Variants which should be suggested when class-create is invoked', - self::PARAM_TEMPLATE_PATHS => 'Paths in which to look for code templates', - self::PARAM_INDENTATION => 'Indentation chars to use in code generation and transformation', - self::PARAM_GENERATE_ACCESSOR_PREFIX => 'Prefix to use for generated accessors', - self::PARAM_GENERATE_ACCESSOR_UPPER_CASE_FIRST => 'If the first letter of a generated accessor should be made uppercase', - self::PARAM_GENERATE_MUTATOR_PREFIX => 'Prefix to use for generated mutators', - self::PARAM_GENERATE_MUTATOR_UPPER_CASE_FIRST => 'If the first letter of a generated mutator should be made uppercase', - self::PARAM_GENERATE_MUTATOR_FLUENT => 'If the mutator should be fluent', - self::PARAM_IMPORT_GLOBALS => 'Import functions even if they are in the global namespace', - self::PARAM_OBJECT_FILL_NAMED => 'Object fill refactoring: use named parameters', - self::PARAM_OBJECT_FILL_HINT => 'Object fill refactoring: show hint as a comment', - ]); - } - - public function load(ContainerBuilder $container): void - { - $this->registerTransformers($container); - $this->registerGenerators($container); - $this->registerFinders($container); - - if (class_exists(RpcExtension::class)) { - // this shouldn't be here - $this->registerRpc($container); - } - - $this->registerUpdater($container); - $this->registerRefactorings($container); - $this->registerTransformerImplementations($container); - $this->registerRenderer($container); - $this->registerGeneratorImplementations($container); - } - - private function registerTransformers(ContainerBuilder $container): void - { - $container->register(CodeTransform::class, function (Container $container) { - return CodeTransform::fromTransformers($container->get('code_transform.transformers')); - }); - - $container->register('code_transform.transformers', function (Container $container) { - $transformers = []; - foreach ($container->getServiceIdsForTag(self::TAG_TRANSFORMER) as $serviceId => $attrs) { - $transformers[$attrs['name']] = $container->get($serviceId); - } - - return Transformers::fromArray($transformers); - }); - } - - private function registerGenerators(ContainerBuilder $container): void - { - $container->register(self::SERVICE_CLASS_GENERATORS, function (Container $container) { - $generators = []; - foreach ($container->getServiceIdsForTag(self::TAG_NEW_CLASS_GENERATOR) as $serviceId => $attrs) { - $generator = $container->get($serviceId); - - // if the tagged "service" is an array, then assume it's an - // array of class generators and move on. - if (is_array($generator)) { - $generators = array_merge($generators, $generator); - continue; - } - - $this->assertNameAttribute($attrs, $serviceId); - $generators[$attrs['name']] = $generator; - } - - return Generators::fromArray($generators); - }); - - $container->register(self::SERVICE_CLASS_INFLECTORS, function (Container $container) { - $generators = []; - foreach ($container->getServiceIdsForTag(self::TAG_FROM_EXISTING_GENERATOR) as $serviceId => $attrs) { - $this->assertNameAttribute($attrs, $serviceId); - $generators[$attrs['name']] = $container->get($serviceId); - } - - return Generators::fromArray($generators); - }); - } - - private function registerRefactorings(ContainerBuilder $container): void - { - $container->register(ReplaceQualifierWithImport::class, function (Container $container) { - return new WorseReplaceQualifierWithImport( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(BuilderFactory::class), - $container->get(Updater::class) - ); - }); - - $container->register(ExtractConstant::class, function (Container $container) { - return new WorseExtractConstant( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class) - ); - }); - - $container->register(GenerateDecorator::class, function (Container $container) { - return new WorseGenerateDecorator( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - ); - }); - - $container->register(GenerateMember::class, function (Container $container) { - return new WorseGenerateMember( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(BuilderFactory::class), - $container->get(Updater::class) - ); - }); - - $container->register('code_transform.generate_accessor', function (Container $container) { - return new WorseGenerateAccessor( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - $container->parameter(self::PARAM_GENERATE_ACCESSOR_PREFIX)->string(), - $container->parameter(self::PARAM_GENERATE_ACCESSOR_UPPER_CASE_FIRST)->bool() - ); - }); - - $container->register('code_transform.generate_mutator', function (Container $container) { - return new WorseGenerateMutator( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - $container->parameter(self::PARAM_GENERATE_MUTATOR_PREFIX)->string(), - $container->parameter(self::PARAM_GENERATE_MUTATOR_UPPER_CASE_FIRST)->bool(), - $container->parameter(self::PARAM_GENERATE_MUTATOR_FLUENT)->bool() - ); - }); - - $container->register(RenameVariable::class, function (Container $container) { - return new TolerantRenameVariable(); - }); - - $container->register(OverrideMethod::class, function (Container $container) { - return new WorseOverrideMethod( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(BuilderFactory::class), - $container->get(Updater::class), - $container->get(PhpVersionResolver::class)->resolve() ?? PHP_VERSION, - ); - }); - - $container->register(ExtractMethod::class, function (Container $container) { - return new WorseExtractMethod( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(BuilderFactory::class), - $container->get(Updater::class), - $container->get(AstProvider::class), - ); - }); - - $container->register(ExtractExpression::class, function (Container $container) { - return new TolerantExtractExpression(); - }); - - $container->register(ImportName::class, function (Container $container) { - return new TolerantImportName( - $container->get(Updater::class), - $container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class), - $container->parameter(self::PARAM_IMPORT_GLOBALS)->bool(), - ); - }); - $container->register(WorseFillObject::class, function (Container $container) { - return new WorseFillObject( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class), - $container->get(Updater::class), - $container->parameter(self::PARAM_OBJECT_FILL_NAMED)->bool(), - $container->parameter(self::PARAM_OBJECT_FILL_HINT)->bool(), - ); - }); - $container->register(WorseFillMatchArms::class, function (Container $container) { - return new WorseFillMatchArms( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class), - ); - }); - $container->register(TolerantHereDoc::class, function (Container $container) { - return new TolerantHereDoc( - $container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class), - ); - }); - - $container->register(GenerateConstructor::class, function (Container $container) { - return new WorseGenerateConstructor( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(BuilderFactory::class), - $container->get(Updater::class), - $container->get(WorseReflectionExtension::SERVICE_AST_PROVIDER), - ); - }); - - $container->register(ChangeVisiblity::class, function (Container $container) { - return new TolerantChangeVisiblity(); - }); - } - - private function registerGeneratorImplementations(ContainerBuilder $container): void - { - $container->register('code_transform_extra.class_generator.variants', function (Container $container) { - $generators = [ - 'default' => new ClassGenerator($container->get('code_transform.renderer')), - 'interface' => new ClassGenerator($container->get('code_transform.renderer'), 'interface'), - 'trait' => new ClassGenerator($container->get('code_transform.renderer'), 'trait'), - 'enum' => new ClassGenerator($container->get('code_transform.renderer'), 'enum'), - ]; - foreach ($container->getParameter(self::PARAM_NEW_CLASS_VARIANTS) as $variantName => $variant) { - $generators[$variantName] = new ClassGenerator($container->get('code_transform.renderer'), $variant); - } - - return $generators; - }, [ CodeTransformExtension::TAG_NEW_CLASS_GENERATOR => [] ]); - - $container->register('code_transform_extra.from_existing_generator', function (Container $container) { - return new InterfaceFromExistingGenerator( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get('code_transform.renderer') - ); - }, [ CodeTransformExtension::TAG_FROM_EXISTING_GENERATOR => [ - 'name' => 'interface' - ] ]); - } - - private function registerUpdater(ContainerBuilder $container): void - { - $container->register(Updater::class, function (Container $container) { - return new TolerantUpdater( - $container->get('code_transform.renderer'), - $container->get(TextFormat::class), - $container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class) - ); - }); - $container->register(BuilderFactory::class, function (Container $container) { - return new WorseBuilderFactory($container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class)); - }); - - $container->register(DocBlockUpdater::class, function (Container $container) { - return new ParserDocblockUpdater(DocblockParser::create(), $container->get(TextFormat::class)); - }); - } - - private function registerFinders(ContainerBuilder $container): void - { - $container->register(InterestingOffsetFinder::class, function (Container $container) { - return new WorseInterestingOffsetFinder( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class) - ); - }); - $container->register(MissingMemberFinder::class, function (Container $container) { - return new WorseMissingMemberFinder( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class) - ); - }); - } - - private function registerRenderer(ContainerBuilder $container): void - { - $container->register('code_transform.twig_loader', function (Container $container) { - $resolver = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER); - $loader = new ChainLoader(); - $templatePaths = $container->getParameter(self::PARAM_TEMPLATE_PATHS); - $templatePaths[] = self::APP_TEMPLATE_PATH; - $templatePaths[] = self::APP_TEMPLATE_VENDOR; - - $resolvedTemplatePaths = array_map(function (string $path) use ($resolver) { - return $resolver->resolve($path); - }, $templatePaths); - - $phpVersion = $container->get(PhpVersionResolver::class)->resolve(); - $paths = (new PhpVersionPathResolver($phpVersion))->resolve($resolvedTemplatePaths); - - foreach ($paths as $path) { - $loader->addLoader(new FilesystemLoader($path)); - } - - return $loader; - }); - - $container->register('code_transform.renderer', function (Container $container) { - $twig = new Environment($container->get('code_transform.twig_loader'), [ - 'strict_variables' => true, - 'autoescape' => false, - ]); - $renderer = new TwigRenderer($twig); - $twig->addExtension(new TwigExtension( - $container->get(TextFormat::class), - $container->get(WorseTypeRenderer::class) - )); - - return $renderer; - }); - - $container->register(WorseTypeRenderer::class, function (Container $container) { - $version = $container->get(PhpVersionResolver::class); - assert($version instanceof PhpVersionResolver); - $version = $version->resolve(); - return (new WorseTypeRendererFactory([ - '7.0' => new WorseTypeRenderer70(), - '7.4' => new WorseTypeRenderer74(), - '8.0' => new WorseTypeRenderer80(), - '8.1' => new WorseTypeRenderer81(), - '8.2' => new WorseTypeRenderer82(), - ]))->rendererFor($version); - }); - - $container->register(TextFormat::class, function (Container $container) { - return new TextFormat($container->parameter(self::PARAM_INDENTATION)->string()); - }); - } - - private function assertNameAttribute($attrs, $serviceId): void - { - if (!isset($attrs['name'])) { - throw new RuntimeException(sprintf( - 'Generator "%s" must be registered with the "name" tag', - $serviceId - )); - } - } - - private function registerRpc(ContainerBuilder $container): void - { - $container->register('code_transform.rpc.handler.class_inflect', function (Container $container) { - return new ClassInflectHandler( - $container->get(self::SERVICE_CLASS_INFLECTORS), - $container->get(ClassToFileExtension::SERVICE_CONVERTER) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ClassInflectHandler::NAME] ]); - - $container->register('code_transform.rpc.handler.class_new', function (Container $container) { - return new ClassNewHandler( - $container->get(self::SERVICE_CLASS_GENERATORS), - $container->get(ClassToFileExtension::SERVICE_CONVERTER) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ClassNewHandler::NAME] ]); - - - $container->register('code_transform.rpc.handler.transform', function (Container $container) { - return new TransformHandler( - $container->get(CodeTransform::class) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => TransformHandler::NAME] ]); - } - - private function registerTransformerImplementations(ContainerBuilder $container): void - { - $container->register('code_transform.transformer.complete_constructor_private', function (Container $container) { - return new CompleteConstructor( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - 'private', - ); - }, [ 'code_transform.transformer' => [ 'name' => 'complete_constructor' ]]); - - $container->register('code_transform.transformer.complete_constructor_public', function (Container $container) { - return new CompleteConstructor( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - 'public', - ); - }, [ 'code_transform.transformer' => [ 'name' => 'complete_constructor_public' ]]); - $container->register('code_transform.transformer.promote_constructor_private', function (Container $container) { - return new CompleteConstructor( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - 'private', - true, - ); - }, [ 'code_transform.transformer' => [ 'name' => 'promote_constructor' ]]); - - $container->register('code_transform.transformer.promote_constructor_public', function (Container $container) { - return new CompleteConstructor( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - 'public', - true, - ); - }, [ 'code_transform.transformer' => [ 'name' => 'promote_constructor_public' ]]); - - $container->register('code_transform.transformer.implement_contracts', function (Container $container) { - return new ImplementContracts( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - $container->get(BuilderFactory::class) - ); - }, [ 'code_transform.transformer' => [ 'name' => 'implement_contracts' ]]); - - $container->register('code_transform.transformer.fix_namespace_class_name', function (Container $container) { - return new ClassNameFixerTransformer( - $container->get('class_to_file.file_to_class') - ); - }, [ 'code_transform.transformer' => [ 'name' => 'fix_namespace_class_name' ]]); - - $container->register(UpdateDocblockReturnTransformer::class, function (Container $container) { - return new UpdateDocblockReturnTransformer( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - $container->get(BuilderFactory::class), - $container->get(DocBlockUpdater::class), - ); - }, [ 'code_transform.transformer' => [ 'name' => 'add_missing_docblocks_return' ]]); - - $container->register(UpdateDocblockParamsTransformer::class, function (Container $container) { - return new UpdateDocblockParamsTransformer( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - $container->get(BuilderFactory::class), - $container->get(DocBlockUpdater::class) - ); - }, [ 'code_transform.transformer' => [ 'name' => 'add_missing_params' ]]); - $container->register(UpdateDocblockGenericTransformer::class, function (Container $container) { - return new UpdateDocblockGenericTransformer( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - $container->get(BuilderFactory::class), - $container->get(DocBlockUpdater::class) - ); - }, [ 'code_transform.transformer' => [ 'name' => 'add_missing_class_generic' ]]); - - $container->register(UpdateReturnTypeTransformer::class, function (Container $container) { - return new UpdateReturnTypeTransformer( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - $container->get(BuilderFactory::class) - ); - }, [ 'code_transform.transformer' => [ 'name' => 'add_missing_return_types' ]]); - - $container->register('code_transform.transformer.add_missing_properties', function (Container $container) { - return new AddMissingProperties( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class) - ); - }, [ 'code_transform.transformer' => [ 'name' => 'add_missing_properties' ]]); - - $container->register(AddOverrideAttributeTransformer::class, function (Container $container) { - return new AddOverrideAttributeTransformer( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(PhpVersionResolver::class)->resolve() ?? PHP_VERSION, - $container->get(WorseReflectionExtension::SERVICE_AST_PROVIDER), - ); - }, [ 'code_transform.transformer' => [ 'name' => 'add_override_attribute' ]]); - - $container->register('code_transform.transformer.remove_unused_imports', function (Container $container) { - return new RemoveUnusedImportsTransformer( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(WorseReflectionExtension::SERVICE_AST_PROVIDER), - ); - }, [ 'code_transform.transformer' => [ 'name' => 'remove_unused_imports' ]]); - } -} diff --git a/lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php b/lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php deleted file mode 100644 index 757b2beacd..0000000000 --- a/lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php +++ /dev/null @@ -1,125 +0,0 @@ -setDefaults([ - self::PARAM_NEW_PATH => null, - self::PARAM_VARIANT => null, - self::PARAM_OVERWRITE_EXISTING => null, - ]); - $resolver->setRequired([ - self::PARAM_CURRENT_PATH - ]); - } - - public function handle(array $arguments) - { - if (false === $arguments[self::PARAM_OVERWRITE_EXISTING]) { - return EchoResponse::fromMessage('Cancelled'); - } - - if (null === $arguments[self::PARAM_VARIANT]) { - $this->requireInput(ChoiceInput::fromNameLabelChoicesAndDefault( - self::PARAM_VARIANT, - 'Variant: ', - (array) array_combine( - $this->generators->names(), - $this->generators->names() - ) - )); - } - - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_NEW_PATH, - $this->newMessage(), - $arguments[self::PARAM_CURRENT_PATH], - 'file' - )); - - if ( - $arguments[self::PARAM_NEW_PATH] && - null === $arguments[self::PARAM_OVERWRITE_EXISTING] && - file_exists($arguments[self::PARAM_NEW_PATH]) && - 0 !== filesize($arguments[self::PARAM_NEW_PATH]) - ) { - $this->requireInput(ConfirmInput::fromNameAndLabel( - self::PARAM_OVERWRITE_EXISTING, - 'File exists and is not empty, overwrite?' - )); - } - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $code = $this->generate($arguments); - - $this->writeFileContents($arguments, $code); - - return ReplaceFileSourceResponse::fromPathAndSource( - ($code->uri()->scheme() === 'file' && $code->uri()->path()) ? $code->uri()->path() : $arguments[self::PARAM_NEW_PATH], - (string) $code - ); - } - - abstract protected function generate(array $arguments): SourceCode; - - abstract protected function newMessage(): string; - - protected function className(string $path) - { - $candidates = $this->fileToClass->fileToClassCandidates(FilePath::fromString($path)); - return ClassName::fromString($candidates->best()->__toString()); - } - - private function writeFileContents(array $arguments, SourceCode $code): void - { - $newPath = $arguments[self::PARAM_NEW_PATH]; - $dirName = dirname($newPath); - - if (!file_exists($dirName)) { - if (!@mkdir($dirName, 0777, true)) { - throw new RuntimeException(sprintf( - 'Could not create directory at "%s"', - $dirName - )); - } - } - - if (!file_put_contents($newPath, (string) $code)) { - throw new RuntimeException(sprintf( - 'Could not save file contents to "%s"', - $newPath - )); - } - } -} diff --git a/lib/Extension/CodeTransform/Rpc/ClassInflectHandler.php b/lib/Extension/CodeTransform/Rpc/ClassInflectHandler.php deleted file mode 100644 index 7a1877456b..0000000000 --- a/lib/Extension/CodeTransform/Rpc/ClassInflectHandler.php +++ /dev/null @@ -1,35 +0,0 @@ -generators->get($arguments[self::PARAM_VARIANT]); - assert($inflector instanceof GenerateFromExisting); - - $currentClass = $this->className($arguments[self::PARAM_CURRENT_PATH]); - $targetClass = $this->className($arguments[self::PARAM_NEW_PATH]); - - return $inflector->generateFromExisting( - $currentClass, - $targetClass - ); - } -} diff --git a/lib/Extension/CodeTransform/Rpc/ClassNewHandler.php b/lib/Extension/CodeTransform/Rpc/ClassNewHandler.php deleted file mode 100644 index 54b467ce8f..0000000000 --- a/lib/Extension/CodeTransform/Rpc/ClassNewHandler.php +++ /dev/null @@ -1,30 +0,0 @@ -generators->get($arguments[self::PARAM_VARIANT]); - assert($generator instanceof GenerateNew); - - $className = $this->className($arguments[self::PARAM_NEW_PATH]); - return $generator->generateNew($className); - } -} diff --git a/lib/Extension/CodeTransform/Rpc/TransformHandler.php b/lib/Extension/CodeTransform/Rpc/TransformHandler.php deleted file mode 100644 index 5d3eb171b8..0000000000 --- a/lib/Extension/CodeTransform/Rpc/TransformHandler.php +++ /dev/null @@ -1,83 +0,0 @@ -setDefaults([ - self::PARAM_NAME => null, - ]); - $resolver->setRequired([ - self::PARAM_PATH, - self::PARAM_SOURCE, - ]); - } - - public function handle(array $arguments) - { - if (null === $arguments[self::PARAM_NAME]) { - return $this->transformerChoiceAction($arguments[self::PARAM_PATH], $arguments[self::PARAM_SOURCE]); - } - - $code = SourceCode::fromStringAndPath($arguments[self::PARAM_SOURCE], $arguments[self::PARAM_PATH]); - - $transformedCode = $this->codeTransform->transform($code, [ - $arguments[self::PARAM_NAME] - ]); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $arguments[self::PARAM_PATH], - $arguments[self::PARAM_SOURCE], - (string) $transformedCode - ); - } - - private function transformerChoiceAction(string $path, string $source) - { - $transformers= $this->codeTransform->transformers()->names(); - - // get destination path - return InputCallbackResponse::fromCallbackAndInputs( - Request::fromNameAndParameters( - $this->name(), - [ - self::PARAM_NAME => null, - self::PARAM_PATH => $path, - self::PARAM_SOURCE => $source, - ] - ), - [ - ChoiceInput::fromNameLabelChoicesAndDefault( - self::PARAM_NAME, - 'Transform: ', - (array) array_combine($transformers, $transformers) - ) - ] - ); - } -} diff --git a/lib/Extension/CodeTransform/Tests/Unit/CodeTransformExtensionTest.php b/lib/Extension/CodeTransform/Tests/Unit/CodeTransformExtensionTest.php deleted file mode 100644 index 88897f4bb6..0000000000 --- a/lib/Extension/CodeTransform/Tests/Unit/CodeTransformExtensionTest.php +++ /dev/null @@ -1,71 +0,0 @@ -createContainer(); - - foreach ($container->getServiceIds() as $serviceId) { - $service = $container->get($serviceId); - } - $this->addToAssertionCount(1); - } - - #[DataProvider('provideClassNew')] - public function testClassNew(string $variant): void - { - /** @var array */ - $generators = $this->createContainer()->get('code_transform_extra.class_generator.variants'); - self::assertArrayHasKey($variant, $generators); - $generators[$variant]->generateNew(ClassName::fromString('Foo')); - } - - /** - * @return Generator - */ - public static function provideClassNew(): Generator - { - yield ['default']; - yield ['interface']; - yield ['enum']; - yield ['trait']; - } - - private function createContainer(): Container - { - $container = PhpactorContainer::fromExtensions([ - CodeTransformExtension::class, - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - FilePathResolverExtension::class, - LoggingExtension::class, - PhpExtension::class, - WorseReflectionExtension::class, - ], [ - CodeTransformExtension::PARAM_TEMPLATE_PATHS => [ - __DIR__ . '/../../../../../templates/code', - ], - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__, - ]); - - return $container; - } -} diff --git a/lib/Extension/CodeTransform/Tests/Unit/Rpc/AbstractClassGenerateHandler.php b/lib/Extension/CodeTransform/Tests/Unit/Rpc/AbstractClassGenerateHandler.php deleted file mode 100644 index b293069c76..0000000000 --- a/lib/Extension/CodeTransform/Tests/Unit/Rpc/AbstractClassGenerateHandler.php +++ /dev/null @@ -1,98 +0,0 @@ -fileToClass = $this->prophesize(FileToClass::class); - $this->workspace = Workspace::create(__DIR__ . '/../../Workspace'); - $this->workspace->reset(); - } - - public function testAsksToOverwriteExistingFile(): void - { - $path = $this->workspace->path('foo'); - file_put_contents($path, 'foo'); - - $response = $this->createTester()->handle($this->createHandler()->name(), [ - ClassInflectHandler::PARAM_CURRENT_PATH => self::EXAMPLE_PATH, - ClassInflectHandler::PARAM_NEW_PATH => $path, - ClassInflectHandler::PARAM_VARIANT=> self::EXAMPLE_VARIANT, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $response); - $input = $response->inputs()[0]; - $this->assertInstanceOf(ConfirmInput::class, $input); - $this->assertEquals(ClassInflectHandler::PARAM_OVERWRITE_EXISTING, $input->name()); - } - - public function testCancelsOverwritesExistingFile(): void - { - $path = $this->workspace->path('foo'); - file_put_contents($path, 'foo'); - - $response = $this->createTester()->handle($this->createHandler()->name(), [ - ClassInflectHandler::PARAM_CURRENT_PATH => $path, - ClassInflectHandler::PARAM_NEW_PATH => $path, - ClassInflectHandler::PARAM_VARIANT=> self::EXAMPLE_VARIANT, - ClassInflectHandler::PARAM_OVERWRITE_EXISTING => false, - ]); - - $this->assertInstanceOf(EchoResponse::class, $response); - } - - public function testAsksForVariant(): void - { - $response = $this->createTester()->handle($this->createHandler()->name(), [ - ClassInflectHandler::PARAM_CURRENT_PATH => self::EXAMPLE_PATH - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $response); - $this->assertCount(2, $response->inputs()); - $input = $response->inputs()[0]; - $this->assertInstanceOf(ChoiceInput::class, $input); - - $input = $response->inputs()[1]; - $this->assertInstanceOf(TextInput::class, $input); - } - - abstract public function createHandler(): Handler; - - protected function createTester(): HandlerTester - { - return new HandlerTester($this->createHandler()); - } - - protected function exampleNewPath() - { - return Path::canonicalize($this->workspace->path(self::EXAMPLE_NEW_PATH)); - } -} diff --git a/lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassInflectHandlerTest.php b/lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassInflectHandlerTest.php deleted file mode 100644 index bf580f59fa..0000000000 --- a/lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassInflectHandlerTest.php +++ /dev/null @@ -1,69 +0,0 @@ -generator = $this->prophesize(GenerateFromExisting::class); - } - - public function createHandler(): Handler - { - return new ClassInflectHandler( - new Generators([ - self::EXAMPLE_VARIANT => $this->generator->reveal() - ]), - $this->fileToClass->reveal() - ); - } - - public function testInflectsClass(): void - { - $this->fileToClass->fileToClassCandidates( - FilePath::fromString(self::EXAMPLE_PATH) - )->willReturn(ClassNameCandidates::fromClassNames([ - $class1 = ConvertedClassName::fromString(self::EXAMPLE_CLASS_1) - ])); - - $this->fileToClass->fileToClassCandidates( - FilePath::fromString($this->exampleNewPath()) - )->willReturn(ClassNameCandidates::fromClassNames([ - $class2 = ConvertedClassName::fromString(self::EXAMPLE_CLASS_2) - ])); - - $this->generator->generateFromExisting( - ClassName::fromString(self::EXAMPLE_CLASS_1), - ClassName::fromString(self::EXAMPLE_CLASS_2) - )->willReturn(SourceCode::fromStringAndPath('exampleNewPath())); - - $response = $this->createTester()->handle(ClassInflectHandler::NAME, [ - ClassInflectHandler::PARAM_CURRENT_PATH => self::EXAMPLE_PATH, - ClassInflectHandler::PARAM_NEW_PATH => $this->exampleNewPath(), - ClassInflectHandler::PARAM_VARIANT => self::EXAMPLE_VARIANT, - ]); - - $this->assertInstanceOf(ReplaceFileSourceResponse::class, $response); - $this->assertEquals($this->exampleNewPath(), $response->path()); - $this->assertFileExists($this->exampleNewPath()); - } -} diff --git a/lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassNewHandlerTest.php b/lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassNewHandlerTest.php deleted file mode 100644 index fa98dad009..0000000000 --- a/lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassNewHandlerTest.php +++ /dev/null @@ -1,65 +0,0 @@ -generator = $this->prophesize(GenerateNew::class); - } - - public function createHandler(): Handler - { - return new ClassNewHandler( - new Generators([ - 'one' => $this->generator->reveal() - ]), - $this->fileToClass->reveal() - ); - } - - public function testGeneratesNewClass(): void - { - $this->fileToClass->fileToClassCandidates( - FilePath::fromString($this->exampleNewPath()) - )->willReturn(ClassNameCandidates::fromClassNames([ - $class1 = ConvertedClassName::fromString(self::EXAMPLE_CLASS_1) - ])); - - $this->generator->generateNew( - ClassName::fromString(self::EXAMPLE_CLASS_1) - )->willReturn( - SourceCode::fromStringAndPath('exampleNewPath()) - ); - - $response = $this->createTester()->handle(ClassNewHandler::NAME, [ - ClassInflectHandler::PARAM_CURRENT_PATH => self::EXAMPLE_PATH, - ClassInflectHandler::PARAM_NEW_PATH => $this->exampleNewPath(), - ClassInflectHandler::PARAM_VARIANT => self::EXAMPLE_VARIANT, - ]); - - $this->assertInstanceOf(ReplaceFileSourceResponse::class, $response); - $this->assertEquals($this->exampleNewPath(), $response->path()); - $this->assertFileExists($this->exampleNewPath()); - } -} diff --git a/lib/Extension/CodeTransform/Tests/Unit/Rpc/TransformHandlerTest.php b/lib/Extension/CodeTransform/Tests/Unit/Rpc/TransformHandlerTest.php deleted file mode 100644 index 32d5dc1908..0000000000 --- a/lib/Extension/CodeTransform/Tests/Unit/Rpc/TransformHandlerTest.php +++ /dev/null @@ -1,80 +0,0 @@ -codeTransform = $this->prophesize(CodeTransform::class); - $this->tester = new HandlerTester(new TransformHandler( - $this->codeTransform->reveal() - )); - - $this->transformer = $this->prophesize(Transformer::class); - } - - public function testPresentsTransformerChoice(): void - { - $this->codeTransform->transformers()->willReturn(new Transformers([ - 'trans' => $this->transformer->reveal() - ])); - $response = $this->tester->handle('transform', [ - 'path' => self::EXAMPLE_NEW_PATH, - 'source' => self::EXAMPLE_SOURCE_CODE - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $response); - $this->assertCount(1, $response->inputs()); - $choiceInput = $response->inputs()[0]; - $this->assertInstanceOf(ChoiceInput::class, $choiceInput); - $this->assertCount(1, $choiceInput->choices()); - $this->assertEquals(['trans' => 'trans'], $choiceInput->choices()); - } - - public function testTransformsCode(): void - { - $expectedTransformed = SourceCode::fromStringAndPath('HALLO', '/GOODBYE'); - - $this->codeTransform->transformers()->willReturn(new Transformers([ - 'trans' => $this->transformer->reveal() - ])); - $this->codeTransform->transform(SourceCode::fromStringAndPath( - self::EXAMPLE_SOURCE_CODE, - self::EXAMPLE_NEW_PATH - ), ['trans'])->willReturn($expectedTransformed); - - $response = $this->tester->handle('transform', [ - TransformHandler::PARAM_PATH => self::EXAMPLE_NEW_PATH, - TransformHandler::PARAM_SOURCE => self::EXAMPLE_SOURCE_CODE, - TransformHandler::PARAM_NAME => 'trans', - ]); - - $this->assertInstanceOf(UpdateFileSourceResponse::class, $response); - $this->assertEquals(self::EXAMPLE_NEW_PATH, $response->path()); - $this->assertEquals('HALLO', $response->newSource()); - } -} diff --git a/lib/Extension/CodeTransformExtra/Application/AbstractClassGenerator.php b/lib/Extension/CodeTransformExtra/Application/AbstractClassGenerator.php deleted file mode 100644 index 9b93503f45..0000000000 --- a/lib/Extension/CodeTransformExtra/Application/AbstractClassGenerator.php +++ /dev/null @@ -1,43 +0,0 @@ -generators->names(); - } - - protected function logger(): LoggerInterface - { - return $this->logger; - } - - protected function writeFile(string $filePath, string $code, bool $overwrite): void - { - if (false === $overwrite && file_exists($filePath) && 0 !== filesize($filePath)) { - throw new FileAlreadyExists(sprintf('File "%s" already exists and is non-empty', $filePath)); - } - - if (!file_exists(dirname($filePath))) { - mkdir(dirname($filePath), 0777, true); - } - - file_put_contents(FilePath::fromString($filePath), (string) $code); - } -} diff --git a/lib/Extension/CodeTransformExtra/Application/ClassInflect.php b/lib/Extension/CodeTransformExtra/Application/ClassInflect.php deleted file mode 100644 index 1395840dd7..0000000000 --- a/lib/Extension/CodeTransformExtra/Application/ClassInflect.php +++ /dev/null @@ -1,59 +0,0 @@ - $globDest) { - if (false === is_file($globSrc)) { - continue; - } - - try { - $newCodes[] = $this->doGenerateFromExisting($globSrc, $globDest, $variant, $overwrite); - } catch (NotFound $e) { - $this->logger()->error($e->getMessage()); - } - } - - return $newCodes; - } - - private function doGenerateFromExisting(string $src, string $dest, string $variant, bool $overwrite): SourceCode - { - $srcClassName = $this->normalizer->normalizeToClass($src); - $destClassName = $this->normalizer->normalizeToClass($dest); - - $code = $this->generators->get($variant)->generateFromExisting( - ClassName::fromString((string) $srcClassName), - ClassName::fromString((string) $destClassName) - ); - - $filePath = $this->normalizer->normalizeToFile($destClassName); - $code = $code->withPath($filePath); - - $this->writeFile($filePath, (string) $code, $overwrite); - - return $code; - } -} diff --git a/lib/Extension/CodeTransformExtra/Application/ClassNew.php b/lib/Extension/CodeTransformExtra/Application/ClassNew.php deleted file mode 100644 index b09427563e..0000000000 --- a/lib/Extension/CodeTransformExtra/Application/ClassNew.php +++ /dev/null @@ -1,25 +0,0 @@ -normalizer->normalizeToClass($src); - - $code = $this->generators->get($variant)->generateNew(ClassName::fromString((string) $className)); - - $filePath = Phpactor::isFile($src) ? Phpactor::normalizePath($src) : $this->normalizer->normalizeToFile($className); - - $code = $code->withPath($filePath); - - $this->writeFile($filePath, (string) $code, $overwrite); - - return $code; - } -} diff --git a/lib/Extension/CodeTransformExtra/Application/ClassNew1.php b/lib/Extension/CodeTransformExtra/Application/ClassNew1.php deleted file mode 100644 index 4b8f9d15f4..0000000000 --- a/lib/Extension/CodeTransformExtra/Application/ClassNew1.php +++ /dev/null @@ -1,7 +0,0 @@ -filesystemHelper = new FilesystemHelper(); - } - - public function transform($source, array $transformations) - { - if (file_exists($source)) { - /** @var string $workDir */ - $workDir = getcwd(); - $source = Path::makeAbsolute($source, $workDir); - $source = SourceCode::fromStringAndPath(file_get_contents($source), $source); - } - - if (!$source instanceof SourceCode) { - $source = $this->filesystemHelper->contentsFromFileOrStdin($source); - $source = SourceCode::fromString($source); - } - - $transformedCode = $this->transform->transform($source, $transformations); - - return $transformedCode; - } -} diff --git a/lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php b/lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php deleted file mode 100644 index fede1784b8..0000000000 --- a/lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php +++ /dev/null @@ -1,178 +0,0 @@ -setDefaults([ - ]); - } - - public function load(ContainerBuilder $container): void - { - $this->registerApplication($container); - $this->registerConsole($container); - $this->registerRpc($container); - } - - private function registerApplication(ContainerBuilder $container): void - { - $container->register('application.transform', function (Container $container) { - return new Transformer( - $container->get(CodeTransform::class) - ); - }); - - $container->register('application.class_new', function (Container $container) { - return new ClassNew( - $container->get('application.helper.class_file_normalizer'), - $container->get('code_transform.new_class_generators') - ); - }); - - $container->register('application.class_inflect', function (Container $container) { - return new ClassInflect( - $container->get('application.helper.class_file_normalizer'), - $container->get('code_transform.from_existing_generators'), - LoggingExtension::channelLogger($container, 'CT'), - ); - }); - } - - private function registerConsole(ContainerBuilder $container): void - { - $container->register('command.transform', function (Container $container) { - return new ClassTransformCommand( - $container->get('application.transform') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'class:transform' ]]); - - $container->register('command.class_new', function (Container $container) { - return new ClassNewCommand( - $container->get('application.class_new'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'class:new' ]]); - - $container->register('command.class_inflect', function (Container $container) { - return new ClassInflectCommand( - $container->get('application.class_inflect'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'class:inflect' ]]); - } - - private function registerRpc(ContainerBuilder $container): void - { - $container->register('code_transform.rpc.handler.extract_constant', function (Container $container) { - return new ExtractConstantHandler( - $container->get(ExtractConstant::class) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ExtractConstantHandler::NAME] ]); - - $container->register('code_transform.rpc.handler.extract_method', function (Container $container) { - return new ExtractMethodHandler( - $container->get(ExtractMethod::class) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ExtractMethodHandler::NAME] ]); - - $container->register('code_transform.rpc.handler.generate_accessor', function (Container $container) { - return new PropertyAccessGeneratorHandler( - 'generate_accessor', - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get('code_transform.generate_accessor') - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => 'generate_accessor'] ]); - - $container->register('code_transform.rpc.handler.generate_mutator', function (Container $container) { - return new PropertyAccessGeneratorHandler( - 'generate_mutator', - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get('code_transform.generate_mutator') - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => 'generate_mutator'] ]); - - $container->register('code_transform.rpc.handler.generate_method', function (Container $container) { - return new GenerateMethodHandler( - $container->get(GenerateMember::class) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => GenerateMethodHandler::NAME] ]); - - $container->register('code_transform.rpc.handler.refactor.import_class', function (Container $container) { - return new ImportClassHandler( - $container->get(ImportName::class), - $container->get('application.class_search'), - SourceCodeFilesystemExtension::FILESYSTEM_COMPOSER - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ImportClassHandler::NAME] ]); - - $container->register('code_transform.rpc.handler.rename_variable', function (Container $container) { - return new RenameVariableHandler( - $container->get(RenameVariable::class) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => RenameVariableHandler::NAME] ]); - - $container->register('code_transform.handler.change_visiblity', function (Container $container) { - return new ChangeVisiblityHandler( - $container->get(ChangeVisiblity::class) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ChangeVisiblityHandler::NAME] ]); - - $container->register('code_transform.rpc.handler.override_method', function (Container $container) { - return new OverrideMethodHandler( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(OverrideMethod::class) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => OverrideMethodHandler::NAME] ]); - - $container->register('code_transform.rpc.handler.extract_expression', function (Container $container) { - return new ExtractExpressionHandler( - $container->get(ExtractExpression::class) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ExtractExpressionHandler::NAME] ]); - - $container->register('code_transform.rpc.handler.import_unresolvable_classes', function (Container $container) { - return new ImportMissingClassesHandler( - $container->get(RpcExtension::SERVICE_REQUEST_HANDLER), - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ImportMissingClassesHandler::NAME] ]); - } -} diff --git a/lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php b/lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php deleted file mode 100644 index 9f8472ce80..0000000000 --- a/lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php +++ /dev/null @@ -1,86 +0,0 @@ -setDescription('Inflect new class from existing class (path or FQN)'); - $this->addArgument('src', InputArgument::REQUIRED, 'Source path or FQN'); - $this->addArgument('dest', InputArgument::REQUIRED, 'Destination path or FQN'); - $this->addArgument('variant', InputOption::VALUE_REQUIRED, 'Type of inflection', 'default'); - $this->addOption('list', null, InputOption::VALUE_NONE, 'List variants'); - $this->addOption('force', null, InputOption::VALUE_NONE, 'Force overwriting'); - FormatHandler::configure($this); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - if ($input->getOption('list')) { - return $this->listGenerators($input, $output); - } - - $out = $this->process($input, $output); - $this->dumperRegistry->get($input->getOption('format'))->dump($output, $out); - - return 0; - } - - private function process(InputInterface $input, OutputInterface $output) - { - $src = $input->getArgument('src'); - $dest = $input->getArgument('dest'); - $variant = $input->getArgument('variant'); - $response = [ - 'src' => $src, - 'dest' => $dest, - 'path' => null, - 'exists' => false, - ]; - - try { - $response['path'] = $this->classInflect->generateFromExisting($src, $dest, $variant, $input->getOption('force')); - } catch (FileAlreadyExists) { - $questionHelper = new QuestionHelper(); - $question = new ConfirmationQuestion('File already exists, overwrite? [y/n]', false); - - if (false === $questionHelper->ask($input, $output, $question)) { - $response['exists'] = true; - return $response; - } - - $filePath = $this->classInflect->generateFromExisting($src, $dest, $variant, true); - $response['path'] = $filePath; - } - - return $response; - } - - private function listGenerators(InputInterface $input, OutputInterface $output) - { - $dumper = $this->dumperRegistry->get($input->getOption('format')); - $dumper->dump($output, $this->classInflect->availableGenerators()); - - return 0; - } -} diff --git a/lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php b/lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php deleted file mode 100644 index 3409cc2a41..0000000000 --- a/lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php +++ /dev/null @@ -1,93 +0,0 @@ -setDescription('Create new class (path or FQN)'); - $this->addArgument('src', InputArgument::REQUIRED, 'Source path or FQN'); - $this->addOption('variant', null, InputOption::VALUE_REQUIRED, 'Variant', 'default'); - $this->addOption('list', null, InputOption::VALUE_NONE, 'List variants'); - $this->addOption('force', null, InputOption::VALUE_NONE, 'Force overwriting'); - FormatHandler::configure($this); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - if ($input->getOption('list')) { - $this->listGenerators($input, $output); - return 0; - } - - $out = $this->process($input, $output); - $this->dumperRegistry->get($input->getOption('format'))->dump($output, $out); - - return 0; - } - - private function process(InputInterface $input, OutputInterface $output) - { - $src = $input->getArgument('src'); - $variant = $input->getOption('variant'); - - try { - $sourceCode = $this->generateSourceCode($src, $variant, $input, $output); - } catch (FileAlreadyExists) { - return [ - 'src' => $src, - 'path' => null, - 'exists' => true, - ]; - } - - return [ - 'src' => $src, - 'path' => $sourceCode->uri()->path(), - 'exists' => false, - ]; - } - - private function listGenerators(InputInterface $input, OutputInterface $output): void - { - $dumper = $this->dumperRegistry->get($input->getOption('format')); - $dumper->dump($output, $this->classNew->availableGenerators()); - } - - private function generateSourceCode(string $src, string $variant, InputInterface $input, OutputInterface $output): SourceCode - { - try { - return $this->classNew->generate($src, $variant, $input->getOption('force')); - } catch (FileAlreadyExists $exception) { - $questionHelper = new QuestionHelper(); - $question = new ConfirmationQuestion('File already exists, overwrite? [y/n]', false); - - if (false === $questionHelper->ask($input, $output, $question)) { - throw $exception; - } - - return $this->classNew->generate($src, $variant, true); - } - } -} diff --git a/lib/Extension/CodeTransformExtra/Command/ClassTransformCommand.php b/lib/Extension/CodeTransformExtra/Command/ClassTransformCommand.php deleted file mode 100644 index fa49ff2c9a..0000000000 --- a/lib/Extension/CodeTransformExtra/Command/ClassTransformCommand.php +++ /dev/null @@ -1,93 +0,0 @@ -differ = new Differ(new UnifiedDiffOutputBuilder()); - } - - public function configure(): void - { - $this->setDescription('Apply a transformation to an existing class (path or FQN)'); - $this->addArgument('src', InputArgument::REQUIRED, 'Source path or FQN'); - $this->addOption('transform', 't', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Tranformations to apply', []); - $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not make any changes'); - $this->addOption('diff', null, InputOption::VALUE_NONE, 'Output diff'); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $pattern = $input->getArgument('src'); - $dryRun = $input->getOption('dry-run'); - $diff = $input->getOption('diff'); - /** @var array $transformations */ - $transformations = $input->getOption('transform'); - - $pattern = Phpactor::normalizePath($pattern); - - if (false === Glob::isDynamic($pattern) && false === file_exists($pattern)) { - throw new RuntimeException(sprintf( - 'File "%s" does not exist', - $pattern - )); - } - - $paths = array_filter(Glob::glob($pattern), function ($path) { - return is_file($path); - }); - - if (empty($paths)) { - $output->writeln(sprintf('No files found for pattern "%s"', $pattern)); - return 0; - } - - $affected = 0; - foreach ($paths as $path) { - $existing = SourceCode::fromStringAndPath(file_get_contents($path), $path); - $transformed = $this->transformer->transform($existing, $transformations); - - $changed = trim($existing->__toString()) != trim($transformed); - - if ($changed) { - $affected++; - } - - if ($changed && $diff) { - $output->writeln($this->differ->diff($existing, $transformed)); - } - - if ($dryRun === false && $changed) { - $output->writeln($path); - file_put_contents($path, $transformed); - } - } - - $output->writeln(sprintf( - '%s files affected%s', - $affected, - $dryRun ? ' (dry run)' : '' - )); - - return 0; - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php b/lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php deleted file mode 100644 index 981b96b5d7..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php +++ /dev/null @@ -1,51 +0,0 @@ -setRequired([ - self::PARAM_PATH, - self::PARAM_SOURCE, - self::PARAM_OFFSET - ]); - $resolver->setTypes([ - self::PARAM_OFFSET => 'integer', - ]); - } - - public function handle(array $arguments) - { - $source = $arguments[self::PARAM_SOURCE]; - $source = SourceCode::fromStringAndPath($source, $arguments[self::PARAM_PATH]); - $source = $this->changeVisiblity->changeVisiblity($source, $arguments[self::PARAM_OFFSET]); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $source->uri()->path(), - $arguments[self::PARAM_SOURCE], - (string) $source - ); - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php b/lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php deleted file mode 100644 index f8e998ef3c..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php +++ /dev/null @@ -1,68 +0,0 @@ -setDefaults([ - self::PARAM_CONSTANT_NAME => null, - self::PARAM_CONSTANT_NAME_SUGGESTION => null, - ]); - $resolver->setRequired([ - self::PARAM_PATH, - self::PARAM_OFFSET, - self::PARAM_SOURCE, - ]); - } - - public function handle(array $arguments) - { - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_CONSTANT_NAME, - self::INPUT_LABEL_NAME, - $arguments[self::PARAM_CONSTANT_NAME_SUGGESTION] ?: '' - )); - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $textEdits = $this->extractConstant->extractConstant( - SourceCode::fromStringAndPath($arguments[self::PARAM_SOURCE], $arguments[self::PARAM_PATH]), - $arguments[self::PARAM_OFFSET], - $arguments[self::PARAM_CONSTANT_NAME] - ); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $arguments[self::PARAM_PATH], - $arguments[self::PARAM_SOURCE], - $textEdits->textEdits()->apply($arguments[self::PARAM_SOURCE]) - ); - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php b/lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php deleted file mode 100644 index 80bd8967f9..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php +++ /dev/null @@ -1,74 +0,0 @@ -setDefaults([ - self::PARAM_VARIABLE_NAME => null, - self::PARAM_OFFSET_START => null, - ]); - $resolver->setRequired([ - self::PARAM_PATH, - self::PARAM_SOURCE, - self::PARAM_OFFSET_END, - ]); - } - - public function handle(array $arguments) - { - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_VARIABLE_NAME, - self::INPUT_LABEL_NAME, - '' - )); - - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_OFFSET_START, - 'Offset start: ' - )); - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $textEdits = $this->extractExpression->extractExpression( - SourceCode::fromString($arguments[self::PARAM_SOURCE]), - $arguments[self::PARAM_OFFSET_START], - $arguments[self::PARAM_OFFSET_END], - $arguments[self::PARAM_VARIABLE_NAME] - ); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $arguments[self::PARAM_PATH], - $arguments[self::PARAM_SOURCE], - $textEdits->apply($arguments[self::PARAM_SOURCE]) - ); - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php b/lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php deleted file mode 100644 index 62e8fe0483..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php +++ /dev/null @@ -1,80 +0,0 @@ -setDefaults([ - self::PARAM_METHOD_NAME => null, - self::PARAM_OFFSET_START => null, - self::PARAM_OFFSET_END => null, - ]); - $resolver->setRequired([ - self::PARAM_SOURCE, - self::PARAM_PATH, - ]); - } - - public function handle(array $arguments) - { - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_METHOD_NAME, - self::INPUT_LABEL_NAME, - '' - )); - - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_OFFSET_START, - 'Offset start: ' - )); - - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_OFFSET_END, - 'Offset end: ' - )); - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $sourceCode = SourceCode::fromStringAndPath($arguments[self::PARAM_SOURCE], $arguments[self::PARAM_PATH]); - $textDocumentEdits = $this->extractMethod->extractMethod( - $sourceCode, - $arguments[self::PARAM_OFFSET_START], - $arguments[self::PARAM_OFFSET_END], - $arguments[self::PARAM_METHOD_NAME] - ); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $arguments[self::PARAM_PATH], - $arguments[self::PARAM_SOURCE], - $textDocumentEdits->textEdits()->apply((string)$sourceCode) - ); - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php b/lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php deleted file mode 100644 index a4f68ce2cb..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php +++ /dev/null @@ -1,64 +0,0 @@ -setRequired([ - self::PARAM_PATH, - self::PARAM_SOURCE, - self::PARAM_OFFSET, - ]); - } - - public function handle(array $arguments) - { - $textDocumentEdits = $this->generateMethod->generateMember( - SourceCode::fromStringAndPath( - $arguments[self::PARAM_SOURCE], - $arguments[self::PARAM_PATH] - ), - $arguments[self::PARAM_OFFSET] - ); - - $originalSource = $this->determineOriginalSource($textDocumentEdits->uri(), $arguments); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $textDocumentEdits->uri()->path(), - $originalSource, - $textDocumentEdits->textEdits()->apply((string)$originalSource) - ); - } - - private function determineOriginalSource(TextDocumentUri $uri, array $arguments) - { - $originalSource = $uri->path() === $arguments[self::PARAM_PATH] ? - $arguments[self::PARAM_SOURCE] : - file_get_contents($uri->path()); - - return $originalSource; - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php b/lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php deleted file mode 100644 index 972f22b746..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php +++ /dev/null @@ -1,143 +0,0 @@ -setDefaults([ - self::PARAM_QUALIFIED_NAME => null, - self::PARAM_ALIAS => null, - ]); - $resolver->setRequired([ - self::PARAM_OFFSET, - self::PARAM_SOURCE, - self::PARAM_PATH, - ]); - } - - public function handle(array $arguments) - { - if (null === $arguments[self::PARAM_QUALIFIED_NAME]) { - $name = (new WordAtOffset(WordAtOffset::SPLIT_QUALIFIED_PHP_NAME))($arguments[self::PARAM_SOURCE], $arguments[self::PARAM_OFFSET]); - $suggestions = $this->suggestions($name); - - if (count($suggestions) === 0) { - return EchoResponse::fromMessage(sprintf( - 'No classes found with name "%s"', - $name - )); - } - - if (count($suggestions) > 1) { - $this->requireInput( - ListInput::fromNameLabelChoices( - self::PARAM_QUALIFIED_NAME, - 'Select class:', - array_combine($suggestions, $suggestions) - ) - ); - } else { - $arguments[self::PARAM_QUALIFIED_NAME] = reset($suggestions); - } - } - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - try { - $sourceCode = $this->nameImport->importName( - SourceCode::fromStringAndPath( - $arguments[self::PARAM_SOURCE], - $arguments[self::PARAM_PATH] - ), - ByteOffset::fromInt($arguments[self::PARAM_OFFSET]), - NameImport::forClass($arguments[self::PARAM_QUALIFIED_NAME], $arguments[self::PARAM_ALIAS]) - )->apply($arguments[self::PARAM_SOURCE]); - } catch (NameAlreadyUsedException $e) { - if ($e instanceof NameAlreadyImportedException && $e->existingName() === $arguments[self::PARAM_QUALIFIED_NAME]) { - return EchoResponse::fromMessage(sprintf( - 'Class "%s" is already imported', - $arguments[self::PARAM_QUALIFIED_NAME] - )); - } - - $arguments[self::PARAM_ALIAS] = null; - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_ALIAS, - sprintf( - '"%s" is already used, choose an alias: ', - $e->name() - ), - $e->name() - )); - - return $this->createInputCallback($arguments); - } catch (TransformException $e) { - return EchoResponse::fromMessage($e->getMessage()); - } - - return CollectionResponse::fromActions([ - UpdateFileSourceResponse::fromPathOldAndNewSource( - $arguments[self::PARAM_PATH], - $arguments[self::PARAM_SOURCE], - (string) $sourceCode - ), - EchoResponse::fromMessage(sprintf( - 'Imported class "%s"', - $arguments[self::PARAM_QUALIFIED_NAME] - )) - ]); - } - - private function suggestions(string $name) - { - $suggestions = $this->classSearch->classSearch( - $this->filesystem, - $name - ); - - return array_map(function (array $suggestion) { - return $suggestion['class']; - }, $suggestions); - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php b/lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php deleted file mode 100644 index 513924e42e..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php +++ /dev/null @@ -1,66 +0,0 @@ -setRequired([ - self::PARAM_PATH, - self::PARAM_SOURCE, - ]); - } - - public function handle(array $arguments) - { - $document = TextDocumentBuilder::create( - $arguments[self::PARAM_SOURCE] - )->language('php')->uri($arguments[self::PARAM_PATH])->build(); - - $diagnostics = wait($this->reflector->diagnostics($document))->byClass(UnresolvableNameDiagnostic::class); - - $responses = []; - foreach ($diagnostics as $unresolvedClass) { - assert($unresolvedClass instanceof UnresolvableNameDiagnostic); - - $responses[] = $this->handler->handle(Request::fromNameAndParameters(ImportClassHandler::NAME, [ - ImportClassHandler::PARAM_PATH => $arguments[self::PARAM_PATH], - ImportClassHandler::PARAM_SOURCE => $arguments[self::PARAM_SOURCE], - ImportClassHandler::PARAM_OFFSET => $unresolvedClass->range()->start()->toInt() + 1 - ])); - } - - if ($responses === []) { - return EchoResponse::fromMessage('No unresolved classes found'); - } - - return CollectionResponse::fromActions($responses); - } - - public function name(): string - { - return self::NAME; - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php b/lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php deleted file mode 100644 index 70bd93374b..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php +++ /dev/null @@ -1,127 +0,0 @@ -setDefaults([ - self::PARAM_METHOD_NAME => null, - self::PARAM_CLASS_NAME => null, - ]); - $resolver->setRequired([ - self::PARAM_PATH, - self::PARAM_SOURCE, - ]); - } - - public function handle(array $arguments) - { - $class = $this->class($arguments[self::PARAM_SOURCE], $arguments[self::PARAM_CLASS_NAME]); - $parentClass = $this->parentClass($class); - - $this->requireInput(ListInput::fromNameLabelChoices( - self::PARAM_METHOD_NAME, - sprintf('Methods from "%s"', $parentClass->name()), - $this->methodChoices($parentClass) - )->withMultiple(true)); - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $newCode = $arguments[self::PARAM_SOURCE]; - foreach ((array) $arguments[self::PARAM_METHOD_NAME] as $methodName) { - $newCode = $this->overrideMethod->overrideMethod( - SourceCode::fromString((string) $newCode), - (string) $class->name(), - $methodName - )->apply($newCode); - } - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $arguments[self::PARAM_PATH], - $arguments[self::PARAM_SOURCE], - (string) $newCode - ); - } - - private function class($source, $className = null) - { - $classes = $this->reflector->reflectClassesIn(TextDocumentBuilder::fromUnknown($source)); - - if ($classes->count() === 0) { - throw new InvalidArgumentException( - 'No classes in source file' - ); - } - - if (null === $className && $classes->count() > 1) { - throw new InvalidArgumentException( - 'Currently will only override methods in files with one class' - ); - } - - return $className ? $classes->get($className) : $classes->first(); - } - - private function parentClass(ReflectionClass $class) - { - /** @var ReflectionClass $parentClass */ - $parentClass = $class->parent(); - - if (null === $parentClass) { - throw new InvalidArgumentException(sprintf( - 'Class "%s" has no parent', - $class->name() - )); - } - - return $parentClass; - } - - private function methodChoices(ReflectionClass $parentClass) - { - // TODO filter methods already implemented in the current class - $methodNames = array_map(function (ReflectionMethod $method) { - return $method->name(); - }, iterator_to_array( - $parentClass->methods()->byVisibilities([ Visibility::public(), Visibility::protected() ]) - )); - - sort($methodNames); - - return array_combine($methodNames, $methodNames); - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php b/lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php deleted file mode 100644 index 5f95be121f..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php +++ /dev/null @@ -1,149 +0,0 @@ -name; - } - - public function configure(Resolver $resolver): void - { - $resolver->setDefaults([ - self::PARAM_NAMES => null, - ]); - $resolver->setRequired([ - self::PARAM_PATH, - self::PARAM_SOURCE, - self::PARAM_OFFSET, - ]); - } - - public function handle(array $arguments) - { - if ($context = $this->getPropertyContext($arguments)) { - return $this->handleSingle($context, $arguments); - } - - return $this->handleClass($arguments); - } - - private function getPropertyContext(array $arguments): ?NodeContext - { - $offset = $this->reflector->reflectOffset(TextDocumentBuilder::fromUnknown($arguments[self::PARAM_SOURCE]), $arguments[self::PARAM_OFFSET]); - - if ($offset->nodeContext()->symbol()->symbolType() === Symbol::PROPERTY) { - return $offset->nodeContext(); - } - - return null; - } - - private function handleClass(array $arguments): Response - { - $class = $this->class($arguments[self::PARAM_SOURCE]); - - $this->requireInput(ListInput::fromNameLabelChoices( - self::PARAM_NAMES, - sprintf('Properties from "%s"', $class->name()), - $this->propertiesChoices($class) - )->withMultiple(true)); - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $originalSource = $arguments[self::PARAM_SOURCE]; - $newSource = SourceCode::fromStringAndPath($originalSource, $arguments[self::PARAM_PATH]); - - $edits = $this->propertyAccessGenerator->generate( - $newSource, - (array)$arguments[self::PARAM_NAMES], - $arguments[self::PARAM_OFFSET] - ); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $arguments[self::PARAM_PATH], - $originalSource, - $edits->apply($originalSource) - ); - } - - private function class(string $source): ReflectionClass - { - $classes = $this->reflector->reflectClassesIn(TextDocumentBuilder::fromUnknown($source))->classes(); - - if ($classes->count() === 0) { - throw new InvalidArgumentException( - 'No classes in source file' - ); - } - - if ($classes->count() > 1) { - throw new InvalidArgumentException( - 'Currently will only generates accessor/mutators by name in files with one class' - ); - } - - return $classes->first(); - } - - private function propertiesChoices(ReflectionClass $class): array - { - // Select only those from the current class because the accessor/mutator generator - // is not able to work with the parent class at the time - $properties = $class->properties()->belongingTo($class->name()); - - $propertiesNames = array_map(function (ReflectionProperty $property) { - return $property->name(); - }, iterator_to_array($properties)); - - natsort($propertiesNames); - - return array_combine($propertiesNames, $propertiesNames); - } - - private function handleSingle(NodeContext $context, array $arguments) - { - $newSource = $this->propertyAccessGenerator->generate( - SourceCode::fromStringAndPath($arguments[self::PARAM_SOURCE], $arguments[self::PARAM_PATH]), - [$context->symbol()->name()], - $arguments[self::PARAM_OFFSET] - )->apply($arguments[self::PARAM_SOURCE]); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $arguments[self::PARAM_PATH], - $arguments[self::PARAM_SOURCE], - (string) $newSource - ); - } -} diff --git a/lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php b/lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php deleted file mode 100644 index fe917c3609..0000000000 --- a/lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php +++ /dev/null @@ -1,84 +0,0 @@ -setDefaults([ - self::PARAM_NAME => null, - self::PARAM_NAME_SUGGESTION => null, - self::PARAM_SCOPE => null, - ]); - $resolver->setRequired([ - self::PARAM_PATH, - self::PARAM_SOURCE, - self::PARAM_OFFSET, - ]); - } - - public function handle(array $arguments) - { - $this->requireInput(TextInput::fromNameLabelAndDefault( - self::PARAM_NAME, - self::INPUT_LABEL, - $arguments[self::PARAM_NAME_SUGGESTION] ?: '' - )); - - $this->requireInput(ChoiceInput::fromNameLabelChoices( - self::PARAM_SCOPE, - 'Scope: ', - [ - RenameVariable::SCOPE_FILE => RenameVariable::SCOPE_FILE, - RenameVariable::SCOPE_LOCAL => RenameVariable::SCOPE_LOCAL, - ] - )); - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $sourceCode = $this->renameVariable->renameVariable( - SourceCode::fromStringAndPath( - $arguments[self::PARAM_SOURCE], - $arguments[self::PARAM_PATH] - ), - $arguments[self::PARAM_OFFSET], - $arguments[self::PARAM_NAME], - $arguments[self::PARAM_SCOPE] - ); - - return UpdateFileSourceResponse::fromPathOldAndNewSource( - $sourceCode->uri()->path(), - $arguments[self::PARAM_SOURCE], - (string) $sourceCode - ); - } -} diff --git a/lib/Extension/Completion/CompletionExtension.php b/lib/Extension/Completion/CompletionExtension.php deleted file mode 100644 index 437fe19bab..0000000000 --- a/lib/Extension/Completion/CompletionExtension.php +++ /dev/null @@ -1,173 +0,0 @@ -setDefaults([ - self::PARAM_DEDUPE => true, - self::PARAM_DEDUPE_MATCH_FQN => true, - self::PARAM_LIMIT => null, - self::PARAM_LABEL_FORMATTER => LabelFormatter::HELPFUL, - ]); - $schema->setDescriptions([ - self::PARAM_DEDUPE => 'If results should be de-duplicated', - self::PARAM_DEDUPE_MATCH_FQN => 'If ``' . self::PARAM_DEDUPE . '``, consider the class FQN in addition to the completion suggestion', - self::PARAM_LIMIT => 'Sets a limit on the number of completion suggestions for any request', - self::PARAM_LABEL_FORMATTER => 'Definition of how to format entries in the completion list', - ]); - $schema->setEnums([ - self::PARAM_LABEL_FORMATTER => [ - LabelFormatter::HELPFUL, - LabelFormatter::FQN, - ] - ]); - } - - - public function load(ContainerBuilder $container): void - { - $this->registerCompletion($container); - $container->register(CompletorLogger::class, function (Container $container) { - return new CompletorLogger( - LoggingExtension::channelLogger($container, 'completion'), - ); - }); - } - - private function registerCompletion(ContainerBuilder $container): void - { - $container->register(self::SERVICE_REGISTRY, function (Container $container) { - $completors = []; - foreach ($container->getServiceIdsForTag(self::TAG_COMPLETOR) as $serviceId => $attrs) { - $types = $attrs[self::KEY_COMPLETOR_TYPES] ?? ['php']; - foreach ($types as $type) { - if (!isset($completors[$type])) { - $completors[$type] = []; - } - $completor = $container->get($serviceId); - if (null === $completor) { - continue; - } - $completors[$type][] = $completor; - } - } - - $mapped = []; - /** @var Completor[] $completors */ - foreach ($completors as $type => $completors) { - $completors = new ChainCompletor( - $completors, - $container->get(CompletorLogger::class), - ); - if ($container->parameter(self::PARAM_DEDUPE)->bool()) { - $completors = new DedupeCompletor( - $completors, - $container->parameter(self::PARAM_DEDUPE_MATCH_FQN)->bool() - ); - } - - $limit = $container->parameter(self::PARAM_LIMIT)->intOrNull(); - if (is_int($limit)) { - $completors = new LimitingCompletor($completors, $limit); - } - - $completors = new LabelFormattingCompletor($completors, $container->get(LabelFormatter::class)); - if ($container->has(SuggestionDocumentor::class)) { - $completors = new DocumentingCompletor($completors, $container->get(SuggestionDocumentor::class)); - } - - $mapped[(string)$type] = $completors; - } - - return new TypedCompletorRegistry($mapped); - }); - - $container->register(LabelFormatter::class, function (Container $container) { - return match ($formatter = $container->parameter(self::PARAM_LABEL_FORMATTER)->string()) { - LabelFormatter::HELPFUL => new HelpfulLabelFormatter(), - LabelFormatter::FQN => new PassthruLabelFormatter(), - default => throw new InvalidArgumentException('Unknown formatter type: ' . $formatter), - }; - }); - - $container->register(self::SERVICE_SHORT_DESC_FORMATTER, function (Container $container) { - $formatters = []; - foreach (array_keys($container->getServiceIdsForTag(self::TAG_SHORT_DESC_FORMATTER)) as $serviceId) { - $taggedFormatters = $container->get($serviceId); - $taggedFormatters = is_array($taggedFormatters) ? $taggedFormatters : [ $taggedFormatters ]; - - foreach ($taggedFormatters as $taggedFormatter) { - $formatters[] = $taggedFormatter; - } - } - - return new ObjectFormatter($formatters); - }); - - $container->register(self::SERVICE_SNIPPET_FORMATTER, function (Container $container) { - $formatters = []; - foreach (array_keys($container->getServiceIdsForTag(self::TAG_SNIPPET_FORMATTER)) as $serviceId) { - $taggedFormatters = $container->get($serviceId); - $taggedFormatters = is_array($taggedFormatters) ? $taggedFormatters : [ $taggedFormatters ]; - - foreach ($taggedFormatters as $taggedFormatter) { - $formatters[] = $taggedFormatter; - } - } - - return new ObjectFormatter($formatters); - }); - - $container->register(self::SERVICE_SIGNATURE_HELPER, function (Container $container) { - $helpers = []; - - foreach (array_keys($container->getServiceIdsForTag(self::TAG_SIGNATURE_HELPER)) as $serviceId) { - $helpers[] = $container->get($serviceId); - } - - return new ChainSignatureHelper( - LoggingExtension::channelLogger($container, self::LOGGER_CHANNEL), - $helpers - ); - }); - } -} diff --git a/lib/Extension/Completion/Tests/Unit/CompletionExtensionTest.php b/lib/Extension/Completion/Tests/Unit/CompletionExtensionTest.php deleted file mode 100644 index aeaa6e6ea9..0000000000 --- a/lib/Extension/Completion/Tests/Unit/CompletionExtensionTest.php +++ /dev/null @@ -1,141 +0,0 @@ -completor1 = $this->prophesize(Completor::class); - $this->signatureHelper1 = $this->prophesize(SignatureHelper::class); - $this->formatter1 = $this->prophesize(Formatter::class); - } - - public function testCreatesChainedCompletor(): void - { - $document = TextDocumentBuilder::create(self::EXAMPLE_SOURCE)->build(); - $this->completor1->complete( - $document, - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - )->will(function () { - return (function () { - yield Suggestion::create(self::EXAMPLE_SUGGESTION); - })(); - }); - - $completor = $this - ->createContainer() - ->expect(CompletionExtension::SERVICE_REGISTRY, TypedCompletorRegistry::class) - ->completorForType('php'); - $results = iterator_to_array($completor->complete( - $document, - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - )); - - $this->assertEquals(self::EXAMPLE_SUGGESTION, $results[0]->name()); - } - - public function testCreatesFormatterFromEitherSingleFormatterOrArray(): void - { - $object = new stdClass(); - $this->formatter1->canFormat($object)->shouldBeCalledTimes(3)->willReturn(false); - - $formatter = $this->createContainer()->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER); - $canFormat = $formatter->canFormat($object); - $this->assertEquals(false, $canFormat); - } - - public function testCreatesSignatureHelper(): void - { - $document = TextDocumentBuilder::create(self::EXAMPLE_SOURCE)->build(); - $this->signatureHelper1->signatureHelp( - $document, - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - )->will(function () { - return (function () { - return new SignatureHelp([], 0); - })(); - }); - - $signatureHelper = $this->createContainer()->get(CompletionExtension::SERVICE_SIGNATURE_HELPER); - $help = $signatureHelper->signatureHelp( - $document, - ByteOffset::fromInt(self::EXAMPLE_OFFSET) - ); - - $this->assertInstanceOf(SignatureHelp::class, $help); - } - - private function createContainer(): Container - { - $builder = new PhpactorContainer(); - $extension = new CompletionExtension(); - - $builder->register('completor1', function () { - return $this->completor1->reveal(); - }, [ CompletionExtension::TAG_COMPLETOR => []]); - - $builder->register('formatter', function () { - return $this->formatter1->reveal(); - }, [ CompletionExtension::TAG_SHORT_DESC_FORMATTER => []]); - - $builder->register('signarure_helper', function () { - return $this->signatureHelper1->reveal(); - }, [ CompletionExtension::TAG_SIGNATURE_HELPER => []]); - - $builder->register('short_desc_formatter_array', function () { - return [ - $this->formatter1->reveal(), - $this->formatter1->reveal(), - ]; - }, [ CompletionExtension::TAG_SHORT_DESC_FORMATTER => []]); - - $builder->register('snippet_formatter_array', function () { - return [ - $this->formatter1->reveal(), - $this->formatter1->reveal(), - ]; - }, [ CompletionExtension::TAG_SNIPPET_FORMATTER => []]); - - $extension->load($builder); - - $extension = new LoggingExtension(); - $extension->load($builder); - return $builder->build([ - 'logging.enabled' => false, - CompletionExtension::PARAM_DEDUPE => false, - CompletionExtension::PARAM_DEDUPE_MATCH_FQN => false, - CompletionExtension::PARAM_LIMIT => 10, - CompletionExtension::PARAM_LABEL_FORMATTER => LabelFormatter::HELPFUL, - ]); - } -} diff --git a/lib/Extension/CompletionExtra/Application/Complete.php b/lib/Extension/CompletionExtra/Application/Complete.php deleted file mode 100644 index 488a4f253a..0000000000 --- a/lib/Extension/CompletionExtra/Application/Complete.php +++ /dev/null @@ -1,41 +0,0 @@ ->, - * issues: array - * } - */ - public function complete(string $source, int $offset, string $type = 'php'): array - { - $completor = $this->registry->completorForType($type); - $suggestions = $completor->complete( - TextDocumentBuilder::create($source)->language($type)->build(), - ByteOffset::fromInt($offset) - ); - $suggestions = iterator_to_array($suggestions); - $suggestions = array_map(function (Suggestion $suggestion) { - return $suggestion->toArray(); - }, $suggestions); - - return [ - 'suggestions' => $suggestions, - - // deprecated - 'issues' => [], - ]; - } -} diff --git a/lib/Extension/CompletionExtra/Command/CompleteCommand.php b/lib/Extension/CompletionExtra/Command/CompleteCommand.php deleted file mode 100644 index c2881bac55..0000000000 --- a/lib/Extension/CompletionExtra/Command/CompleteCommand.php +++ /dev/null @@ -1,49 +0,0 @@ -helper = new FilesystemHelper(); - } - - public function configure(): void - { - $this->setDescription('Suggest completions DEPRECATED! Use RPC instead'); - $this->addArgument('path', InputArgument::REQUIRED, 'STDIN, source path or FQN'); - $this->addArgument('offset', InputArgument::REQUIRED, 'Offset to complete'); - $this->addOption('type', null, InputOption::VALUE_REQUIRED, 'Type of completion (e.g. php)', 'php'); - FormatHandler::configure($this); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $completions = $this->complete->complete( - $this->helper->contentsFromFileOrStdin($input->getArgument('path')), - $input->getArgument('offset'), - $input->getOption('type') - ); - - $format = $input->getOption('format'); - $this->dumperRegistry->get($format)->dump($output, $completions); - - return 0; - } -} diff --git a/lib/Extension/CompletionExtra/CompletionExtraExtension.php b/lib/Extension/CompletionExtra/CompletionExtraExtension.php deleted file mode 100644 index 8452979275..0000000000 --- a/lib/Extension/CompletionExtra/CompletionExtraExtension.php +++ /dev/null @@ -1,63 +0,0 @@ -registerCommands($container); - $this->registerApplicationServices($container); - $this->registerRpc($container); - } - - - public function configure(Resolver $schema): void - { - } - - private function registerRpc(ContainerBuilder $container): void - { - $container->register('class_mover.handler.hover', function (Container $container) { - return new HoverHandler( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER) - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => HoverHandler::NAME] ]); - } - - private function registerCommands(ContainerBuilder $container): void - { - $container->register('command.complete', function (Container $container) { - return new CompleteCommand( - $container->get('application.complete'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'complete' ]]); - } - - private function registerApplicationServices(ContainerBuilder $container): void - { - $container->register('application.complete', function (Container $container) { - return new Complete( - $container->expect(CompletionExtension::SERVICE_REGISTRY, TypedCompletorRegistry::class) - ); - }); - } -} diff --git a/lib/Extension/CompletionExtra/Rpc/HoverHandler.php b/lib/Extension/CompletionExtra/Rpc/HoverHandler.php deleted file mode 100644 index 941d177788..0000000000 --- a/lib/Extension/CompletionExtra/Rpc/HoverHandler.php +++ /dev/null @@ -1,138 +0,0 @@ -setRequired([ - self::PARAM_SOURCE, - self::PARAM_OFFSET, - ]); - } - - /** - * @param array $arguments - */ - public function handle(array $arguments): Response - { - $offset = $this->reflector->reflectOffset( - TextDocumentBuilder::create(Cast::toString($arguments[self::PARAM_SOURCE]))->build(), - Cast::toInt($arguments[self::PARAM_OFFSET]) - ); - - $type = $offset->nodeContext()->type(); - $nodeContext = $offset->nodeContext(); - - $info = $this->messageFromSymbolContext($nodeContext); - $info = $info ?: sprintf( - '%s %s', - $nodeContext->symbol()->symbolType(), - $nodeContext->symbol()->name() - ); - - return EchoResponse::fromMessage($info); - } - - private function renderSymbolContext(NodeContext $nodeContext): ?string - { - return match ($nodeContext->symbol()->symbolType()) { - Symbol::METHOD, Symbol::PROPERTY, Symbol::CONSTANT => $this->renderMember($nodeContext), - Symbol::CLASS_ => $this->renderClass($nodeContext->type()), - Symbol::FUNCTION => $this->renderFunction($nodeContext), - Symbol::VARIABLE => $this->renderVariable($nodeContext), - default => null, - }; - } - - private function renderMember(NodeContext $nodeContext): ?string - { - $name = $nodeContext->symbol()->name(); - $container = $nodeContext->containerType(); - - try { - $class = $this->reflector->reflectClassLike((string) $container); - $member = null; - - // note that all class-likes (classes, traits and interfaces) have - // methods but not all have constants or properties, so we play safe - // with members() which is first-come-first-serve, rather than risk - // a fatal error because of a non-existing method. - $member = match ($nodeContext->symbol()->symbolType()) { - Symbol::METHOD => $class->methods()->get($name), - Symbol::CONSTANT => $class->members()->get($name), - Symbol::PROPERTY => $class->members()->get($name), - default => throw new RuntimeException('Unknown member type'), - }; - - - return $this->formatter->format($member); - } catch (NotFound $e) { - return $e->getMessage(); - } - } - - private function renderFunction(NodeContext $nodeContext) - { - $name = $nodeContext->symbol()->name(); - $function = $this->reflector->reflectFunction($name); - - return $this->formatter->format($function); - } - - private function renderVariable(NodeContext $nodeContext) - { - return $this->formatter->format($nodeContext->type()); - } - - private function renderClass(Type $type) - { - try { - $class = $this->reflector->reflectClassLike((string) $type); - return $this->formatter->format($class); - } catch (NotFound $e) { - return $e->getMessage(); - } - } - - private function messageFromSymbolContext(NodeContext $nodeContext): ?string - { - try { - return $this->renderSymbolContext($nodeContext); - } catch (CouldNotFormat) { - } - - return null; - } -} diff --git a/lib/Extension/CompletionRpc/CompletionRpcExtension.php b/lib/Extension/CompletionRpc/CompletionRpcExtension.php deleted file mode 100644 index fef15ce013..0000000000 --- a/lib/Extension/CompletionRpc/CompletionRpcExtension.php +++ /dev/null @@ -1,30 +0,0 @@ -register('completion_rpc.handler', function (Container $container) { - return new CompleteHandler($container->expect( - CompletionExtension::SERVICE_REGISTRY, - TypedCompletorRegistry::class, - )); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => CompleteHandler::NAME] ]); - } -} diff --git a/lib/Extension/CompletionRpc/Handler/CompleteHandler.php b/lib/Extension/CompletionRpc/Handler/CompleteHandler.php deleted file mode 100644 index 37a8791945..0000000000 --- a/lib/Extension/CompletionRpc/Handler/CompleteHandler.php +++ /dev/null @@ -1,63 +0,0 @@ -setRequired([ - self::PARAM_SOURCE, - self::PARAM_OFFSET, - ]); - - $resolver->setDefaults([ - self::PARAM_TYPE => 'php' - ]); - } - - /** - * @param array $arguments - */ - public function handle(array $arguments): Response - { - $suggestions = $this->registry->completorForType($arguments['type'])->complete( - TextDocumentBuilder::create($arguments[self::PARAM_SOURCE]) - ->language($arguments['type']) - ->build(), - ByteOffset::fromInt($arguments[self::PARAM_OFFSET]) - ); - - $suggestions = array_map(function (Suggestion $suggestion) { - return $suggestion->toArray(); - }, iterator_to_array($suggestions)); - - return ReturnResponse::fromValue([ - 'suggestions' => $suggestions, - 'issues' => [], - ]); - } -} diff --git a/lib/Extension/CompletionRpc/Tests/Unit/CompletionRpcExtensionTest.php b/lib/Extension/CompletionRpc/Tests/Unit/CompletionRpcExtensionTest.php deleted file mode 100644 index a8daafd58b..0000000000 --- a/lib/Extension/CompletionRpc/Tests/Unit/CompletionRpcExtensionTest.php +++ /dev/null @@ -1,38 +0,0 @@ -createRequestHandler(); - $response = $handler->handle(Request::fromNameAndParameters('complete', [ - 'source' => '', - 'offset' => 1, - ])); - $this->assertInstanceOf(ReturnResponse::class, $response); - } - - private function createRequestHandler(): RequestHandler - { - $container = PhpactorContainer::fromExtensions([ - CompletionRpcExtension::class, - RpcExtension::class, - CompletionExtension::class, - LoggingExtension::class, - ]); - - return $container->get(RpcExtension::SERVICE_REQUEST_HANDLER); - } -} diff --git a/lib/Extension/CompletionRpc/Tests/Unit/Handler/CompleteHandlerTest.php b/lib/Extension/CompletionRpc/Tests/Unit/Handler/CompleteHandlerTest.php deleted file mode 100644 index 8a9abe042d..0000000000 --- a/lib/Extension/CompletionRpc/Tests/Unit/Handler/CompleteHandlerTest.php +++ /dev/null @@ -1,52 +0,0 @@ - $completor */ - private ObjectProphecy $completor; - - private TypedCompletorRegistry $registry; - - public function setUp(): void - { - $this->completor = $this->prophesize(Completor::class); - $this->registry = new TypedCompletorRegistry([ - 'php' => $this->completor->reveal(), - ]); - } - - public function testHandler(): void - { - $handler = new CompleteHandler($this->registry); - $this->completor->complete( - TextDocumentBuilder::create('aaa')->language('php')->build(), - ByteOffset::fromInt(1234) - )->will(function () { - yield Suggestion::create('aaa'); - yield Suggestion::create('bbb'); - }); - $action = (new HandlerTester($handler))->handle('complete', [ - 'source' => 'aaa', - 'offset' => 1234 - ]); - - $this->assertInstanceOf(ReturnResponse::class, $action); - $this->assertCount(2, $action->value()['suggestions']); - } -} diff --git a/lib/Extension/CompletionWorse/CompletionWorseExtension.php b/lib/Extension/CompletionWorse/CompletionWorseExtension.php deleted file mode 100644 index f6b5fa990e..0000000000 --- a/lib/Extension/CompletionWorse/CompletionWorseExtension.php +++ /dev/null @@ -1,487 +0,0 @@ -registerCompletion($container); - $this->registerSignatureHelper($container); - $container->register(NodeAtCursorProvider::class, function (Container $container) { - return new NodeAtCursorProvider($container->get(AstProvider::class)); - }); - } - - - public function configure(Resolver $schema): void - { - $completors = array_merge($this->getOtherCompletors(), $this->getTolerantCompletors()); - $defaults = array_combine(array_map( - fn (string $key) => $this->completorEnabledKey($key), - array_keys($completors) - ), array_map( - fn (string $key) => true, - array_keys($completors) - )); - - $defaults['completion_worse.completor.constant.enabled'] = false; - - $schema->setDefaults(array_merge($defaults, [ - self::PARAM_CLASS_COMPLETOR_LIMIT => 100, - self::PARAM_NAME_COMPLETION_PRIORITY => self::NAME_SEARCH_STRATEGY_PROXIMITY, - self::PARAM_SNIPPETS => true, - self::PARAM_EXPERIMENTAL => false, - self::PARAM_DEBUG => false, - ])); - - $descriptions = array_combine(array_map( - fn (string $key) => sprintf('completion_worse.completor.%s.enabled', $key), - array_keys($completors) - ), array_map( - fn (string $key, array $pair) => sprintf( - "Enable or disable the ``%s`` completor.\n\n%s.", - $key, - $pair[0] - ), - array_keys($completors), - $completors - )); - - $schema->setDescriptions(array_merge($descriptions, [ - self::PARAM_DEBUG => 'Include debug info in completion results', - self::PARAM_SNIPPETS => 'Enable or disable completion snippets', - self::PARAM_EXPERIMENTAL => 'Enable experimental functionality', - self::PARAM_CLASS_COMPLETOR_LIMIT => 'Suggestion limit for the filesystem based SCF class_completor', - self::PARAM_NAME_COMPLETION_PRIORITY => <<getTolerantCompletors() as $name => [$_, $completor]) { - $container->register(sprintf('worse_completion.completor.%s', $name), $completor, [ - self::TAG_TOLERANT_COMPLETOR => [ 'name' => $name ] - ]); - } - foreach ($this->getOtherCompletors() as $name => [$_, $completor]) { - $container->register(sprintf('worse_completion.completor.%s', $name), $completor, [ - CompletionExtension::TAG_COMPLETOR => [ 'name' => $name ] - ]); - } - - $container->register(SuggestionDocumentor::class, function (Container $container) { - return new WorseSuggestionDocumentor( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(ObjectRendererExtension::SERVICE_MARKDOWN_RENDERER) - ); - }); - - $container->register(TypeSuggestionProvider::class, function (Container $container) { - return new TypeSuggestionProvider( - $container->get(NameSearcher::class) - ); - }); - - $container->register(ChainTolerantCompletor::class, function (Container $container) { - return new ChainTolerantCompletor( - array_filter(array_map(function (string $serviceId) use ($container) { - if ($container->parameter(self::PARAM_DEBUG)->bool()) { - return new DebugTolerantCompletor($container->get($serviceId)); - } - return $container->get($serviceId) ?? false; - }, $container->get(self::SERVICE_COMPLETOR_MAP))), - $container->get(NodeAtCursorProvider::class), - $container->get(CompletorLogger::class), - ); - }, [ CompletionExtension::TAG_COMPLETOR => []]); - - $container->register(self::SERVICE_COMPLETOR_MAP, function (Container $container) { - $completors = []; - foreach ($container->getServiceIdsForTag(self::TAG_TOLERANT_COMPLETOR) as $serviceId => $attrs) { - if (!isset($attrs['name'])) { - throw new RuntimeException(sprintf( - 'Completor "%s" must declare an "name" attribute', - $serviceId - )); - } - - $name = $attrs['name']; - - if (isset($completors[$name])) { - throw new RuntimeException(sprintf( - 'Completor name "%s" (service ID "%s") already registered', - $name, - $serviceId - )); - } - - if (false === $container->getParameter($this->completorEnabledKey($name))) { - continue; - } - $completors[$name] = $serviceId; - } - - return $completors; - }); - - $container->register(DocumentPrioritizer::class, function (Container $container) { - $priority = $container->getParameter(self::PARAM_NAME_COMPLETION_PRIORITY); - return match ($priority) { - self::NAME_SEARCH_STRATEGY_PROXIMITY => new SimilarityResultPrioritizer(), - self::NAME_SEARCH_STRATEGY_NONE => new DefaultResultPrioritizer(), - default => throw new RuntimeException(sprintf( - 'Unknown search priority strategy "%s", must be one of "%s"', - $priority, - implode('", "', [ - self::NAME_SEARCH_STRATEGY_PROXIMITY, - self::NAME_SEARCH_STRATEGY_NONE - ]) - )), - }; - }); - - $container->register('completion_worse.short_desc.formatters', function (Container $container) { - return [ - new TypeFormatter(), - new MethodFormatter(), - new ParameterFormatter(), - new ParametersFormatter(), - new ClassFormatter(), - new PropertyFormatter(), - new FunctionFormatter(), - new VariableFormatter(), - new InterfaceFormatter(), - new TraitFormatter(), - new ConstantFormatter(), - new EnumCaseFormatter(), - ]; - }, [ CompletionExtension::TAG_SHORT_DESC_FORMATTER => []]); - - $container->register( - self::SERVICE_COMPLETION_WORSE_SNIPPET_FORMATTERS, - function (Container $container) { - $reflector = $container->get(WorseReflectionExtension::SERVICE_REFLECTOR); - - if (!$container->parameter(self::PARAM_SNIPPETS)->bool()) { - return []; - } - - $formatters = [ - new FunctionLikeSnippetFormatter(), - new ParametersSnippetFormatter(), - ]; - - if ($container->parameter(self::PARAM_EXPERIMENTAL)->bool()) { - $formatters = array_merge($formatters, [ - new NameSearchResultFunctionSnippetFormatter($reflector), - new NameSearchResultClassSnippetFormatter($reflector), - ]); - } - - return $formatters; - }, - [ CompletionExtension::TAG_SNIPPET_FORMATTER => []] - ); - } - /** - * @return array - */ - private function getOtherCompletors(): array - { - return [ - 'doctrine_annotation' => [ - 'Completion for annotations provided by the Doctrine annotation library', - function (Container $container) { - return new DoctrineAnnotationCompletor( - $container->get(NameSearcher::class), - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class) - ); - }, - ], - ]; - } - - /** - * @return array - */ - private function getTolerantCompletors(): array - { - return [ - 'imported_names' => [ - 'Completion for names imported into the current namespace', - function (Container $container) { - return $this->contextCompletor($container, new ImportedNameCompletor( - )); - }, - ], - 'worse_parameter' => [ - 'Completion for method or function parameters', - function (Container $container) { - return new WorseParameterCompletor( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER) - ); - }, - ], - 'named_parameter' => [ - 'Completion for named parameters', - function (Container $container) { - return new WorseNamedParameterCompletor( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER) - ); - }, - ], - 'constructor' => [ - 'Completion for constructors', - function (Container $container) { - return new WorseConstructorCompletor( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER) - ); - }, - ], - 'class_member' => [ - 'Completion for class members', - function (Container $container) { - return new WorseClassMemberCompletor( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER), - $container->get(CompletionExtension::SERVICE_SNIPPET_FORMATTER), - $container->get(ObjectRendererExtension::SERVICE_MARKDOWN_RENDERER) - ); - }, - ], - 'scf_class' => [ - 'Brute force completion for class names (not recommended)', - function (Container $container) { - return $this->limitCompletor($container, new ScfClassCompletor( - $container->get(SourceCodeFilesystemExtension::SERVICE_REGISTRY)->get('composer'), - $container->get('class_to_file.file_to_class') - )); - }, - ], - 'local_variable' => [ - 'Completion for local variables', - function (Container $container) { - return new WorseLocalVariableCompletor( - new VariableCompletionHelper( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - ), - $container->expect(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER, ObjectFormatter::class) - ); - }, - ], - 'subscript' => [ - 'Completion for subscript (array access from array shapes)', - function (Container $container) { - return new WorseSubscriptCompletor( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, SourceCodeReflector::class), - ); - }, - ], - 'declared_function' => [ - 'Completion for functions defined in the Phpactor runtime', - function (Container $container) { - return new WorseFunctionCompletor( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER), - $container->get(CompletionExtension::SERVICE_SNIPPET_FORMATTER) - ); - }, - ], - 'declared_constant' => [ - 'Completion for constants defined in the Phpactor runtime', - function (Container $container) { - return new WorseConstantCompletor(); - }, - ], - 'declared_class' => [ - 'Completion for classes defined in the Phpactor runtime', - function (Container $container) { - return new WorseDeclaredClassCompletor( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER) - ); - }, - ], - 'expression_name_search' => [ - 'Completion for class names, constants and functions at expression positions that are located in the index', - function (Container $container) { - return $this->contextCompletor($container, $this->limitCompletor($container, new ExpressionNameCompletor( - $container->get(NameSearcher::class), - new ObjectFormatter( - $container->get(self::SERVICE_COMPLETION_WORSE_SNIPPET_FORMATTERS) - ), - $container->get(DocumentPrioritizer::class) - ))); - }, - ], - 'use' => [ - 'Completion for use imports', - function (Container $container) { - return $this->limitCompletor($container, new UseNameCompletor( - $container->get(NameSearcher::class), - $container->get(DocumentPrioritizer::class) - )); - }, - ], - 'attribute' => [ - 'Completion for attribute class names', - function (Container $container) { - return $this->limitCompletor($container, new AttributeCompletor( - $container->get(NameSearcher::class), - $container->get(DocumentPrioritizer::class) - )); - }, - ], - 'class_like' => [ - 'Completion for class like contexts', - function (Container $container) { - return $this->limitCompletor($container, new ClassLikeCompletor( - $container->get(NameSearcher::class), - $container->get(DocumentPrioritizer::class) - )); - }, - ], - 'type' => [ - 'Completion for scalar types', - function (Container $container) { - return $this->limitCompletor($container, new TypeCompletor( - $container->get(TypeSuggestionProvider::class) - )); - }, - ], - 'keyword' => [ - 'Completion for keywords (not very accurate)', - function (Container $container) { - return new KeywordCompletor(); - }, - ], - 'docblock' => [ - 'Docblock completion', - function (Container $container) { - return new DocblockCompletor( - $container->get(TypeSuggestionProvider::class), - $container->get(WorseReflectionExtension::SERVICE_AST_PROVIDER) - ); - }, - ], - ]; - } - - - private function registerSignatureHelper(ContainerBuilder $container): void - { - $container->register('completion_worse.signature_helper', function (Container $container) { - return new WorseSignatureHelper( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(CompletionExtension::SERVICE_SHORT_DESC_FORMATTER) - ); - }, [ CompletionExtension::TAG_SIGNATURE_HELPER => []]); - } - - private function completorEnabledKey(string $key): string - { - return sprintf('completion_worse.completor.%s.enabled', $key); - } - - private function limitCompletor(Container $container, TolerantCompletor $completor): TolerantCompletor - { - $limit = $container->parameter(self::PARAM_CLASS_COMPLETOR_LIMIT)->int(); - - return new LimitingCompletor($completor, $limit); - } - - private function contextCompletor(Container $container, TolerantCompletor $tolerantCompletor): TolerantCompletor - { - return new ContextSensitiveCompletor( - $tolerantCompletor, - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class) - ); - } -} diff --git a/lib/Extension/CompletionWorse/Tests/Unit/CompletionWorseExtensionTest.php b/lib/Extension/CompletionWorse/Tests/Unit/CompletionWorseExtensionTest.php deleted file mode 100644 index 81be890e5f..0000000000 --- a/lib/Extension/CompletionWorse/Tests/Unit/CompletionWorseExtensionTest.php +++ /dev/null @@ -1,88 +0,0 @@ -buildContainer(); - - $completor = $container - ->expect(CompletionExtension::SERVICE_REGISTRY, TypedCompletorRegistry::class) - ->completorForType('php'); - assert($completor instanceof Completor); - - $completor->complete( - TextDocumentBuilder::create('build(), - ByteOffset::fromInt(8) - ); - } - - public function testDisableCompletors(): void - { - $container = $this->buildContainer([ - 'completion_worse.completor.worse_parameter.enabled' => false, - ]); - $completors = $container->get('completion_worse.completor_map'); - - self::assertFalse(in_array('completion_worse.completor.constructor', $completors), 'Completor disabled'); - } - - public function testExceptionWhenSelectingUnknownSearchPriotityStrategy(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Unknown search priority strategy "asd"'); - $container = $this->buildContainer([ - CompletionWorseExtension::PARAM_NAME_COMPLETION_PRIORITY => 'asd', - ]); - $container->get(DocumentPrioritizer::class); - } - - /** - * @param array $config - */ - private function buildContainer(array $config = []): Container - { - return PhpactorContainer::fromExtensions( - [ - CompletionExtension::class, - FilePathResolverExtension::class, - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - LoggingExtension::class, - WorseReflectionExtension::class, - CompletionWorseExtension::class, - SourceCodeFilesystemExtension::class, - ReferenceFinderExtension::class, - ObjectRendererExtension::class, - PhpExtension::class, - ], - array_merge([ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__, - ObjectRendererExtension::PARAM_TEMPLATE_PATHS => [], - ], $config) - ); - } -} diff --git a/lib/Extension/ComposerAutoloader/ClassLoaderFactory.php b/lib/Extension/ComposerAutoloader/ClassLoaderFactory.php deleted file mode 100644 index f8053c93df..0000000000 --- a/lib/Extension/ComposerAutoloader/ClassLoaderFactory.php +++ /dev/null @@ -1,59 +0,0 @@ -resolveMap('autoload_namespaces.php') as $namespace => $path) { - $loader->set($namespace, $path); - } - - foreach ($this->resolveMap('autoload_psr4.php') as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } - - if ($classMap = $this->resolveMap('autoload_classmap.php')) { - $loader->addClassMap($classMap); - } - - return $loader; - } - - private function resolveMap(string $fileName): array - { - $path = $this->composerDir . '/' . $fileName; - - if (!file_exists($path)) { - $this->logger->warning(sprintf( - 'Composer file "%s" does not exist', - $path - )); - return []; - } - - $map = require $path; - - if (!is_array($map)) { - $this->logger->warning(sprintf( - 'Composer map for "%s" is not an array', - $path - )); - return []; - } - - return $map; - } -} diff --git a/lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php b/lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php deleted file mode 100644 index 4976e52762..0000000000 --- a/lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php +++ /dev/null @@ -1,123 +0,0 @@ -setDefaults([ - self::PARAM_COMPOSER_ENABLE => true, - self::PARAM_AUTOLOADER_PATH => '%project_root%/vendor/autoload.php', - self::PARAM_AUTOLOAD_DEREGISTER => true, - self::PARAM_CLASS_MAPS_ONLY => true - ]); - $resolver->setDescriptions([ - self::PARAM_COMPOSER_ENABLE => 'Include of the projects autoloader to facilitate class location. Note that when including an autoloader code _may_ be executed. This option may be disabled when using the indexer', - self::PARAM_CLASS_MAPS_ONLY => 'Register the composer class maps only, do not register the autoloader - RECOMMENDED', - self::PARAM_AUTOLOADER_PATH => 'Path to project\'s autoloader, can be an array', - self::PARAM_AUTOLOAD_DEREGISTER=> 'Immediately de-register the autoloader once it has been included (prevent conflicts with Phpactor\'s autoloader). Some platforms may require this to be disabled', - ]); - } - - - public function load(ContainerBuilder $container): void - { - $container->register(self::SERVICE_AUTOLOADERS, function (Container $container) { - if (!$container->getParameter(self::PARAM_COMPOSER_ENABLE)) { - return []; - } - - $autoloaderPaths = (array) $container->getParameter(self::PARAM_AUTOLOADER_PATH); - $autoloaderPaths = array_filter(array_map(function ($path) use ($container) { - $path = $container->get( - FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER - )->resolve($path); - if (false === file_exists($path)) { - $this->logAutoloaderNotFound($container, $path); - return false; - } - - return $path; - }, $autoloaderPaths)); - - if ($container->getParameter(self::PARAM_CLASS_MAPS_ONLY)) { - return $this->classMapsOnly(LoggingExtension::channelLogger($container, self::LOG_CHANNEL), $autoloaderPaths); - } - - $currentAutoloaders = spl_autoload_functions(); - $autoloaders = []; - - - foreach ($autoloaderPaths as $autoloaderPath) { - $autoloader = require $autoloaderPath; - - if (!$autoloader instanceof ClassLoader) { - throw new RuntimeException('Autoloader is not an instance of ClassLoader'); - } - - $autoloaders[] = $autoloader; - } - - if ($currentAutoloaders && $container->getParameter(self::PARAM_AUTOLOAD_DEREGISTER)) { - $this->deregisterAutoloader($currentAutoloaders); - } - - return $autoloaders; - }); - } - - private function logAutoloaderNotFound(Container $container, $autoloaderPath): void - { - LoggingExtension::channelLogger($container, self::LOG_CHANNEL)->warning( - sprintf( - 'Could not find autoloader "%s"', - $autoloaderPath - ) - ); - } - - private function deregisterAutoloader(array $currentAutoloaders): void - { - $autoloaders = spl_autoload_functions(); - - if (!$autoloaders) { - return; - } - - foreach ($autoloaders as $autoloadFunction) { - spl_autoload_unregister($autoloadFunction); - } - - foreach ($currentAutoloaders as $autoloader) { - spl_autoload_register($autoloader); - } - } - - private function classMapsOnly(LoggerInterface $logger, array $autoloaderPaths): array - { - return array_map(function (string $autoloadPath) use ($logger): ClassLoader { - $composerPath = dirname($autoloadPath) . '/composer'; - return (new PhpactorClassLoader($composerPath, $logger))->getLoader(); - }, $autoloaderPaths); - } -} diff --git a/lib/Extension/ComposerAutoloader/Tests/Unit/ClassLoaderFactoryTest.php b/lib/Extension/ComposerAutoloader/Tests/Unit/ClassLoaderFactoryTest.php deleted file mode 100644 index 9b1b770af4..0000000000 --- a/lib/Extension/ComposerAutoloader/Tests/Unit/ClassLoaderFactoryTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getLoader(); - $file = $loader->findFile(__CLASS__); - self::assertEquals(Path::canonicalize(__FILE__), Path::canonicalize((string) $file)); - } -} diff --git a/lib/Extension/ComposerAutoloader/Tests/Unit/ComposerAutoloaderExtensionTest.php b/lib/Extension/ComposerAutoloader/Tests/Unit/ComposerAutoloaderExtensionTest.php deleted file mode 100644 index 85e15f689b..0000000000 --- a/lib/Extension/ComposerAutoloader/Tests/Unit/ComposerAutoloaderExtensionTest.php +++ /dev/null @@ -1,79 +0,0 @@ -create([ - ])->get(ComposerAutoloaderExtension::SERVICE_AUTOLOADERS); - $this->assertCount(1, $autoloaders); - $autoloader = reset($autoloaders); - $this->assertInstanceOf(ClassLoader::class, $autoloader); - } - - public function testProvidesAutoloadersNoDeregister(): void - { - $autoloaders = $this->create([ - ComposerAutoloaderExtension::PARAM_AUTOLOAD_DEREGISTER => false, - ])->get(ComposerAutoloaderExtension::SERVICE_AUTOLOADERS); - $this->assertCount(1, $autoloaders); - $autoloader = reset($autoloaders); - $this->assertInstanceOf(ClassLoader::class, $autoloader); - } - - public function testWithCustomProjectRoot(): void - { - $autoloaders = $this->create([ - ])->get(ComposerAutoloaderExtension::SERVICE_AUTOLOADERS); - $this->assertCount(1, $autoloaders); - $autoloader = reset($autoloaders); - $this->assertInstanceOf(ClassLoader::class, $autoloader); - } - - public function testWarningForNonExistingLoader(): void - { - $autoloaders = $this->create([ - ComposerAutoloaderExtension::PARAM_AUTOLOADER_PATH => 'not-existing.php', - ])->get(ComposerAutoloaderExtension::SERVICE_AUTOLOADERS); - - $this->assertCount(0, $autoloaders); - } - - public function testWarningAutoloaderIsntAutoloader(): void - { - $autoloaders = $this->create([ - ComposerAutoloaderExtension::PARAM_AUTOLOADER_PATH => __DIR__ . '/not-an-autoloader.php', - ])->get(ComposerAutoloaderExtension::SERVICE_AUTOLOADERS); - $this->assertCount(1, $autoloaders); - } - - public function testMultipleAutoloaders(): void - { - $autoloaders = $this->create([ - ComposerAutoloaderExtension::PARAM_AUTOLOADER_PATH => [ - __DIR__ . '/../../../../../vendor/autoload.php', - __DIR__ . '/../../../../../vendor/autoload.php', - ], - ])->get(ComposerAutoloaderExtension::SERVICE_AUTOLOADERS); - $this->assertCount(2, $autoloaders); - } - - private function create(array $config): Container - { - return PhpactorContainer::fromExtensions([ - ComposerAutoloaderExtension::class, - LoggingExtension::class, - FilePathResolverExtension::class - ], $config); - } -} diff --git a/lib/Extension/ComposerAutoloader/Tests/Unit/not-an-autoloader.php b/lib/Extension/ComposerAutoloader/Tests/Unit/not-an-autoloader.php deleted file mode 100644 index fe065bdacc..0000000000 --- a/lib/Extension/ComposerAutoloader/Tests/Unit/not-an-autoloader.php +++ /dev/null @@ -1,3 +0,0 @@ -register(ComposerInspector::class, function (Container $container) { - $pathResolver = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - return new ComposerInspector( - $pathResolver->resolve('%project_root%/composer.lock'), - $pathResolver->resolve('%project_root%/composer.json'), - ); - }); - - $container->register('composer.bin_path_expander', function (Container $container) { - return new CallbackExpander('composer_bin_dir', fn () => $container->get(ComposerInspector::class)->binDir()); - }); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/Configuration/ChangeSuggestor/PhpactorComposerSuggestor.php b/lib/Extension/Configuration/ChangeSuggestor/PhpactorComposerSuggestor.php deleted file mode 100644 index 61be2b95c1..0000000000 --- a/lib/Extension/Configuration/ChangeSuggestor/PhpactorComposerSuggestor.php +++ /dev/null @@ -1,27 +0,0 @@ -suggestor)($this->phpactorConfig, $this->composerInspector); - } -} diff --git a/lib/Extension/Configuration/Command/ConfigSuggestCommand.php b/lib/Extension/Configuration/Command/ConfigSuggestCommand.php deleted file mode 100644 index cce24d400b..0000000000 --- a/lib/Extension/Configuration/Command/ConfigSuggestCommand.php +++ /dev/null @@ -1,45 +0,0 @@ -setDescription('Suggest configuration changes based on current project'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - assert($output instanceof ConsoleOutput); - $question = new QuestionHelper(); - $nbChanges = 0; - foreach ($this->configurator->suggestChanges() as $change) { - $enable = $question->ask($input, $output, new ConfirmationQuestion($change->prompt())); - try { - $this->configurator->apply($change, is_bool($enable) ? $enable : false); - $nbChanges++; - } catch (Exception $e) { - $output->writeln(sprintf('Could not apply change: : %s', $e->getMessage())); - } - } - - $output->getErrorOutput()->writeln(sprintf('%d changes applied', $nbChanges)); - - return 0; - } -} diff --git a/lib/Extension/Configuration/ConfigurationExtension.php b/lib/Extension/Configuration/ConfigurationExtension.php deleted file mode 100644 index a7cba93fb6..0000000000 --- a/lib/Extension/Configuration/ConfigurationExtension.php +++ /dev/null @@ -1,103 +0,0 @@ -registerCommands($container); - $this->registerMisc($container); - - $container->register(PhpactorConfigChangeApplicator::class, function (Container $container) { - return new PhpactorConfigChangeApplicator($container->get(ConfigManipulator::class)); - }, [ - self::TAG_APPLICATOR => [], - ]); - - $container->register(Configurator::class, function (Container $container) { - $suggestors = $applicators = []; - foreach ($container->getServiceIdsForTag(self::TAG_SUGGESTOR) as $id => $attrs) { - $suggestors[] = $container->expect($id, ChangeSuggestor::class); - } - foreach ($container->getServiceIdsForTag(self::TAG_APPLICATOR) as $id => $attrs) { - $applicators[] = $container->expect($id, ChangeApplicator::class); - } - - return new Configurator($suggestors, $applicators); - }); - - $container->register(self::SERVICE_PHPACTOR_CONFIG_LOCAL, function (Container $container) { - $path = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class)->resolve('%project_root%/.phpactor.json'); - return JsonConfig::fromPath($path); - }); - } - - public function configure(Resolver $schema): void - { - } - - private function registerCommands(ContainerBuilder $container): void - { - $container->register(ConfigInitCommand::class, function (Container $container) { - return new ConfigInitCommand($container->get(ConfigManipulator::class)); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'config:initialize']]); - - $container->register(ConfigJsonSchemaCommand::class, function (Container $container) { - return new ConfigJsonSchemaCommand( - $container->get(JsonSchemaBuilder::class) - ); - }, [ - ConsoleExtension::TAG_COMMAND => [ - 'name' => 'config:json-schema' - ] - ]); - $container->register(ConfigSuggestCommand::class, function (Container $container) { - return new ConfigSuggestCommand( - $container->get(Configurator::class) - ); - }, [ - ConsoleExtension::TAG_COMMAND => [ - 'name' => 'config:auto' - ] - ]); - - $container->register(ConfigSetCommand::class, function (Container $container) { - return new ConfigSetCommand($container->get(ConfigManipulator::class)); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'config:set']]); - } - - private function registerMisc(ContainerBuilder $container): void - { - $container->register(ConfigManipulator::class, function (Container $container) { - return new ConfigManipulator( - realpath(__DIR__ . '/../../..') . '/phpactor.schema.json', - $container->parameter(FilePathResolverExtension::PARAM_PROJECT_ROOT)->string() . '/.phpactor.json' - ); - }); - } -} diff --git a/lib/Extension/Configuration/Model/JsonSchemaBuilder.php b/lib/Extension/Configuration/Model/JsonSchemaBuilder.php deleted file mode 100644 index dddefc7fc4..0000000000 --- a/lib/Extension/Configuration/Model/JsonSchemaBuilder.php +++ /dev/null @@ -1,97 +0,0 @@ - 'https://json-schema.org/draft-07/schema', - 'title' => $this->title, - 'type' => 'object', - 'properties' => [ - '$schema' => [ - 'description' => 'JSON schema location', - 'type' => [ - 'string', - 'null' - ], - ] - ] - ]; - - foreach ($this->extensions as $extensionClass) { - $optionsResolver = new Resolver(); - $extension = new $extensionClass(); - assert($extension instanceof Extension); - $extension->configure($optionsResolver); - - foreach ($optionsResolver->definitions() as $definition) { - assert($definition instanceof Definition); - $meta = [ - 'description' => $definition->description(), - ]; - if ($definition->types()) { - $meta['type'] = $this->mapTypes($definition->types()); - } - if (null !== $definition->defaultValue()) { - $meta['default'] = $definition->defaultValue(); - } - if ([] !== $definition->enum()) { - $meta['enum'] = $definition->enum(); - } - - $schema['properties'][$definition->name()] = $meta; - } - } - - return (string)json_encode($schema, JSON_PRETTY_PRINT); - } - - /** - * @param string[] $types - * - * @return string[] - */ - private function mapTypes(array $types): array - { - return array_map(function (string $type) { - if ($type === 'array') { - return 'object'; - } - - if ($type === 'bool') { - return 'boolean'; - } - - if ($type === 'int') { - return 'integer'; - } - - if ($type === 'float') { - return 'number'; - } - - if (str_ends_with($type, '[]')) { - return 'array'; - } - - return $type; - }, $types); - } -} diff --git a/lib/Extension/Configuration/Tests/Unit/Model/JsonSchemaBuilderTest.php b/lib/Extension/Configuration/Tests/Unit/Model/JsonSchemaBuilderTest.php deleted file mode 100644 index 866c18030d..0000000000 --- a/lib/Extension/Configuration/Tests/Unit/Model/JsonSchemaBuilderTest.php +++ /dev/null @@ -1,95 +0,0 @@ -createExtension1()); - - $schema = (new JsonSchemaBuilder('test', $extensions))->dump(); - self::assertEquals(<<<'EOT' - { - "$schema": "https:\/\/json-schema.org\/draft-07\/schema", - "title": "test", - "type": "object", - "properties": { - "$schema": { - "description": "JSON schema location", - "type": [ - "string", - "null" - ] - }, - "bar.foo": { - "description": "This does something", - "type": [ - "string" - ], - "default": 1234, - "enum": [ - "one", - "two" - ] - }, - "foo.bar": { - "description": null, - "type": [ - "string" - ], - "default": "bar" - }, - "bloob": { - "description": "Testing boolean defaults", - "type": [ - "boolean" - ], - "default": false - } - } - } - EOT - , $schema); - } - - private function createExtension1(): Extension - { - return new class() implements Extension { - public function configure(Resolver $resolver): void - { - $resolver->setDefaults([ - 'bar.foo' => 1234, - 'foo.bar' => 'bar', - 'bloob' => false - ]); - $resolver->setRequired([ - 'bar.foo', - ]); - $resolver->setTypes([ - 'bar.foo' => 'string', - 'foo.bar' => 'string', - 'bloob' => 'bool' - ]); - $resolver->setDescriptions([ - 'bar.foo' => 'This does something', - 'bloob' => 'Testing boolean defaults' - ]); - $resolver->setEnums([ - 'bar.foo' => ['one', 'two'], - ]); - } - - public function load(ContainerBuilder $builder): void - { - } - }; - } -} diff --git a/lib/Extension/Console/ConsoleExtension.php b/lib/Extension/Console/ConsoleExtension.php deleted file mode 100644 index da01a2fec7..0000000000 --- a/lib/Extension/Console/ConsoleExtension.php +++ /dev/null @@ -1,80 +0,0 @@ -register(self::SERVICE_COMMAND_LOADER, function (Container $container) { - $map = []; - foreach ($container->getServiceIdsForTag(self::TAG_COMMAND) as $commandId => $attrs) { - if (!isset($attrs['name'])) { - throw new InvalidArgumentException(sprintf( - 'Command with service ID "%s" must have the "name" attribute', - $commandId - )); - } - - $map[$attrs['name']] = $commandId; - } - - return new PhpactorCommandLoader($container, $map); - }); - - $container->register(self::SERVICE_OUTPUT, function (Container $container) { - return new ConsoleOutput( - $container->getParameter(self::PARAM_VERBOSITY), - $container->getParameter(self::PARAM_DECORATED) - ); - }); - - $container->register(self::SERVICE_INPUT, function (Container $container) { - return new ArgvInput(); - }); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_VERBOSITY => OutputInterface::VERBOSITY_NORMAL, - self::PARAM_DECORATED => null, - ]); - $schema->setDescriptions([ - self::PARAM_VERBOSITY => 'Verbosity level', - self::PARAM_DECORATED => 'Whether to decorate messages (null for auto-guessing)', - ]); - $schema->setEnums([ - self::PARAM_VERBOSITY => [ - OutputInterface::VERBOSITY_QUIET, - OutputInterface::VERBOSITY_NORMAL, - OutputInterface::VERBOSITY_VERBOSE, - OutputInterface::VERBOSITY_VERY_VERBOSE, - OutputInterface::VERBOSITY_DEBUG, - ], - self::PARAM_DECORATED => [ - true, - false, - null - ] - ]); - } -} diff --git a/lib/Extension/Console/PhpactorCommandLoader.php b/lib/Extension/Console/PhpactorCommandLoader.php deleted file mode 100644 index b35e0b89be..0000000000 --- a/lib/Extension/Console/PhpactorCommandLoader.php +++ /dev/null @@ -1,17 +0,0 @@ -setName($name); - - return $command; - } -} diff --git a/lib/Extension/Console/Tests/Unit/ConsoleExtensionTest.php b/lib/Extension/Console/Tests/Unit/ConsoleExtensionTest.php deleted file mode 100644 index 3eff6d9604..0000000000 --- a/lib/Extension/Console/Tests/Unit/ConsoleExtensionTest.php +++ /dev/null @@ -1,58 +0,0 @@ -createContainer(); - - $loader = $container->get(ConsoleExtension::SERVICE_COMMAND_LOADER); - $command = $loader->get('test'); - - $this->assertInstanceOf(Command::class, $command); - } - - public function testCreatesInputAndOutput(): void - { - $input = $this->createContainer()->get(ConsoleExtension::SERVICE_INPUT); - $output = $this->createContainer()->get(ConsoleExtension::SERVICE_OUTPUT); - - $this->assertInstanceOf(ArgvInput::class, $input); - $this->assertInstanceOf(ConsoleOutput::class, $output); - } - - public function testThrowsExceptionIfNoNameAttributeProvided(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('must have the "name" attribute'); - $container = PhpactorContainer::fromExtensions([ - ConsoleExtension::class, - InvalidExtension::class - ]); - - $loader = $container->get(ConsoleExtension::SERVICE_COMMAND_LOADER); - } - - private function createContainer(): Container - { - $container = PhpactorContainer::fromExtensions([ - ConsoleExtension::class, - TestExtension::class - ]); - - return $container; - } -} diff --git a/lib/Extension/Console/Tests/Unit/Example/InvalidExtension.php b/lib/Extension/Console/Tests/Unit/Example/InvalidExtension.php deleted file mode 100644 index 2234b6a383..0000000000 --- a/lib/Extension/Console/Tests/Unit/Example/InvalidExtension.php +++ /dev/null @@ -1,23 +0,0 @@ -register('test.command.test', function () { - return new TestCommand(); - }, [ ConsoleExtension::TAG_COMMAND => [] ]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/Console/Tests/Unit/Example/TestCommand.php b/lib/Extension/Console/Tests/Unit/Example/TestCommand.php deleted file mode 100644 index 28b0f97438..0000000000 --- a/lib/Extension/Console/Tests/Unit/Example/TestCommand.php +++ /dev/null @@ -1,9 +0,0 @@ -register('test.command.test', function () { - return new TestCommand(); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'test' ] ]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/ContextMenu/ContextMenuExtension.php b/lib/Extension/ContextMenu/ContextMenuExtension.php deleted file mode 100644 index 6867657d19..0000000000 --- a/lib/Extension/ContextMenu/ContextMenuExtension.php +++ /dev/null @@ -1,37 +0,0 @@ -register('rpc.handler.context_menu', function (Container $container) { - return new ContextMenuHandler( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(InterestingOffsetFinder::class), - $container->get('application.helper.class_file_normalizer'), - ContextMenu::fromArray(json_decode(file_get_contents(__DIR__ . '/menu.json'), true)), - $container - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ContextMenuHandler::NAME] ]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/ContextMenu/Handler/ContextMenuHandler.php b/lib/Extension/ContextMenu/Handler/ContextMenuHandler.php deleted file mode 100644 index 62bcfb666f..0000000000 --- a/lib/Extension/ContextMenu/Handler/ContextMenuHandler.php +++ /dev/null @@ -1,172 +0,0 @@ -setRequired([ - self::PARAMETER_SOURCE, - self::PARAMETER_OFFSET, - ]); - - $resolver->setDefaults([ - self::PARAMETER_ACTION => null, - self::PARAMETER_CURRENT_PATH => null, - ]); - } - - public function handle(array $arguments) - { - $offset = $this->offsetFromSourceAndOffset( - $arguments[self::PARAMETER_SOURCE], - $arguments[self::PARAMETER_OFFSET], - $arguments[self::PARAMETER_CURRENT_PATH] - ); - $symbol = $offset->nodeContext()->symbol(); - - return $this->resolveAction($offset, $symbol, $arguments); - } - - private function resolveAction(ReflectionOffset $offset, Symbol $symbol, array $arguments) - { - if (false === $this->menu->hasContext($symbol->symbolType())) { - return EchoResponse::fromMessage(sprintf( - 'No context actions available for symbol type "%s"', - $symbol->symbolType() - )); - } - - $symbolMenu = $this->menu->forContext($symbol->symbolType()); - - if (null !== $arguments[self::PARAMETER_ACTION]) { - return $this->delegateAction($symbolMenu, $arguments, $offset); - } - - return $this->actionSelectionAction($symbol, $symbolMenu, $arguments); - } - - private function delegateAction(array $symbolMenu, array $arguments, ReflectionOffset $offset): Response - { - $action = $symbolMenu[$arguments[self::PARAMETER_ACTION]]; - - // to avoid a cyclic dependency we get the request handler from the container ... - return $this->container->get(ContextMenuExtension::SERVICE_REQUEST_HANDLER)->handle( - Request::fromNameAndParameters( - $action->action(), - $this->replaceTokens($action->parameters(), $offset, $arguments) - ) - ); - } - - private function actionSelectionAction(Symbol $symbol, $symbolMenu, array $arguments): InputCallbackResponse - { - return InputCallbackResponse::fromCallbackAndInputs( - Request::fromNameAndParameters( - self::NAME, - [ - self::PARAMETER_SOURCE => $arguments[self::PARAMETER_SOURCE], - self::PARAMETER_OFFSET => $arguments[self::PARAMETER_OFFSET], - self::PARAMETER_CURRENT_PATH => $arguments[self::PARAMETER_CURRENT_PATH], - ] - ), - [ - ChoiceInput::fromNameLabelChoices( - self::PARAMETER_ACTION, - sprintf('%s "%s":', ucfirst($symbol->symbolType()), $symbol->name()), - array_combine(array_keys($symbolMenu), array_keys($symbolMenu)) - )->withKeys(array_combine(array_keys($symbolMenu), array_map(function (Action $action) { - return $action->key(); - }, $symbolMenu))) - ] - ); - } - - private function offsetFromSourceAndOffset(string $source, int $offset, string $currentPath) - { - $sourceCode = TextDocumentBuilder::create($source)->uri($currentPath)->build(); - - $interestingOffset = $this->offsetFinder->find( - $sourceCode, - ByteOffset::fromInt($offset) - ); - - return $this->reflector->reflectOffset( - $sourceCode, - $interestingOffset->toInt() - ); - } - - private function replaceTokens(array $parameters, ReflectionOffset $offset, array $arguments) - { - $nodeContext = $offset->nodeContext(); - foreach ($parameters as $parameterName => $parameterValue) { - switch ($parameterValue) { - case '%current_path%': - $parameterValue = $arguments[self::PARAMETER_CURRENT_PATH]; - break; - case '%path%': - // TODO: the "path" of the reflected type. You might expect - // this to be the current path but it is not. It is used - // when we want to act on the file in the "type" under the - // cursor. this shouldn't be a thing. - $type = $nodeContext->containerType()->isDefined() ? $nodeContext->containerType() : $nodeContext->type(); - $parameterValue = $this->classFileNormalizer->classToFile($type->generalize()); - break; - case '%offset%': - $parameterValue = $arguments[self::PARAMETER_OFFSET]; - break; - case '%source%': - $parameterValue = $arguments[self::PARAMETER_SOURCE]; - break; - case '%symbol%': - $parameterValue = $nodeContext->symbol()->name(); - break; - } - - $parameters[$parameterName] = $parameterValue; - } - - return $parameters; - } -} diff --git a/lib/Extension/ContextMenu/Model/Action.php b/lib/Extension/ContextMenu/Model/Action.php deleted file mode 100644 index 2a13079cd5..0000000000 --- a/lib/Extension/ContextMenu/Model/Action.php +++ /dev/null @@ -1,28 +0,0 @@ -action; - } - - public function parameters(): array - { - return $this->parameters; - } - - public function key(): ?string - { - return $this->key; - } -} diff --git a/lib/Extension/ContextMenu/Model/ContextMenu.php b/lib/Extension/ContextMenu/Model/ContextMenu.php deleted file mode 100644 index ec8523c16b..0000000000 --- a/lib/Extension/ContextMenu/Model/ContextMenu.php +++ /dev/null @@ -1,91 +0,0 @@ - $action) { - $this->actions[$name] = Invoke::new(Action::class, $action); - } - $this->validate(); - } - - public static function fromArray(array $array): self - { - return Invoke::new(self::class, $array); - } - - public function hasContext(string $context): bool - { - return isset($this->contexts[$context]); - } - - public function forContext(string $context): array - { - if (!isset($this->contexts[$context])) { - throw new RuntimeException(sprintf( - 'Context "%s" does not exist', - $context - )); - } - - return array_combine($this->contexts[$context], array_map(function (string $action) { - return $this->getAction($action); - }, $this->contexts[$context])); - } - - private function getAction(string $name): Action - { - if (!isset($this->actions[$name])) { - throw new RuntimeException(sprintf( - 'Action "%s" does not exist, known actions: "%s"', - $name, - implode('", "', array_keys($this->actions)) - )); - } - - return $this->actions[$name]; - } - - private function validate(): void - { - $missingActions = []; - foreach ($this->contexts as $name => $actions) { - $keys = []; - foreach ($actions as $actionName) { - if (!isset($this->actions[$actionName])) { - throw new RuntimeException(sprintf( - 'Action "%s" used in context "%s" does not exist, known actions: "%s"', - $actionName, - $name, - implode('", "', array_keys($this->actions)) - )); - } - - $action = $this->actions[$actionName]; - $key = $action->key() ?? ''; - - if (isset($keys[$key])) { - throw new RuntimeException(sprintf( - 'Key "%s" in context "%s" mapped by action "%s" is already used by action "%s"', - $key, - $name, - $actionName, - $keys[$key] - )); - } - - $keys[$key] = $actionName; - } - } - } -} diff --git a/lib/Extension/ContextMenu/menu.json b/lib/Extension/ContextMenu/menu.json deleted file mode 100644 index c644896b86..0000000000 --- a/lib/Extension/ContextMenu/menu.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "actions": { - "rename_variable": { - "action": "rename_variable", - "key": "r", - "parameters": { - "path": "%current_path%", - "offset": "%offset%", - "source": "%source%", - "name_suggestion": "%symbol%" - } - }, - "hover": { - "action": "hover", - "key": "h", - "parameters": { - "offset": "%offset%", - "source": "%source%" - } - }, - "extract_constant": { - "action": "extract_constant", - "key": "e", - "parameters": { - "path": "%path%", - "offset": "%offset%", - "source": "%source%" - } - }, - "cycle_visibility": { - "action": "change_visibility", - "key": "v", - "parameters": { - "offset": "%offset%", - "source": "%source%", - "path": "%current_path%" - } - }, - "find_references": { - "action": "references", - "key": "f", - "parameters": { - "offset": "%offset%", - "source": "%source%", - "path": "%current_path%" - } - }, - "goto_definition": { - "action": "goto_definition", - "key": "g", - "parameters": { - "offset": "%offset%", - "source": "%source%", - "path": "%path%" - } - }, - "goto_type": { - "action": "goto_type", - "key": "t", - "parameters": { - "offset": "%offset%", - "source": "%source%", - "path": "%path%" - } - }, - "replace_references": { - "action": "references", - "key": "r", - "parameters": { - "offset": "%offset%", - "source": "%source%", - "path": "%current_path%", - "mode": "replace" - } - }, - "hover": { - "action": "hover", - "key": "h", - "parameters": { - "offset": "%offset%", - "source": "%source%" - } - }, - "class_new": { - "action": "class_new", - "key": "l", - "parameters": { - "current_path": "%path%" - } - }, - "copy": { - "action": "copy_class", - "key": "c", - "parameters": { - "source_path": "%path%" - } - }, - "inflect": { - "action": "class_inflect", - "key": "i", - "parameters": { - "current_path": "%path%" - } - }, - "move": { - "action": "move_class", - "key": "m", - "parameters": { - "source_path": "%path%" - } - }, - "navigate": { - "action": "navigate", - "key": "n", - "parameters": { - "source_path": "%path%" - } - }, - "override_method": { - "action": "override_method", - "key": "v", - "parameters": { - "path": "%current_path%", - "source": "%source%" - } - }, - "transform_file": { - "action": "transform", - "key": "t", - "parameters": { - "path": "%path%", - "source": "%source%" - } - }, - "import": { - "action": "import_class", - "key": "p", - "parameters": { - "offset": "%offset%", - "path": "%current_path%", - "source": "%source%" - } - }, - "import_missing_classes": { - "action": "import_missing_classes", - "key": "o", - "parameters": { - "path": "%current_path%", - "source": "%source%" - } - }, - "generate_accessor": { - "action": "generate_accessor", - "key": "a", - "parameters": { - "path": "%path%", - "source": "%source%", - "offset": "%offset%" - } - }, - "generate_mutator": { - "action": "generate_mutator", - "key": "s", - "parameters": { - "path": "%path%", - "source": "%source%", - "offset": "%offset%" - } - }, - "goto_implementation": { - "action": "goto_implementation", - "key": "_", - "parameters": { - "offset": "%offset%", - "source": "%source%", - "path": "%path%" - } - }, - "generate_method": { - "action": "generate_method", - "key": "m", - "parameters": { - "path": "%current_path%", - "offset": "%offset%", - "source": "%source%" - } - } - }, - "contexts": { - "variable": [ - "goto_type", - "hover", - "rename_variable" - ], - "string": [ - "extract_constant", - "hover" - ], - "number": [ - "extract_constant" - ], - "constant": [ - "cycle_visibility", - "find_references", - "goto_definition", - "hover", - "replace_references" - ], - "class": [ - "class_new", - "copy", - "find_references", - "generate_accessor", - "generate_mutator", - "goto_definition", - "goto_implementation", - "hover", - "import", - "import_missing_classes", - "inflect", - "move", - "navigate", - "override_method", - "replace_references", - "transform_file" - ], - "property": [ - "cycle_visibility", - "find_references", - "generate_accessor", - "generate_mutator", - "goto_definition", - "goto_type", - "hover", - "replace_references" - ], - "method": [ - "cycle_visibility", - "find_references", - "generate_method", - "goto_definition", - "goto_type", - "hover", - "replace_references" - ] - } -} diff --git a/lib/Extension/Core/Application/CacheClear.php b/lib/Extension/Core/Application/CacheClear.php deleted file mode 100644 index 2d7f005674..0000000000 --- a/lib/Extension/Core/Application/CacheClear.php +++ /dev/null @@ -1,29 +0,0 @@ -cachePath = Path::canonicalize($cachePath); - $this->filesystem = new Filesystem(); - } - - public function clearCache(): void - { - $this->filesystem->remove($this->cachePath); - } - - public function cachePath() - { - return $this->cachePath; - } -} diff --git a/lib/Extension/Core/Application/Helper/ClassFileNormalizer.php b/lib/Extension/Core/Application/Helper/ClassFileNormalizer.php deleted file mode 100644 index c0b32a6964..0000000000 --- a/lib/Extension/Core/Application/Helper/ClassFileNormalizer.php +++ /dev/null @@ -1,64 +0,0 @@ -classToFile($classOrFile); - } - - return $classOrFile; - } - - public function normalizeToClass(string $classOrFile): string - { - if (true === $resp = Phpactor::isFile($classOrFile)) { - return (string) $this->fileToClass(Phpactor::normalizePath($classOrFile)); - } - - return $classOrFile; - } - - /** - * @return string - */ - public function classToFile(string $class, bool $hasToExist = false) - { - $filePathCandidates = $this->fileClassConverter->classToFileCandidates( - ClassName::fromString($class) - ); - - if ($hasToExist) { - foreach ($filePathCandidates as $candidate) { - if (file_exists((string) $candidate)) { - return (string) $candidate; - } - } - - return null; - } - - return (string) $filePathCandidates->best(); - } - - public function fileToClass(string $file): string - { - $classCandidates = $this->fileClassConverter->fileToClassCandidates( - FilePath::fromString($file) - ); - - return (string) $classCandidates->best(); - } -} diff --git a/lib/Extension/Core/Application/Helper/FilesystemHelper.php b/lib/Extension/Core/Application/Helper/FilesystemHelper.php deleted file mode 100644 index a406d2ece1..0000000000 --- a/lib/Extension/Core/Application/Helper/FilesystemHelper.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ - public static function globSourceDestination(string $src, string $dest): Generator - { - foreach (Glob::glob($src) as $globSrc) { - $globDest = $dest; - - // if the src is not the same as the globbed src, then it is a wildcard - // and we want to append the filename to the destination - if ($src !== $globSrc) { - $globDest = Path::join($dest, basename($globSrc)); - } - - yield $globSrc => $globDest; - } - } -} diff --git a/lib/Extension/Core/Application/Status.php b/lib/Extension/Core/Application/Status.php deleted file mode 100644 index d843e71515..0000000000 --- a/lib/Extension/Core/Application/Status.php +++ /dev/null @@ -1,121 +0,0 @@ -registry->names(); - $diagnostics = [ - 'filesystems' => $filesystems, - 'cwd' => $this->workingDirectory, - 'php_version' => $this->phpVersionResolver->resolve(), - 'config_files' => [], - 'good' => [], - 'bad' => [], - ]; - - if (in_array(SourceCodeFilesystemExtension::FILESYSTEM_COMPOSER, $filesystems)) { - $diagnostics['good'][] = 'Composer detected - Phpactor could work faster without an index'; - } else { - $diagnostics['bad'][] = 'Composer not found - some functionality will not be available (e.g. class creation) and class location will fallback to scanning the filesystem if index not enabled - this can be slow. Make sure you\'ve run `composer install` in your project!'; - } - - if (in_array(SourceCodeFilesystemExtension::FILESYSTEM_GIT, $filesystems)) { - $diagnostics['good'][] = 'Git detected - enables faster refactorings in your repository scope!'; - } else { - $diagnostics['bad'][] = 'Git not detected. Some operations which would have been better scoped to your project repository will now include vendor paths.'; - } - - if (XdebugHandler::isXdebugActive()) { - $diagnostics['bad'][] = 'XDebug is enabled. XDebug has a negative effect on performance.'; - } else { - $diagnostics['good'][] = 'XDebug is disabled. XDebug has a negative effect on performance.'; - } - - if ($this->trust->isTrusted($this->workingDirectory)) { - $diagnostics['good'][] = sprintf('Path "%s" is trusted and configuration will be loaded from it.', $this->workingDirectory); - } else { - $diagnostics['bad'][] = sprintf('Path "%s" is not trusted and configuration will not be loaded from it.', $this->workingDirectory); - } - - foreach ($this->paths as $configFile) { - $diagnostics['config_files'][$configFile->path()] = file_exists($configFile->path()); - } - - $diagnostics = $this->resolveVersion($diagnostics); - - return $diagnostics; - } - /** - * @param array $diagnostics - * @return array - */ - private function resolveVersion(array $diagnostics): array - { - if (Phar::running() !== '') { - $diagnostics['phpactor_version'] = InstalledVersions::getVersion('phpactor/phpactor'); - return $diagnostics; - } - - if ($path = $this->executableFinder->find('git')) { - $process = new Process( - [ - 'git', - 'log', - '-1', - '--pretty=format:"%h (%ad) %f REF(%D)REF', - '--date=relative' - ], - __DIR__ . '/../../../..' - ); - $process->run(); - return array_merge($diagnostics, $this->versionInfo($process)); - } - - return $diagnostics; - } - /** - * @return array - */ - private function versionInfo(Process $process): array - { - if ($process->getExitCode() !== 0) { - return [ - 'phpactor_version' => 'ERROR: ' . $process->getErrorOutput(), - ]; - } - - if (!preg_match('{^"?(.*)REF(.*?)REF}', $process->getOutput(), $matches)) { - return [ - 'phpactor_version' => $process->getOutput(), - ]; - } - - return [ - 'phpactor_version' => $matches[1], - ]; - } -} diff --git a/lib/Extension/Core/Command/CacheClearCommand.php b/lib/Extension/Core/Command/CacheClearCommand.php deleted file mode 100644 index 3b329e6b36..0000000000 --- a/lib/Extension/Core/Command/CacheClearCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -setDescription('Clear the cache'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->cache->clearCache(); - $output->writeln(sprintf('Cache cleared: %s', $this->cache->cachePath())); - - return 0; - } -} diff --git a/lib/Extension/Core/Command/ConfigDumpCommand.php b/lib/Extension/Core/Command/ConfigDumpCommand.php deleted file mode 100644 index 15029c49a9..0000000000 --- a/lib/Extension/Core/Command/ConfigDumpCommand.php +++ /dev/null @@ -1,66 +0,0 @@ -setDescription('Show loaded config files and dump current configuration.'); - $this->addOption('config-only', null, InputOption::VALUE_NONE, 'Do not output configuration file locations'); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - if (false === $input->getOption('config-only')) { - $this->dumpMetaInformation($output); - } - - $output->writeln(json_encode($this->config, JSON_PRETTY_PRINT)); - - return 0; - } - - private function dumpMetaInformation(OutputInterface $output): void - { - $output->writeln('Config files:'); - $output->write("\n"); - foreach ($this->paths as $candidate) { - if (!file_exists($candidate->path())) { - $output->write(' [✖]'); - } else { - $output->write(' [✔]'); - } - $output->writeln(' ' .$candidate->path()); - } - - $output->write("\n"); - $output->writeln('File path tokens:'); - $output->write("\n"); - foreach ($this->expanders->toArray() as $tokenName => $value) { - $output->writeln(sprintf(' %%%s%%: %s', $tokenName, $value)); - } - $terminal = new Terminal(); - $output->write("\n"); - $output->writeln(str_repeat('-', $terminal->getWidth())); - $output->write("\n"); - } -} diff --git a/lib/Extension/Core/Command/ConfigInitCommand.php b/lib/Extension/Core/Command/ConfigInitCommand.php deleted file mode 100644 index 9f4090bb88..0000000000 --- a/lib/Extension/Core/Command/ConfigInitCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -setDescription('Initialize Phpactor configuration file or update the location of the JSON schema'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $output->writeln('// This command will create or update a JSON configuration file'); - $output->writeln('// The YAML config format is not supported by this tool'); - - $created = !file_exists($this->initializer->configPath()); - $action = $this->initializer->initialize(); - - if ($created) { - $output->writeln(sprintf('Created %s', $this->initializer->configPath())); - return 0; - } - - $output->writeln(sprintf('Updated: %s', $this->initializer->configPath())); - - return 0; - } -} diff --git a/lib/Extension/Core/Command/ConfigJsonSchemaCommand.php b/lib/Extension/Core/Command/ConfigJsonSchemaCommand.php deleted file mode 100644 index 6378f9d71a..0000000000 --- a/lib/Extension/Core/Command/ConfigJsonSchemaCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -setDescription('Dump the JSON schema to the given relative path'); - $this->addArgument('path', InputArgument::REQUIRED, 'Target path for JSON schema file'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $path = (string)$input->getArgument('path'); - if (!@file_put_contents( - $path, - $this->builder->dump() - )) { - throw new RuntimeException(sprintf( - 'Could not write JSON file "%s"', - $path - )); - } - return 0; - } -} diff --git a/lib/Extension/Core/Command/ConfigSetCommand.php b/lib/Extension/Core/Command/ConfigSetCommand.php deleted file mode 100644 index c94fcb308f..0000000000 --- a/lib/Extension/Core/Command/ConfigSetCommand.php +++ /dev/null @@ -1,56 +0,0 @@ -setDescription('Set a config value'); - $this->addArgument(self::ARG_KEY, InputArgument::REQUIRED, 'Config key to set'); - $this->addArgument(self::ARG_VALUE, InputArgument::OPTIONAL, 'Value (JSON encoded) if omitted, key will be removed'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $action = $this->manipulator->initialize(); - - /** @var string $key */ - $key = $input->getArgument(self::ARG_KEY); - - /** @var string|null $value */ - $value = $input->getArgument(self::ARG_VALUE); - - if ($value !== null) { - try { - $this->manipulator->set($key, json_decode($value, true, 512, JSON_THROW_ON_ERROR)); - } catch (JsonException) { - $output->writeln(sprintf('Could not decode JSON value: %s', $value)); - return 1; - } - $output->writeln(sprintf('Updated: %s', $this->manipulator->configPath())); - $output->writeln(sprintf('%s = %s', $key, $value)); - return 0; - } - - $this->manipulator->delete($key); - $output->writeln(sprintf('Removed key: %s', $key)); - return 0; - } -} diff --git a/lib/Extension/Core/Command/DebugContainerCommand.php b/lib/Extension/Core/Command/DebugContainerCommand.php deleted file mode 100644 index 921d04e367..0000000000 --- a/lib/Extension/Core/Command/DebugContainerCommand.php +++ /dev/null @@ -1,115 +0,0 @@ -addOption('services', null, InputOption::VALUE_NONE, 'List all services'); - $this->addOption('parameters', null, InputOption::VALUE_NONE, 'List all parameters'); - $this->addOption('tags', null, InputOption::VALUE_NONE, 'List all tags'); - $this->addOption('tag', null, InputOption::VALUE_OPTIONAL|InputOption::VALUE_IS_ARRAY, 'Show specific tag'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - if ($input->getOption('services')) { - $this->renderServices($output); - } - if ($input->getOption('tags')) { - $this->renderTags($output); - } - if ($input->getOption('parameters')) { - $this->renderParameters($output); - } - - foreach ((array)$input->getOption('tag') as $tag) { - assert(is_string($tag)); - $this->renderTag($output, $tag); - } - - return 0; - } - - private function renderServices(OutputInterface $output): Table - { - $table = new Table($output); - $table->setStyle('borderless'); - $table->setHeaders([ - 'service ID', 'class', - ]); - foreach ($this->container->getServiceIds() as $serviceId) { - $type = ''; - - try { - $value = $this->container->get($serviceId); - $type = get_debug_type($value); - } catch (RuntimeException $exception) { - $table->addRow(['Error: '.$serviceId.'', $exception->getMessage()]); - } - - $table->addRow([$serviceId, $type]); - } - $table->render(); - return $table; - } - - private function renderTags(OutputInterface $output): void - { - $table = new Table($output); - $table->setStyle('borderless'); - $table->setHeaders([ - 'tags', 'service','attributes', - ]); - foreach ($this->container->getTags() as $tag => $serviceAttributes) { - $first = true; - foreach ($serviceAttributes as $serviceName => $attrs) { - $tag = $first ? $tag : ''; - $table->addRow([$tag, $serviceName, json_encode($attrs)]); - $first = false; - } - } - $table->render(); - } - - private function renderTag(OutputInterface $output, string $tag): void - { - $table = new Table($output); - $table->setStyle('borderless'); - $table->setHeaders([ - 'service','attributes', - ]); - foreach ($this->container->getServiceIdsForTag($tag) as $serviceId => $attrs) { - $table->addRow([$serviceId, json_encode($attrs, JSON_PRETTY_PRINT)]); - } - $table->render(); - } - - private function renderParameters(OutputInterface $output): void - { - $table = new Table($output); - $table->setStyle('borderless'); - $table->setHeaders([ - 'parameter','value', - ]); - foreach ($this->container->getParameters() as $key => $value) { - $table->addRow([$key, json_encode($value, JSON_PRETTY_PRINT)]); - } - $table->render(); - } -} diff --git a/lib/Extension/Core/Command/StatusCommand.php b/lib/Extension/Core/Command/StatusCommand.php deleted file mode 100644 index 15a305d7ac..0000000000 --- a/lib/Extension/Core/Command/StatusCommand.php +++ /dev/null @@ -1,56 +0,0 @@ -setDescription('Information about the current status of Phpactor'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $diagnostics = $this->status->check(); - - $output->writeln('Version: ' . $diagnostics['phpactor_version']); - $output->writeln(sprintf( - 'Filesystems: %s', - implode(', ', $diagnostics['filesystems']) - )); - $output->writeln('Working directory: ' . $diagnostics['cwd']); - $output->write("\n"); - - $output->writeln('Config files (missing is not bad):'); - $output->write("\n"); - foreach ($diagnostics['config_files'] as $configFile => $exists) { - $check = $exists ? '✔' : '✘'; - $output->writeln(sprintf(' %s %s', $check, $configFile)); - } - - $output->write("\n"); - - $output->writeln('Diagnostics:'); - $output->write("\n"); - foreach ($diagnostics['good'] as $good) { - $output->writeln(' ✔ ' . $good); - } - - foreach ($diagnostics['bad'] as $bad) { - $output->writeln(' ✘ ' . $bad); - } - $output->write("\n"); - - return 0; - } -} diff --git a/lib/Extension/Core/Command/TrustCommand.php b/lib/Extension/Core/Command/TrustCommand.php deleted file mode 100644 index e162c07f47..0000000000 --- a/lib/Extension/Core/Command/TrustCommand.php +++ /dev/null @@ -1,68 +0,0 @@ -setDescription('Trust the current working directory and load the Phactor configuration'); - $this->addOption(self::OPT_TRUST, null, InputOption::VALUE_NONE, 'Trust and don\'t ask'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $trustIt = (bool)$input->getOption(self::OPT_TRUST); - - $message = match ($this->status->isTrusted($this->projectDir)) { - true => sprintf('Path %s is trusted and configuartion will be loaded from it', $this->projectDir), - false => sprintf('Path %s is not trusted configuration will not be loaded from it', $this->projectDir), - }; - $output->writeln($message); - - $helper = new QuestionHelper(); - $yes = 'Yes. It\'s mine. I trust it'; - $no = 'No. I do not trust it'; - - if (true == $trustIt) { - $trusted = true; - } else { - $response = $helper->ask( - $input, - $output, - new ChoiceQuestion(sprintf('Do you trust %s?', $this->projectDir), [ - 'yes' => $yes, - 'no' => $no, - ]) - ); - $trusted = $response === 'yes'; - } - - $this->status->setTrusted($this->projectDir, $trusted); - - if ($trusted) { - $output->writeln(sprintf('%s is now trusted', $this->projectDir)); - return 0; - } - $output->writeln(sprintf('%s is now not trusted', $this->projectDir)); - - return 0; - } -} diff --git a/lib/Extension/Core/Console/Dumper/Dumper.php b/lib/Extension/Core/Console/Dumper/Dumper.php deleted file mode 100644 index 3c6f60b631..0000000000 --- a/lib/Extension/Core/Console/Dumper/Dumper.php +++ /dev/null @@ -1,10 +0,0 @@ - $dumper) { - $this->add($name, $dumper); - } - } - - public function get(?string $name = null): Dumper - { - $name = $name ?: $this->default; - if (!isset($this->dumpers[$name])) { - throw new InvalidArgumentException(sprintf( - 'Unknown dumper "%s", known dumpers: "%s"', - $name, - implode('", "', array_keys($this->dumpers)) - )); - } - - return $this->dumpers[$name]; - } - - private function add(string $name, Dumper $dumper): void - { - $this->dumpers[$name] = $dumper; - } -} diff --git a/lib/Extension/Core/Console/Dumper/IndentedDumper.php b/lib/Extension/Core/Console/Dumper/IndentedDumper.php deleted file mode 100644 index bee141a3cb..0000000000 --- a/lib/Extension/Core/Console/Dumper/IndentedDumper.php +++ /dev/null @@ -1,48 +0,0 @@ -doDump($output, $data); - } - - private function doDump(OutputInterface $output, array $data, $padding = 0): void - { - $style = match ($padding) { - 1 => 'info', - default => 'comment', - }; - foreach ($data as $key => $value) { - if (is_array($value)) { - $output->writeln(sprintf('%s<%s>%s:', str_repeat(self::PADDING, $padding), $style, $key)); - $this->doDump($output, $value, ++$padding); - $padding--; - continue; - } - - $output->writeln(sprintf( - '%s<%s>%s:%s', - str_repeat(self::PADDING, $padding), - $style, - $key, - $this->formatValue($value) - )); - } - } - - private function formatValue($value) - { - if (is_bool($value)) { - return $value ? 'true' : 'false'; - } - - return $value; - } -} diff --git a/lib/Extension/Core/Console/Dumper/JsonDumper.php b/lib/Extension/Core/Console/Dumper/JsonDumper.php deleted file mode 100644 index f943aadc34..0000000000 --- a/lib/Extension/Core/Console/Dumper/JsonDumper.php +++ /dev/null @@ -1,13 +0,0 @@ -writeln(json_encode($data)); - } -} diff --git a/lib/Extension/Core/Console/Dumper/TableDumper.php b/lib/Extension/Core/Console/Dumper/TableDumper.php deleted file mode 100644 index 57bc05ce5e..0000000000 --- a/lib/Extension/Core/Console/Dumper/TableDumper.php +++ /dev/null @@ -1,42 +0,0 @@ - $value) { - if (is_array($value)) { - $value = $this->formatArray($value); - } - - $table->addRow([ '' . $key . '', $value ]); - } - $table->render(); - } - - private function formatArray(array $data, $padding = 0) - { - $output = []; - foreach ($data as $key => $value) { - if (is_array($value)) { - $output[] = str_repeat(self::PADDING, $padding) . $key . ':'; - $output[] = $this->formatArray($value, ++$padding); - $padding--; - continue; - } - - $output[] = sprintf('%s%s: %s', str_repeat(self::PADDING, $padding), $key, $value); - } - - return implode("\n", $output); - } -} diff --git a/lib/Extension/Core/Console/Formatter/Highlight.php b/lib/Extension/Core/Console/Formatter/Highlight.php deleted file mode 100644 index 107765889a..0000000000 --- a/lib/Extension/Core/Console/Formatter/Highlight.php +++ /dev/null @@ -1,26 +0,0 @@ -'; - $rightBracket = ''; - } - - return sprintf( - '%s%s%s%s%s', - substr($line, 0, $col), - $leftBracket, - $subject, - $rightBracket, - substr($line, $col + strlen($subject)) - ); - } -} diff --git a/lib/Extension/Core/Console/Handler/FilesystemHandler.php b/lib/Extension/Core/Console/Handler/FilesystemHandler.php deleted file mode 100644 index d561d97098..0000000000 --- a/lib/Extension/Core/Console/Handler/FilesystemHandler.php +++ /dev/null @@ -1,14 +0,0 @@ -addOption('filesystem', null, InputOption::VALUE_REQUIRED, 'Filesystem (informs scope of changes)', $default); - } -} diff --git a/lib/Extension/Core/Console/Handler/FormatHandler.php b/lib/Extension/Core/Console/Handler/FormatHandler.php deleted file mode 100644 index 7569ecb6c5..0000000000 --- a/lib/Extension/Core/Console/Handler/FormatHandler.php +++ /dev/null @@ -1,14 +0,0 @@ -addOption('format', null, InputOption::VALUE_REQUIRED, 'Output format'); - } -} diff --git a/lib/Extension/Core/Console/Prompt/BashPrompt.php b/lib/Extension/Core/Console/Prompt/BashPrompt.php deleted file mode 100644 index 0c30fb73d8..0000000000 --- a/lib/Extension/Core/Console/Prompt/BashPrompt.php +++ /dev/null @@ -1,50 +0,0 @@ -getBashPath(), - escapeshellarg($prompt), - escapeshellarg($prefill) - ); - - // for some reason exec (?) doesn't like us using single quotes - $cmd = str_replace('\'', '__QUOTE__', $cmd); - $cmd = str_replace('"', '\'', $cmd); - $cmd = str_replace('__QUOTE__', '"', $cmd); - - $result = exec($cmd); - if (false === $result) { - throw new RuntimeException(sprintf( - 'Could not run bash prompt "%s"', - $prompt - )); - } - return $result; - } - - public function name(): string - { - return 'bash'; - } - - public function isSupported() - { - return null !== $this->getBashPath(); - } - - private function getBashPath() - { - $executableFinder = new ExecutableFinder(); - - return $executableFinder->find('bash'); - } -} diff --git a/lib/Extension/Core/Console/Prompt/ChainPrompt.php b/lib/Extension/Core/Console/Prompt/ChainPrompt.php deleted file mode 100644 index 87ac9a26a5..0000000000 --- a/lib/Extension/Core/Console/Prompt/ChainPrompt.php +++ /dev/null @@ -1,51 +0,0 @@ -addPrompt($prompt); - } - } - - public function prompt(string $prompt, string $prefill): string - { - foreach ($this->prompts as $prompter) { - if (false === $prompter->isSupported()) { - continue; - } - - return $prompter->prompt($prompt, $prefill); - } - - throw new RuntimeException(sprintf( - 'Could not prompt for "%s". '. - 'Appropriate prompt implementation for your platform / environment could not be found (tried "%s"). '. - 'Try specifying the command in full', - $prompt, - implode('", "', array_keys($this->prompts)) - )); - } - - public function isSupported() - { - return true; - } - - public function name(): string - { - return 'chain'; - } - - private function addPrompt(Prompt $prompt): void - { - $this->prompts[$prompt->name()] = $prompt; - } -} diff --git a/lib/Extension/Core/Console/Prompt/Prompt.php b/lib/Extension/Core/Console/Prompt/Prompt.php deleted file mode 100644 index 9c652ddac1..0000000000 --- a/lib/Extension/Core/Console/Prompt/Prompt.php +++ /dev/null @@ -1,18 +0,0 @@ -setDefaults([ - self::PARAM_DUMPER => 'indented', - self::PARAM_XDEBUG_DISABLE => true, - self::PARAM_COMMAND => null, - self::PARAM_MIN_MEMORY_LIMIT => 1610612736, - self::PARAM_SCHEMA => '', - self::PARAM_PROJECT_CONFIG_CANDIDATES => [], - self::PARAM_TRUST => new Trust([], null), - self::PARAM_TRUSTED => false, - ]); - $schema->setDescriptions([ - self::PARAM_XDEBUG_DISABLE => 'If XDebug should be automatically disabled', - self::PARAM_COMMAND => 'Internal use only - name of the command which was executed', - self::PARAM_DUMPER => 'Name of the "dumper" (renderer) to use for some CLI commands', - self::PARAM_MIN_MEMORY_LIMIT => 'Ensure that PHP has a memory_limit of at least this amount in bytes', - self::PARAM_SCHEMA => 'Path to JSON schema, which can be used for config autocompletion, use phpactor config:initialize to update', - self::PARAM_PROJECT_CONFIG_CANDIDATES => '(internal) list of potential project-level configuration files', - self::PARAM_TRUST => '(internal) map of trusted project directories', - self::PARAM_TRUSTED => '(internal) if the configuration is trusted', - ]); - } - - public function load(ContainerBuilder $container): void - { - $this->registerConsole($container); - $this->registerApplicationServices($container); - $this->registerRpc($container); - $this->registerFilePathExpanders($container); - } - - private function registerConsole(ContainerBuilder $container): void - { - $container->register('command.config_dump', function (Container $container) { - return new ConfigDumpCommand( - $container->getParameters(), - $container->expect('console.dumper_registry', DumperRegistry::class), - $container->expect('config_loader.candidates', PathCandidates::class), - $container->expect(FilePathResolverExtension::SERVICE_EXPANDERS, Expanders::class) - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'config:dump']]); - - $container->register('command.debug_container', function (Container $container) { - return new DebugContainerCommand( - $container - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'container:dump']]); - - $container->register('command.trust', function (Container $container) { - return new TrustCommand( - /** @phpstan-ignore argument.type */ - $container->parameter(self::PARAM_TRUST)->value(), - $container->parameter(FilePathResolverExtension::PARAM_PROJECT_ROOT)->string(), - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'config:trust']]); - - $container->register('command.cache_clear', function (Container $container) { - return new CacheClearCommand( - $container->get('application.cache_clear') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'cache:clear' ]]); - - $container->register('command.status', function (Container $container) { - return new StatusCommand( - $container->get('application.status'), - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'status' ]]); - - - $container->register('console.dumper_registry', function (Container $container) { - $dumpers = []; - foreach ($container->getServiceIdsForTag('console.dumper') as $dumperId => $attrs) { - $dumpers[$attrs['name']] = $container->get($dumperId); - } - - return new DumperRegistry($dumpers, $container->getParameter(self::PARAM_DUMPER)); - }); - - $container->register('console.dumper.indented', function (Container $container) { - return new IndentedDumper(); - }, [ 'console.dumper' => ['name' => 'indented']]); - - $container->register('console.dumper.json', function (Container $container) { - return new JsonDumper(); - }, [ 'console.dumper' => ['name' => 'json']]); - - $container->register('console.dumper.fieldvalue', function (Container $container) { - return new TableDumper(); - }, [ 'console.dumper' => ['name' => 'fieldvalue']]); - - $container->register('console.prompter', function (Container $container) { - return new ChainPrompt([ - new BashPrompt() - ]); - }); - } - - private function registerApplicationServices(ContainerBuilder $container): void - { - $container->register('application.cache_clear', function (Container $container) { - return new CacheClear( - $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER)->resolve('%cache%') - ); - }); - - $container->register('application.helper.class_file_normalizer', function (Container $container) { - return new ClassFileNormalizer($container->get('class_to_file.converter')); - }); - - $container->register('application.status', function (Container $container) { - /** @var PathCandidates $paths */ - $paths = $container->has('config_loader.candidates') ? $container->get('config_loader.candidates') : new PathCandidates([]); - - return new Status( - registry: $container->expect('source_code_filesystem.registry', FilesystemRegistry::class), - // candidates are bootstrapped outside of the extensions and are not loaded in the language server - paths: $paths, - workingDirectory: $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class)->resolve('%project_root%'), - phpVersionResolver: $container->get(PhpVersionResolver::class), - /** @phpstan-ignore argument.type */ - trust: $container->parameter(self::PARAM_TRUST)->value(), - ); - }); - } - - private function registerRpc(ContainerBuilder $container): void - { - $container->register('core.rpc.handler.cache_clear', function (Container $container) { - return new CacheClearHandler($container->get('application.cache_clear')); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => CacheClearHandler::NAME] ]); - - $container->register('core.rpc.handler.status', function (Container $container) { - return new StatusHandler($container->get('application.status'), $container->get('config_loader.candidates')); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => StatusHandler::NAME] ]); - - $container->register('core.rpc.handler.config', function (Container $container) { - return new ConfigHandler($container->getParameters()); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ConfigHandler::CONFIG] ]); - - $container->register('core.rpc.handler.trust', function (Container $container) { - return new TrustHandler( - /** @phpstan-ignore argument.type */ - $container->parameter(self::PARAM_TRUST)->value(), - $container->parameter(FilePathResolverExtension::PARAM_PROJECT_ROOT)->string(), - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => TrustHandler::NAME] ]); - } - - private function registerFilePathExpanders(ContainerBuilder $container): void - { - $container->register('core.file_path_resolver.project_config_expander', function (Container $container) { - $path = $container->getParameter(FilePathResolverExtension::PARAM_PROJECT_ROOT) . '/.phpactor'; - return new ValueExpander('project_config', $path); - }, [ FilePathResolverExtension::TAG_EXPANDER => [] ]); - } -} diff --git a/lib/Extension/Core/Rpc/CacheClearHandler.php b/lib/Extension/Core/Rpc/CacheClearHandler.php deleted file mode 100644 index 780338f4d6..0000000000 --- a/lib/Extension/Core/Rpc/CacheClearHandler.php +++ /dev/null @@ -1,33 +0,0 @@ -cacheClear->clearCache(); - - return EchoResponse::fromMessage(sprintf('Cache cleared: %s', $this->cacheClear->cachePath())); - } -} diff --git a/lib/Extension/Core/Rpc/ConfigHandler.php b/lib/Extension/Core/Rpc/ConfigHandler.php deleted file mode 100644 index 562b1041d5..0000000000 --- a/lib/Extension/Core/Rpc/ConfigHandler.php +++ /dev/null @@ -1,30 +0,0 @@ -config, JSON_PRETTY_PRINT)); - } -} diff --git a/lib/Extension/Core/Rpc/StatusHandler.php b/lib/Extension/Core/Rpc/StatusHandler.php deleted file mode 100644 index 0da3197fb6..0000000000 --- a/lib/Extension/Core/Rpc/StatusHandler.php +++ /dev/null @@ -1,103 +0,0 @@ -setDefaults([ - self::PARAM_TYPE => self::TYPE_FORMATTED, - ]); - } - - public function handle(array $arguments) - { - $diagnostics = $this->status->check(); - - $response = match ($arguments[self::PARAM_TYPE]) { - self::TYPE_FORMATTED => $this->handleFormattedType($diagnostics), - default => $this->handleDetailedType($diagnostics), - }; - - return $response; - } - - private function handleDetailedType(array $status): ReturnResponse - { - $status['diagnostics'] = \array_merge( - \array_fill_keys($status['good'], true), - \array_fill_keys($status['bad'], false) - ); - - unset($status['good']); - unset($status['bad']); - - return ReturnResponse::fromValue($status); - } - - private function handleFormattedType(array $diagnostics): EchoResponse - { - $info = [ - 'Info', - '----', - 'Version: ' . $diagnostics['phpactor_version'], - 'PHP: ' . sprintf('%s (supporting %s)', phpversion(), $diagnostics['php_version']), - 'Phpactor dir: ' . realpath(__DIR__ . '/../../../../'), - 'Work dir: ' . $diagnostics['cwd'] . "\n", - 'Diagnostics', - '-----------', - $this->buildSupportMessage($diagnostics), - 'Config files', - '------------', - $this->buildConfigFileMessage(), - ]; - return EchoResponse::fromMessage(implode("\n", $info)); - } - - private function buildSupportMessage(array $diagnostics) - { - return implode("\n", [ - implode("\n", array_map(function (string $message) { - return '[✔] ' . $message; - }, $diagnostics['good'])), - implode("\n", array_map(function (string $message) { - return '[✘] ' . $message; - }, $diagnostics['bad'])), - ]); - } - - private function buildConfigFileMessage() - { - return implode("\n", array_map(function (PathCandidate $file) { - if (file_exists($file->path())) { - return '[✔] ' . $file->path(); - } - return '[✘] ' . $file->path(); - }, iterator_to_array($this->paths))); - } -} diff --git a/lib/Extension/Core/Rpc/TrustHandler.php b/lib/Extension/Core/Rpc/TrustHandler.php deleted file mode 100644 index 562eedf7ca..0000000000 --- a/lib/Extension/Core/Rpc/TrustHandler.php +++ /dev/null @@ -1,48 +0,0 @@ -setRequired([ - self::PARAM_TRUST - ]); - } - - /** - * @param array{trust:int} $arguments - */ - public function handle(array $arguments): Response - { - $trust = (bool)$arguments['trust']; - $this->status->setTrusted($this->projectDir, $trust); - - if ($trust) { - return EchoResponse::fromMessage(sprintf('Project directory "%s" is trusted. Configuration will be loaded from it.', $this->projectDir)); - } - return EchoResponse::fromMessage(sprintf('Project directory "%s" is not trusted. Configuration will not be loaded from it.', $this->projectDir)); - - } - - public function name(): string - { - return self::NAME; - } -} diff --git a/lib/Extension/Core/Tests/Trust/ProjectConfigTrustListenerTest.php b/lib/Extension/Core/Tests/Trust/ProjectConfigTrustListenerTest.php deleted file mode 100644 index ba153aa1b4..0000000000 --- a/lib/Extension/Core/Tests/Trust/ProjectConfigTrustListenerTest.php +++ /dev/null @@ -1,109 +0,0 @@ -workspace()->reset(); - } - - public function testDoNotAskIfNoConfig(): void - { - $candidate = __DIR__ .'/phpactor.not-existing'; - - $transmitter = $this->invokeListener($candidate); - - self::assertEquals(0, $transmitter->count()); - } - - public function testTrustConfig(): void - { - $candidate = __DIR__ .'/phpactor.foobar'; - $userResponse = ProjectConfigTrustListener::RESP_YES; - - $transmitter = $this->invokeListener($candidate, $userResponse); - - $request = $transmitter->shiftRequest(); - self::assertNotNull($request); - self::assertEquals('window/showMessageRequest', $request->method); - $message =$transmitter->shiftNotification(); - self::assertNotNull($message); - self::assertEquals('window/showMessage', $message->method); - - $trust = Trust::load($this->workspace()->path('trust.json')); - - // directory should now be trusted - self::assertTrue($trust->isTrusted(__DIR__)); - - - // and the user won't be bothered about it again - $transmitter = $this->invokeListener($candidate); - self::assertEquals(0, $transmitter->count()); - } - - public function testNoTrustConfig(): void - { - $candidate = __DIR__ .'/phpactor.foobar'; - $userResponse = ProjectConfigTrustListener::RESP_NO; - - $transmitter = $this->invokeListener($candidate, $userResponse); - - self::assertEquals(2, $transmitter->count()); - - $trust = Trust::load($this->workspace()->path('trust.json')); - - // directory should now not be trusted - self::assertFalse($trust->isTrusted(__DIR__)); - - // and the user won't be bothered about it again - $transmitter = $this->invokeListener($candidate); - self::assertEquals(0, $transmitter->count()); - } - - public function testMaybe(): void - { - $candidate = __DIR__ .'/phpactor.foobar'; - $userResponse = ProjectConfigTrustListener::RESP_MAYBE; - - $transmitter = $this->invokeListener($candidate, $userResponse); - - self::assertEquals(1, $transmitter->count()); - - $trust = Trust::load($this->workspace()->path('trust.json')); - // directory is not trusted ... - self::assertFalse($trust->isTrusted(__DIR__)); - - // ... but we'll ask again - $transmitter = $this->invokeListener($candidate); - self::assertEquals(1, $transmitter->count()); - } - - private function invokeListener(string $candidate, ?string $userResponse = null): TestMessageTransmitter - { - $transmitter = new TestMessageTransmitter(); - $watcher = new TestResponseWatcher(); - $clientApi = new ClientApi(new TestRpcClient($transmitter, $watcher)); - - $promise = (new ProjectConfigTrustListener( - $clientApi, - [$candidate], - Trust::load($this->workspace()->path('trust.json')) - ))->handleTrustConfig(); - - if ($userResponse !== null) { - $watcher->resolveLastResponse(new MessageActionItem($userResponse)); - } - return $transmitter; - } -} diff --git a/lib/Extension/Core/Tests/Trust/phpactor.foobar b/lib/Extension/Core/Tests/Trust/phpactor.foobar deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Extension/Core/Trust/Trust.php b/lib/Extension/Core/Trust/Trust.php deleted file mode 100644 index 8dfc69b838..0000000000 --- a/lib/Extension/Core/Trust/Trust.php +++ /dev/null @@ -1,91 +0,0 @@ - $trust - */ - public function __construct( - public array $trust, - public readonly ?string $path - ) { - $this->unconditionalTrust = (bool)getenv('PHPACTOR_UNCONDITIONAL_TRUST'); - } - - public static function load(string $trustPath): self - { - if (!file_exists($trustPath)) { - return new self([], $trustPath); - } - - $trustContents = file_get_contents($trustPath); - if (false === $trustContents) { - throw new RuntimeException(sprintf( - 'Could not read trust file "%s"', - $trustPath - )); - } - - $trust = json_decode($trustContents, true); - - // file is invalid, return empty so that it will be overwritten - if (!is_array($trust)) { - return new self([], $trustPath); - } - - return new self($trust, $trustPath); - } - - public function setTrusted(string $path, bool $trust): void - { - $this->trust[$path] = $trust; - $this->writeTrust(); - } - - public function hasTrust(string $path): bool - { - return isset($this->trust[$path]); - } - - public function isTrusted(string $path): bool - { - if ($this->unconditionalTrust) { - return true; - } - - if (!$this->hasTrust($path)) { - return false; - } - - return $this->trust[$path]; - } - - private function writeTrust(): void - { - if (null === $this->path) { - throw new RuntimeException('Cannot write Trust as no trust file path was provided'); - } - if (!file_exists(dirname($this->path))) { - $success = @mkdir(dirname($this->path), 0755, true); - if (!$success) { - throw new RuntimeException(sprintf( - 'Could not create directory: "%s"', - dirname($this->path) - )); - } - } - $written = file_put_contents($this->path, json_encode($this->trust, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)); - if (false === $written) { - throw new RuntimeException(sprintf( - 'Could not write trust file to "%s"', - $this->path - )); - } - } -} diff --git a/lib/Extension/Debug/Command/GenerateDocumentationCommand.php b/lib/Extension/Debug/Command/GenerateDocumentationCommand.php deleted file mode 100644 index 2fcdd08142..0000000000 --- a/lib/Extension/Debug/Command/GenerateDocumentationCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -setDescription('Generate configuration reference as an RST document'); - $this->addArgument('documentor', InputArgument::REQUIRED); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $documentor = $this->documentorRegistry->get($input->getArgument('documentor')); - fwrite(STDOUT, $documentor->document($this->getName())); - - return 0; - } -} diff --git a/lib/Extension/Debug/DebugExtension.php b/lib/Extension/Debug/DebugExtension.php deleted file mode 100644 index aab49331ae..0000000000 --- a/lib/Extension/Debug/DebugExtension.php +++ /dev/null @@ -1,75 +0,0 @@ -register(DocumentorRegistry::class, function (Container $container) { - $serviceMap = []; - foreach ($container->getServiceIdsForTag(self::TAG_DOCUMENTOR) as $serviceId => $attrs) { - if (!isset($attrs['name'])) { - throw new RuntimeException(sprintf( - 'Documentor "%s" must be provided with a "name" ' . - 'attribute when it is registered', - $serviceId - )); - } - - $serviceMap[$attrs['name']] = $serviceId; - } - return new DocumentorRegistry($container, $serviceMap); - }); - - $container->register(DefinitionDocumentor::class, function (Container $container) { - return new DefinitionDocumentor(); - }); - - $container->register(ExtensionDocumentor::class, function (Container $container) { - return new ExtensionDocumentor( - $container->getParameter(PhpactorContainer::PARAM_EXTENSION_CLASSES), - $container->get(DefinitionDocumentor::class) - ); - }, [ - self::TAG_DOCUMENTOR => ['name' => self::EXTENSION_DOCUMENTOR_NAME] - ]); - - $container->register(GenerateDocumentationCommand::class, function (Container $container) { - return new GenerateDocumentationCommand($container->get(DocumentorRegistry::class)); - }, [ - ConsoleExtension::TAG_COMMAND => [ - 'name' => 'development:generate-documentation' - ] - ]); - - $container->register(JsonSchemaBuilder::class, function (Container $container) { - return new JsonSchemaBuilder( - 'Phpactor Configuration Schema', - /** @phpstan-ignore-next-line */ - $container->getParameter(PhpactorContainer::PARAM_EXTENSION_CLASSES) - ); - }); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/Debug/Model/DefinitionDocumentor.php b/lib/Extension/Debug/Model/DefinitionDocumentor.php deleted file mode 100644 index 0bcdf431c7..0000000000 --- a/lib/Extension/Debug/Model/DefinitionDocumentor.php +++ /dev/null @@ -1,44 +0,0 @@ -name()); - $help[] = "\n"; - $help[] = '``' . $definition->name() . '``'; - $help[] = str_repeat('"', mb_strlen($definition->name()) + 4); - - if ($definition->types()) { - $help[] = "\n"; - $help[] = sprintf('Type: %s', implode('|', $definition->types())); - } - - if ($definition->description()) { - $help[] = "\n"; - $help[] = $definition->description(); - } - - $help[] = "\n"; - $help[] = sprintf( - '**Default**: ``%s``', - json_encode($definition->defaultValue()) - ); - $help[] = "\n"; - - $enum = $definition->enum(); - if ($enum) { - $help[] = sprintf( - '**Allowed values**: %s', - implode(', ', array_map(fn ($v) => json_encode($v), $enum)) - ); - $help[] = "\n"; - } - - return implode("\n", $help); - } -} diff --git a/lib/Extension/Debug/Model/DocHelper.php b/lib/Extension/Debug/Model/DocHelper.php deleted file mode 100644 index 6d148de7f3..0000000000 --- a/lib/Extension/Debug/Model/DocHelper.php +++ /dev/null @@ -1,16 +0,0 @@ - $documentors - */ - public function __construct( - private Container $container, - private array $documentors - ) { - } - - public function get(string $string): Documentor - { - if (!array_key_exists($string, $this->documentors)) { - throw new InvalidArgumentException( - 'Could not find documentor. Available documentors: ' . implode(', ', array_keys($this->documentors)) - ); - } - - return $this->container->expect($this->documentors[$string], Documentor::class); - } -} diff --git a/lib/Extension/Debug/Model/ExtensionDocumentor.php b/lib/Extension/Debug/Model/ExtensionDocumentor.php deleted file mode 100644 index 2b599c2ca7..0000000000 --- a/lib/Extension/Debug/Model/ExtensionDocumentor.php +++ /dev/null @@ -1,99 +0,0 @@ - $extensionFqns - */ - public function __construct( - private array $extensionFqns, - private DefinitionDocumentor $definitionDocumentor - ) { - } - - public function document(string $commandName=''): string - { - $docs = [ - '.. _ref_configuration:', - '', - 'Configuration', - '=============', - "\n", - ".. This document is generated via the `$commandName` command", - "\n", - '.. contents::', - ' :depth: 2', - ' :backlinks: none', - ' :local:', - "\n", - ]; - foreach ($this->extensionFqns as $extensionFqn) { - $documentation = $this->documentExtension($extensionFqn); - if (null === $documentation) { - continue; - } - $docs[] = $documentation; - } - return implode("\n", $docs); - } - - private function documentExtension(string $extensionClass): ?string - { - $parts = explode('\\', $extensionClass); - $documentedName = end($parts); - - /** @phpstan-ignore-next-line */ - if (false === $documentedName) { - throw new RuntimeException(sprintf( - 'Invalid extension class name "%s"', - $extensionClass - )); - } - - $help = [ - '.. _' . $documentedName . ':', - "\n", - $documentedName, - str_repeat('-', mb_strlen($documentedName)), - "\n", - ]; - - $extension = new $extensionClass(); - - if (!$extension instanceof Extension) { - throw new RuntimeException(sprintf( - 'Expected "%s" to be an instanceof Phpactor\Container\Extension', - get_class($extension) - )); - } - - $resolver = new Resolver(); - if ($extension instanceof OptionalExtension) { - (function (string $key) use ($resolver): void { - $resolver->setDefaults([$key => false]); - $resolver->setTypes([$key => 'boolean']); - $resolver->setDescriptions([$key => 'Enable or disable this extension']); - })(sprintf('%s.enabled', $extension->name())); - } - $extension->configure($resolver); - - $hasDefinitions = false; - foreach ($resolver->definitions() as $definition) { - $hasDefinitions = true; - $help[] = $this->definitionDocumentor->document('param', $definition); - } - - if (!$hasDefinitions) { - return null; - } - - return implode("\n", $help); - } -} diff --git a/lib/Extension/Debug/Tests/.gitkeep b/lib/Extension/Debug/Tests/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Extension/Debug/Tests/Unit/DebugExtensionTest.php b/lib/Extension/Debug/Tests/Unit/DebugExtensionTest.php deleted file mode 100644 index 84fadad7d3..0000000000 --- a/lib/Extension/Debug/Tests/Unit/DebugExtensionTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getServiceIds() as $serviceId) { - $container->get($serviceId); - } - $this->addToAssertionCount(1); - } -} diff --git a/lib/Extension/Debug/bootstrap.php b/lib/Extension/Debug/bootstrap.php deleted file mode 100644 index d7fd8efd7e..0000000000 --- a/lib/Extension/Debug/bootstrap.php +++ /dev/null @@ -1,18 +0,0 @@ - new CliContextProvider(), - 'source' => new SourceContextProvider(), -]); - -VarDumper::setHandler(function ($var) use ($cloner, $dumper): void { - $dumper->dump($cloner->cloneVar($var)); -}); diff --git a/lib/Extension/FilePathResolver/FilePathResolverExtension.php b/lib/Extension/FilePathResolver/FilePathResolverExtension.php deleted file mode 100644 index 529ff5f468..0000000000 --- a/lib/Extension/FilePathResolver/FilePathResolverExtension.php +++ /dev/null @@ -1,134 +0,0 @@ -setDefaults([ - self::PARAM_PROJECT_ROOT => getcwd(), - self::PARAM_APP_NAME => 'phpactor', - self::PARAM_APPLICATION_ROOT => null, - self::PARAM_ENABLE_CACHE => true, - self::PARAM_ENABLE_LOGGING => true, - ]); - } - - - public function load(ContainerBuilder $container): void - { - $this->registerPathResolver($container); - $this->registerFilters($container); - } - - public static function calculateProjectId(string $projectRoot): string - { - if (empty($projectRoot)) { - throw new RuntimeException( - 'Project root must be a non-empty string' - ); - } - - return sprintf( - '%s-%s', - basename($projectRoot), - substr(md5(TextDocumentUri::fromString($projectRoot)->path()), 0, 6) - ); - } - - private function registerPathResolver(ContainerBuilder $container): void - { - $container->register(self::SERVICE_FILE_PATH_RESOLVER, function (Container $container) { - $filters = []; - foreach (array_keys($container->getServiceIdsForTag(self::TAG_FILTER)) as $serviceId) { - $filters[] = $container->get($serviceId); - } - - $resolver = new FilteringPathResolver($filters); - - if ($container->parameter(self::PARAM_ENABLE_CACHE)->bool()) { - $resolver = new CachingPathResolver($resolver); - } - - if ($container->parameter(self::PARAM_ENABLE_LOGGING)->bool()) { - $resolver = new LoggingPathResolver( - $resolver, - LoggingExtension::channelLogger($container, self::LOG_CHANNEL), - LogLevel::DEBUG - ); - } - - return $resolver; - }); - } - - private function registerFilters(ContainerBuilder $container): void - { - $container->register('file_path_resolver.filter.canonicalizing', function () { - return new CanonicalizingPathFilter(); - }, [ self::TAG_FILTER => [] ]); - - $container->register('file_path_resolver.filter.token_expanding', function (Container $container) { - return new TokenExpandingFilter($container->expect(self::SERVICE_EXPANDERS, Expanders::class)); - }, [ self::TAG_FILTER => [] ]); - - $container->register(self::SERVICE_EXPANDERS, function (Container $container) { - $suffix = DIRECTORY_SEPARATOR . $container->getParameter(self::PARAM_APP_NAME); - - $projectRoot = $container->parameter(self::PARAM_PROJECT_ROOT)->string(); - $expanders = [ - new ValueExpander('project_id', self::calculateProjectId($projectRoot)), - new ValueExpander('project_root', $projectRoot), - new SuffixExpanderDecorator(new XdgCacheExpander('cache'), $suffix), - new SuffixExpanderDecorator(new XdgConfigExpander('config'), $suffix), - new SuffixExpanderDecorator(new XdgDataExpander('data'), $suffix), - ]; - - /** @var string|null $applicationRoot */ - $applicationRoot = $container->getParameter(self::PARAM_APPLICATION_ROOT); - if (null !== $applicationRoot) { - $expanders[] = new ValueExpander('application_root', $applicationRoot); - } - - foreach (array_keys($container->getServiceIdsForTag(self::TAG_EXPANDER)) as $serviceId) { - $expanders[] = $container->expect($serviceId, Expander::class); - } - - return new Expanders($expanders); - }); - } -} diff --git a/lib/Extension/FilePathResolver/Tests/Unit/FilePathResolverExtensionTest.php b/lib/Extension/FilePathResolver/Tests/Unit/FilePathResolverExtensionTest.php deleted file mode 100644 index 4b8ce0d1d6..0000000000 --- a/lib/Extension/FilePathResolver/Tests/Unit/FilePathResolverExtensionTest.php +++ /dev/null @@ -1,103 +0,0 @@ -createResolver([ - ]); - - $this->assertStringContainsString('cache/phpactor', $resolver->resolve('%cache%')); - $this->assertStringContainsString('config/phpactor', $resolver->resolve('%config%')); - $this->assertStringContainsString('/phpactor', $resolver->resolve('%data%')); - $this->assertStringContainsString((string)getcwd(), $resolver->resolve('%project_root%')); - } - - public function testPathResolverWithApplicationRoot(): void - { - $resolver = $this->createResolver([ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__, - ]); - - $this->assertEquals(__DIR__, $resolver->resolve('%application_root%')); - } - - public function testProjectId(): void - { - $resolver = $this->createResolver([ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__, - FilePathResolverExtension::PARAM_PROJECT_ROOT => '/foobar/barfoo', - ]); - - $this->assertEquals('barfoo-2c52a9', $resolver->resolve('%project_id%')); - } - - public function testPathResolverLogging(): void - { - $resolver = $this->createResolver([ - FilePathResolverExtension::PARAM_ENABLE_LOGGING => true, - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__, - ]); - - $this->assertEquals(__DIR__, $resolver->resolve('%application_root%')); - } - - /** - * @param mixed $input - */ - #[DataProvider('provideProjectIdCalculate')] - public function testProjectIdCalculate($input, ?string $expectedId = null, ?string $expectedException = null): void - { - if ($expectedException) { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage($expectedException); - } - self::assertEquals($expectedId, FilePathResolverExtension::calculateProjectId($input)); - } - - /** - * @return Generator - */ - public static function provideProjectIdCalculate(): Generator - { - yield [ - false, - null, - 'Project root must be a non-empty string' - ]; - - yield [ - '/foobar', - 'foobar-1b9590', - ]; - - yield [ - 'file:///foobar', - 'foobar-1b9590', - ]; - } - - /** - * @param array $config - */ - public function createResolver(array $config): PathResolver - { - $container = PhpactorContainer::fromExtensions([ - FilePathResolverExtension::class, - LoggingExtension::class - ], $config); - - return $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - } -} diff --git a/lib/Extension/LanguageServer/CodeAction/ClosureCodeActionProvider.php b/lib/Extension/LanguageServer/CodeAction/ClosureCodeActionProvider.php deleted file mode 100644 index 1adc41cc1b..0000000000 --- a/lib/Extension/LanguageServer/CodeAction/ClosureCodeActionProvider.php +++ /dev/null @@ -1,36 +0,0 @@ -> $closure - */ - public function __construct(private Closure $closure) - { - } - - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return ($this->closure)($textDocument, $range, $cancel); - } - - public function kinds(): array - { - return []; - } - - public function describe(): string - { - return 'closure'; - } -} diff --git a/lib/Extension/LanguageServer/CodeAction/OutsourcedCodeActionProvider.php b/lib/Extension/LanguageServer/CodeAction/OutsourcedCodeActionProvider.php deleted file mode 100644 index 3811bd75ca..0000000000 --- a/lib/Extension/LanguageServer/CodeAction/OutsourcedCodeActionProvider.php +++ /dev/null @@ -1,133 +0,0 @@ - $command - */ - public function __construct( - private array $command, - private string $cwd, - private LoggerInterface $logger, - private CodeActionProvider $providerInfo, - private int $timeout = 5, - ) { - } - - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument, $range, $cancel) { - $process = new Process(array_merge([ - PHP_BINARY - ], $this->command, [ - json_encode(new CodeActionParams( - ProtocolFactory::textDocumentIdentifier($textDocument->uri), - $range, - new CodeActionContext([]), - ), JSON_THROW_ON_ERROR), - sprintf('--config-extra=%s', sprintf('{"%s": false}', WorseReflectionExtension::PARAM_ENABLE_CONTEXT_LOCATION)) - ]), $this->cwd); - - /** @var int $pid */ - $pid = yield $process->start(); - - ProcessUtil::killAfter($this->logger, $process, $this->timeout); - - $stdin = $process->getStdin(); - - asyncCall(function () use ($process, $cancel, $pid) { - while ($process->isRunning()) { - if ($cancel->isRequested()) { - $process->kill(); - $this->logger->info(sprintf( - 'Killing code-action process "%s" as requested', - $pid, - )); - } - yield delay(500); - } - }); - - try { - yield $stdin->write($textDocument->text); - $stdin->close(); - } catch (StreamException $exception) { - $this->logger->debug(sprintf( - 'Could not write to stdin: %s', - $exception->getMessage(), - )); - - return []; - } - - /** @var string $json */ - $json = yield buffer($process->getStdout()); - - try { - /** @var int $exitCode */ - $exitCode = yield $process->join(); - } catch (ProcessException $e) { - $this->logger->warning(sprintf( - 'Code action resolver took too long to analyse file or was culled to make way for a new request (timed-out after %s seconds)', - $this->timeout, - )); - return []; - } - if ($exitCode !== 0) { - /** @var string $stderr */ - $stderr = yield buffer($process->getStderr()); - - throw new RuntimeException(sprintf( - 'Phpactor code-action process exited with code "%s": %s', - $exitCode, - $stderr - )); - } - - $array = json_decode($json, true); - - if (!is_array($array)) { - throw new RuntimeException(sprintf( - 'Could not decode JSON: %s', - $json - )); - } - - /** @phpstan-ignore-next-line */ - return array_map(fn (array $codeAction) => CodeAction::fromArray($codeAction), $array); - }); - } - - public function kinds(): array - { - return $this->providerInfo->kinds(); - } - - public function describe(): string - { - return sprintf('outsourced: %s', $this->providerInfo->describe()); - } -} diff --git a/lib/Extension/LanguageServer/CodeAction/ProfilingCodeActionProvider.php b/lib/Extension/LanguageServer/CodeAction/ProfilingCodeActionProvider.php deleted file mode 100644 index b73ca22124..0000000000 --- a/lib/Extension/LanguageServer/CodeAction/ProfilingCodeActionProvider.php +++ /dev/null @@ -1,51 +0,0 @@ -innerProvider::class); - $this->logger->info(sprintf('PROF >> code-action [%s] %s', $shortName, $this->innerProvider->describe())); - try { - $result = yield $this->innerProvider->provideActionsFor($textDocument, $range, $cancel); - $elapsed = microtime(true) - $start; - } catch (Throwable $e) { - $elapsed = microtime(true) - $start; - $this->logger->info(sprintf('PROF %-6s << code-action [%s] ERR: [%s] %s (%s)', number_format($elapsed, 4), $shortName, $this->innerProvider->describe(), $e::class, $e->getMessage())); - throw $e; - } - $this->logger->info(sprintf('PROF %-6s << code-action [%s] %s', number_format($elapsed, 4), $shortName, $this->innerProvider->describe())); - return $result; - }); - } - - public function kinds(): array - { - return $this->innerProvider->kinds(); - } - - public function describe(): string - { - return $this->innerProvider->describe(); - } -} diff --git a/lib/Extension/LanguageServer/CodeAction/ThereCanOnlyBeOneCodeActionProvider.php b/lib/Extension/LanguageServer/CodeAction/ThereCanOnlyBeOneCodeActionProvider.php deleted file mode 100644 index 26e8344233..0000000000 --- a/lib/Extension/LanguageServer/CodeAction/ThereCanOnlyBeOneCodeActionProvider.php +++ /dev/null @@ -1,44 +0,0 @@ -cancel) { - $this->cancel->cancel(); - } - - $this->cancel = new CancellationTokenSource(); - - return $this->inner->provideActionsFor($textDocument, $range, new CombinedCancellationToken( - $cancel, - $this->cancel->getToken(), - )); - } - - public function kinds(): array - { - return $this->inner->kinds(); - } - - public function describe(): string - { - return $this->inner->describe(); - } -} diff --git a/lib/Extension/LanguageServer/CodeAction/TolerantCodeActionProvider.php b/lib/Extension/LanguageServer/CodeAction/TolerantCodeActionProvider.php deleted file mode 100644 index d0be97e69a..0000000000 --- a/lib/Extension/LanguageServer/CodeAction/TolerantCodeActionProvider.php +++ /dev/null @@ -1,54 +0,0 @@ -provider->provideActionsFor($textDocument, $range, $cancel); - } catch (Throwable $error) { - // if we are running in the main process the LS client API will be available - if (null !== $this->client) { - $this->client->window()->showMessage()->error(sprintf( - 'Provider %s (%s) failed: %s', - $this->provider::class, - $this->provider->describe(), - $error->getMessage(), - )); - return []; - } - - // otherwise we're probably running in a dedicated process, just throw an error and let it die. - throw $error; - } - }); - } - - public function kinds(): array - { - return $this->provider->kinds(); - } - - public function describe(): string - { - return $this->provider->describe(); - } -} diff --git a/lib/Extension/LanguageServer/Command/CodeActionsCommand.php b/lib/Extension/LanguageServer/Command/CodeActionsCommand.php deleted file mode 100644 index bca509276e..0000000000 --- a/lib/Extension/LanguageServer/Command/CodeActionsCommand.php +++ /dev/null @@ -1,83 +0,0 @@ -setDescription('Internal: resolve code-actions asynchronously'); - $this->addArgument(self::ARG_REQUEST, InputArgument::REQUIRED, 'Code action LSP request'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - /** @var string $request */ - $request = $input->getArgument(self::ARG_REQUEST); - - $array = json_decode($request, true, JSON_THROW_ON_ERROR); - if (!is_array($array)) { - throw new RuntimeException(sprintf( - 'Expected json to decode to an array, got "%s"', - get_debug_type($array) - )); - } - /** @phpstan-ignore argument.type */ - $request = CodeActionParams::fromArray($array); - $textDocumentItem = ProtocolFactory::textDocumentItem($request->textDocument->uri, $this->stdin()); - - // update the in-memory worse reflection workspace index so that we - // can locate the latest function and class definitions in this process. - $this->workspace->index(TextDocumentConverter::fromLspTextItem($textDocumentItem)); - - $diagnostics = wait( - $this->provider->provideActionsFor( - $textDocumentItem, - $request->range, - (new CancellationTokenSource())->getToken() - ) - ); - $decoded = json_encode($diagnostics); - if (false === $decoded) { - throw new RuntimeException( - 'Could not encode diagnostics', - ); - } - $output->write($decoded); - return 0; - } - - private function stdin(): string - { - $in = ''; - - while (false !== $line = fgets(STDIN)) { - $in .= $line; - } - - return $in; - } -} diff --git a/lib/Extension/LanguageServer/Command/DiagnosticsCommand.php b/lib/Extension/LanguageServer/Command/DiagnosticsCommand.php deleted file mode 100644 index b3c7f17040..0000000000 --- a/lib/Extension/LanguageServer/Command/DiagnosticsCommand.php +++ /dev/null @@ -1,70 +0,0 @@ -setDescription('Internal: resolve diagnostics in JSON for document provided over STDIN'); - $this->addOption(self::PARAM_URI, null, InputOption::VALUE_REQUIRED, 'The URL for the document provided over STDIN'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - /** @var string $uri */ - $uri = $input->getOption(self::PARAM_URI) ?: 'untitled:///new'; - - $textDocument = ProtocolFactory::textDocumentItem($uri, $this->stdin()); - - // update the in-memory worse reflection workspace index so that we - // can locate the latest function and class definitions in this process. - $this->workspace->index(TextDocumentConverter::fromLspTextItem($textDocument)); - - $diagnostics = wait( - $this->provider->provideDiagnostics($textDocument, (new CancellationTokenSource())->getToken()) - ); - $decoded = json_encode($diagnostics); - if (false === $decoded) { - throw new RuntimeException( - 'Could not encode diagnostics', - ); - } - $output->write($decoded); - return 0; - } - - private function stdin(): string - { - $in = ''; - - while (false !== $line = fgets(STDIN)) { - $in .= $line; - } - - return $in; - } -} diff --git a/lib/Extension/LanguageServer/Command/StartCommand.php b/lib/Extension/LanguageServer/Command/StartCommand.php deleted file mode 100644 index 8726e4ac58..0000000000 --- a/lib/Extension/LanguageServer/Command/StartCommand.php +++ /dev/null @@ -1,69 +0,0 @@ -setDescription('Start Language Server'); - $this->addOption(self::OPT_ADDRESS, null, InputOption::VALUE_REQUIRED, 'Start a TCP server at this address (e.g. 127.0.0.1:0)'); - $this->addOption(self::OPT_NO_LOOP, null, InputOption::VALUE_NONE, 'Do not run the event loop (debug)'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $address = $input->getOption(self::OPT_ADDRESS); - $noLoop = (bool)$input->getOption(self::OPT_NO_LOOP); - - $builder = $this->languageServerBuilder; - - $this->logMessage($output, 'Starting language server, use -vvv for verbose output'); - - if ($address && is_string($address)) { - $this->configureTcpServer($address, $builder); - } - - $server = $builder->build(); - - if ($noLoop) { - $server->start(); - return 0; - } - - $server->run(); - - return 0; - } - - private function configureTcpServer(string $address, LanguageServerBuilder $builder): void - { - assert(is_string($address)); - $builder->tcpServer($address); - } - - private function logMessage(OutputInterface $output, string $message): void - { - if ($output instanceof ConsoleOutput) { - $output->getErrorOutput()->writeln( - $message - ); - } - } -} diff --git a/lib/Extension/LanguageServer/Container/DiagnosticProviderTag.php b/lib/Extension/LanguageServer/Container/DiagnosticProviderTag.php deleted file mode 100644 index 63804bd12b..0000000000 --- a/lib/Extension/LanguageServer/Container/DiagnosticProviderTag.php +++ /dev/null @@ -1,21 +0,0 @@ - $name, - self::OUTSOURCE => $outsource, - ]; - } -} diff --git a/lib/Extension/LanguageServer/DiagnosticProvider/AggregateDiagnosticsProvider.php b/lib/Extension/LanguageServer/DiagnosticProvider/AggregateDiagnosticsProvider.php deleted file mode 100644 index a67c2e1fb0..0000000000 --- a/lib/Extension/LanguageServer/DiagnosticProvider/AggregateDiagnosticsProvider.php +++ /dev/null @@ -1,86 +0,0 @@ - - */ - private array $providers; - - private LoggerInterface $logger; - - public function __construct(LoggerInterface $logger, DiagnosticsProvider ...$providers) - { - $this->providers = $providers; - $this->logger = $logger; - } - - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument, $cancel) { - $diagnostics = []; - foreach ($this->providers as $provider) { - try { - $start = microtime(true); - $diagnostics = array_merge( - $diagnostics, - // if no code is provided in the diagnostic, set the - // code to be the provider name. - array_map(function (Diagnostic $diagnostic) use ($provider) { - if (null === $diagnostic->code) { - $diagnostic->code = $provider->name(); - } - return $diagnostic; - }, yield $provider->provideDiagnostics($textDocument, $cancel)) - ); - if ($cancel->isRequested()) { - $this->logger->info('Diagnostics cancelled'); - return $diagnostics; - } - $this->logger->debug(sprintf( - 'Diagnostic finsihed in "%s" (%s)', - number_format(microtime(true) - $start, 2), - get_class($provider) - )); - } catch (Throwable $throwable) { - $this->logger->error(sprintf( - 'Diagnostic error from provider "%s": %s', - get_class($provider), - $throwable->getMessage() - ), [ - 'trace' => $throwable->getTraceAsString() - ]); - } - } - - return $diagnostics; - }); - } - - /** - * @return list - */ - public function names(): array - { - return array_map( - fn (DiagnosticsProvider $provider) => $provider->name(), - $this->providers - ); - } - - public function name(): string - { - return implode(', ', $this->names()); - } -} diff --git a/lib/Extension/LanguageServer/DiagnosticProvider/CodeFilteringDiagnosticProvider.php b/lib/Extension/LanguageServer/DiagnosticProvider/CodeFilteringDiagnosticProvider.php deleted file mode 100644 index aa329bae82..0000000000 --- a/lib/Extension/LanguageServer/DiagnosticProvider/CodeFilteringDiagnosticProvider.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ - private array $ignoreCodes; - - /** - * @param list $ignoreCodes - */ - public function __construct( - private DiagnosticsProvider $innerProvider, - array $ignoreCodes - ) { - $this->ignoreCodes = array_flip($ignoreCodes); - } - - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return call(function () use ($cancel, $textDocument) { - return array_values(array_filter(yield $this->innerProvider->provideDiagnostics($textDocument, $cancel), function (Diagnostic $diagnostic) { - return null === $diagnostic->code || !array_key_exists( - $diagnostic->code, - $this->ignoreCodes - ); - })); - }); - } - - public function name(): string - { - return $this->innerProvider->name(); - } -} diff --git a/lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php b/lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php deleted file mode 100644 index db582fe5e8..0000000000 --- a/lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php +++ /dev/null @@ -1,105 +0,0 @@ - $command - */ - public function __construct( - private array $command, - private string $cwd, - private LoggerInterface $logger, - private int $timeout = 5, - ) { - } - - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument, $cancel) { - $process = new Process(array_merge([PHP_BINARY], $this->command, [ - '--uri=' . $textDocument->uri, - sprintf('--config-extra=%s', sprintf('{"%s": false}', WorseReflectionExtension::PARAM_ENABLE_CONTEXT_LOCATION)) - ]), $this->cwd); - $pid = yield $process->start(); - assert(is_int($pid)); - - ProcessUtil::killAfter($this->logger, $process, $this->timeout); - - $stdin = $process->getStdin(); - - asyncCall(function () use ($process, $cancel, $pid) { - while ($process->isRunning()) { - if ($cancel->isRequested()) { - $process->kill(); - $this->logger->info(sprintf( - 'Killing diagnostics process "%s" as requested', - $pid, - )); - } - yield delay(500); - } - }); - - try { - yield $stdin->write($textDocument->text); - $stdin->close(); - } catch (StreamException $exception) { - $this->logger->debug(sprintf( - 'Could not write to stdin: %s', - $exception->getMessage(), - )); - - return []; - } - - $json = yield buffer($process->getStdout()); - - try { - $exitCode = yield $process->join(); - } catch (ProcessException $e) { - $this->logger->warning($e->getMessage()); - return []; - } - if ($exitCode !== 0) { - throw new RuntimeException(sprintf( - 'Phpactor diagnostics process exited with code "%s": %s', - $exitCode, - yield buffer($process->getStderr()) - )); - } - $array = json_decode($json, true); - if (!is_array($array)) { - throw new RuntimeException(sprintf( - 'Could not decode JSON: %s', - $json - )); - } - - return array_map(fn (array $diagnostic) => Diagnostic::fromArray($diagnostic), $array); - }); - } - - public function name(): string - { - return 'outsourced'; - } -} diff --git a/lib/Extension/LanguageServer/DiagnosticProvider/PathExcludingDiagnosticsProvider.php b/lib/Extension/LanguageServer/DiagnosticProvider/PathExcludingDiagnosticsProvider.php deleted file mode 100644 index 8192603729..0000000000 --- a/lib/Extension/LanguageServer/DiagnosticProvider/PathExcludingDiagnosticsProvider.php +++ /dev/null @@ -1,38 +0,0 @@ - $paths - */ - public function __construct( - private DiagnosticsProvider $innerProvider, - private array $paths - ) { - } - - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - foreach ($this->paths as $glob) { - if (true === Glob::match(TextDocumentUri::fromString($textDocument->uri)->path(), $glob)) { - return new Success([]); - } - } - return $this->innerProvider->provideDiagnostics($textDocument, $cancel); - } - - public function name(): string - { - return $this->innerProvider->name(); - } -} diff --git a/lib/Extension/LanguageServer/Dispatcher/PhpactorDispatcherFactory.php b/lib/Extension/LanguageServer/Dispatcher/PhpactorDispatcherFactory.php deleted file mode 100644 index ae66d0337c..0000000000 --- a/lib/Extension/LanguageServer/Dispatcher/PhpactorDispatcherFactory.php +++ /dev/null @@ -1,143 +0,0 @@ -createContainer($initializeParams, $transmitter); - return $this->createContainer( - $initializeParams, - $transmitter - )->get(MiddlewareDispatcher::class); - } - - protected function createContainer(InitializeParams $params, MessageTransmitter $transmitter): Container - { - $container = $this->container; - $parameters = $container->getParameters(); - $parameters[FilePathResolverExtension::PARAM_PROJECT_ROOT] = TextDocumentUri::fromString( - $this->resolveRootUri($params) - )->path(); - - if (isset($parameters[WorseReflectionExtension::PARAM_ENABLE_CONTEXT_LOCATION])) { - $parameters[WorseReflectionExtension::PARAM_ENABLE_CONTEXT_LOCATION] = false; - } - - $extensionClasses = $container->getParameter( - PhpactorContainer::PARAM_EXTENSION_CLASSES - ); - - // merge in any language-server specific configuration - /** @var array $sessionParameters */ - $sessionParameters = $container->getParameter(LanguageServerExtension::PARAM_SESSION_PARAMETERS); - $parameters = array_merge($parameters, $sessionParameters); - - $container = $this->buildContainer( - /** @phpstan-ignore-next-line */ - $extensionClasses, - /** @phpstan-ignore-next-line */ - array_merge($parameters, $params->initializationOptions ?? []), - $transmitter, - $params - ); - - return $container; - } - /** - * @param list $extensionClasses - * @param array $parameters - */ - private function buildContainer( - array $extensionClasses, - array $parameters, - MessageTransmitter $transmitter, - InitializeParams $params - ): Container { - $container = new PhpactorContainer(); - - $extensions = array_map(function (string $class): Extension { - /** @var Extension $class */ - return new $class(); - }, $extensionClasses); - $extensions[] = new LanguageServerSessionExtension($transmitter, $params); - - $resolver = new Resolver(true); - $resolver->setDefaults([ - PhpactorContainer::PARAM_EXTENSION_CLASSES => $extensionClasses - ]); - foreach ($extensions as $extension) { - // This is duplicated in ExtensionDocumentor we should not - // continue to add behavior like this here and should extract - // this and other special logic. - if ($extension instanceof OptionalExtension) { - (function (string $key) use ($resolver): void { - $resolver->setDefaults([$key => false]); - $resolver->setTypes([$key => 'boolean']); - })(sprintf('%s.enabled', $extension->name())); - } - $extension->configure($resolver); - } - - $parameters = $resolver->resolve($parameters); - - (function () use ($container, $resolver): void { - // the validation probably already happened in the parent container and - // the invalid keys would have been removed, meaning the above resolver will - // always be valid - if ($this->container->has(ResolverErrors::class)) { - $container->register(ResolverErrors::class, fn () => $this->container->get(ResolverErrors::class)); - return; - } - - // otherwise use the errors here (this really only happens in tests) - $container->register(ResolverErrors::class, fn () => $resolver->errors()); - })(); - - foreach ($extensions as $extension) { - if ($extension instanceof OptionalExtension) { - if (false === ($parameters[sprintf('%s.enabled', $extension->name())] ?? false)) { - continue; - } - } - $extension->load($container); - } - - return $container->build($parameters); - } - - private function resolveRootUri(InitializeParams $params): string - { - if (null === $params->rootUri) { - throw new ExitSession( - 'Phpactor Language Server must be initialized with a root URI, NULL provided' - ); - } - - // root URI is url encoded, decode it! - return urldecode($params->rootUri); - } -} diff --git a/lib/Extension/LanguageServer/EventDispatcher/LazyAggregateProvider.php b/lib/Extension/LanguageServer/EventDispatcher/LazyAggregateProvider.php deleted file mode 100644 index 5e0e882aa0..0000000000 --- a/lib/Extension/LanguageServer/EventDispatcher/LazyAggregateProvider.php +++ /dev/null @@ -1,53 +0,0 @@ - $serviceIds - */ - public function __construct( - private ContainerInterface $container, - private array $serviceIds - ) { - } - - /** - * @return iterable - */ - public function getListenersForEvent(object $event): iterable - { - if (null === $this->aggregateProvider) { - $this->aggregateProvider = new ListenerProviderAggregate(); - foreach ($this->serviceIds as $serviceId) { - /** @var object|null $listenerProvider */ - $listenerProvider = $this->container->get($serviceId); - - // if null assume that it was conditionally disabled - if (null === $listenerProvider) { - continue; - } - - if (!$listenerProvider instanceof ListenerProviderInterface) { - throw new RuntimeException(sprintf( - 'Listener service with ID "%s" must implement ListenerProviderInterface, it is of class "%s"', - $serviceId, - get_class($listenerProvider) - )); - } - - $this->aggregateProvider->attach($listenerProvider); - } - } - - return $this->aggregateProvider->getListenersForEvent($event); - } -} diff --git a/lib/Extension/LanguageServer/Handler/DebugHandler.php b/lib/Extension/LanguageServer/Handler/DebugHandler.php deleted file mode 100644 index fa2ba34f6c..0000000000 --- a/lib/Extension/LanguageServer/Handler/DebugHandler.php +++ /dev/null @@ -1,158 +0,0 @@ - 'dumpConfig', - self::METHOD_DEBUG_WORKSPACE => 'dumpWorkspace', - self::METHOD_DEBUG_STATUS => 'status' - ]; - } - - /** - * @return Promise - */ - public function dumpConfig(bool $return = false): Promise - { - $message = [ - 'Config Dump', - '===========', - '', - 'File Paths', - '----------', - '', - ]; - - $this->dumpExpanders($message); - - $message[] = ''; - $message[] = 'Config'; - $message[] = '------'; - - - $json = (string)json_encode($this->container->getParameters(), JSON_PRETTY_PRINT); - $message[] = $json; - - if ($return) { - return new Success($json); - } - - $this->client->window()->logMessage()->info(implode("\n", $message)); - return new Success(null); - } - - /** - * @return Promise - */ - public function dumpWorkspace(): Promise - { - $info = []; - foreach ($this->workspace as $document) { - assert($document instanceof TextDocumentItem); - $info[] = sprintf('// %s', $document->uri); - $info[] = '-----------------'; - $info[] = $document->text; - } - - $this->client->window()->logMessage()->info(implode("\n", $info)); - - return new Success(null); - } - - /** - * @return Promise - */ - public function status(): Promise - { - $info = [ - 'Process', - '-------', - '', - ' cwd:' . getcwd(), - ' pid: ' . getmypid(), - ' up: ' . $this->stats->uptime()->format('%ad %hh %im %ss'), - '', - 'Server', - '------', - '', - // ' connections: ' . $this->stats->connectionCount(), - // ' requests: ' . $this->stats->requestCount(), - sprintf(' version: %s', Phpactor::version()), - ' mem: ' . number_format(memory_get_peak_usage()) . 'b', - ' documents: ' . $this->workspace->count(), - ' services: ' . (string)json_encode($this->serviceManager->runningServices()), - ' diagnostics: ' . (string)$this->diagnosticProvider->name(), - '', - 'Paths', - '-----', - '', - ]; - - $this->dumpExpanders($info); - $info[] = ''; - - foreach ($this->statusProviders as $provider) { - $info[] = $provider->title(); - $info[] = str_repeat('-', mb_strlen($provider->title())); - $info[] = ''; - foreach ($provider->provide() as $key => $value) { - $info[] = sprintf(' %s: %s', $key, $value); - } - } - - return new Success(implode("\n", $info)); - } - - /** - * @param array $output - */ - private function dumpExpanders(array &$output): void - { - foreach ( - $this->container->expect( - FilePathResolverExtension::SERVICE_EXPANDERS, - Expanders::class - )->toArray() as $tokenName => $value - ) { - $output[] = sprintf(' %s: %s', $tokenName, $value); - } - } -} diff --git a/lib/Extension/LanguageServer/LanguageServerExtension.php b/lib/Extension/LanguageServer/LanguageServerExtension.php deleted file mode 100644 index 312fb99d83..0000000000 --- a/lib/Extension/LanguageServer/LanguageServerExtension.php +++ /dev/null @@ -1,781 +0,0 @@ -setDefaults([ - self::PARAM_CATCH_ERRORS => true, - self::PARAM_ENABLE_WORKPACE => true, - self::PARAM_SESSION_PARAMETERS => [], - self::PARAM_METHOD_ALIAS_MAP => [], - self::PARAM_DIAGNOSTIC_SLEEP_TIME => 1000, - self::PARAM_DIAGNOSTIC_ON_UPDATE => true, - self::PARAM_DIAGNOSTIC_ON_SAVE => true, - self::PARAM_DIAGNOSTIC_ON_OPEN => true, - self::PARAM_DIAGNOSTIC_PROVIDERS => null, - self::PARAM_DIAGNOSTIC_OUTSOURCE => true, - self::PARAM_CODE_ACTION_OUTSOURCE => true, - self::PARAM_DIAGNOSTIC_EXCLUDE_PATHS => [], - self::PARAM_DIAGNOSTIC_IGNORE_CODES => [], - self::PARAM_ENABLE_TRUST_CHECK => true, - self::PARAM_FILE_EVENTS => true, - self::PARAM_FILE_EVENT_GLOBS => ['**/*.php'], - self::PARAM_PROFILE => false, - self::PARAM_TRACE => false, - self::PARAM_SHUTDOWN_GRACE_PERIOD => 200, - self::PARAM_PHPACTOR_BIN => __DIR__ . '/../../../bin/phpactor', - self::PARAM_SELF_DESTRUCT_TIMEOUT => 2500, - self::PARAM_DIAGNOSTIC_OUTSOURCE_TIMEOUT => 5, - ]); - $schema->setDescriptions([ - self::PARAM_ENABLE_TRUST_CHECK => 'Check to see if project path is trusted before loading configurations from it', - self::PARAM_TRACE => 'Log incoming and outgoing messages (needs log formatter to be set to ``json``)', - self::PARAM_PROFILE => 'Logs timing information for incoming LSP requests', - self::PARAM_METHOD_ALIAS_MAP => 'Allow method names to be re-mapped. Useful for maintaining backwards compatibility', - self::PARAM_SESSION_PARAMETERS => 'Phpactor parameters (config) that apply only to the language server session', - self::PARAM_ENABLE_WORKPACE => <<<'EOT' - If workspace management / text synchronization should be enabled (this isn't required for some language server implementations, e.g. static analyzers) - EOT - , - self::PARAM_DIAGNOSTIC_SLEEP_TIME => 'Amount of time to wait before analyzing the code again for diagnostics', - self::PARAM_DIAGNOSTIC_ON_UPDATE => 'Perform diagnostics when the text document is updated', - self::PARAM_DIAGNOSTIC_ON_SAVE => 'Perform diagnostics when the text document is saved', - self::PARAM_DIAGNOSTIC_ON_OPEN => 'Perform diagnostics when opening a text document', - self::PARAM_DIAGNOSTIC_PROVIDERS => 'Specify which diagnostic providers should be active (default to all)', - self::PARAM_DIAGNOSTIC_OUTSOURCE => 'If applicable diagnostics should be "outsourced" to a different process', - self::PARAM_CODE_ACTION_OUTSOURCE => 'Code actions will be "outsourced" to a different process', - self::PARAM_DIAGNOSTIC_OUTSOURCE_TIMEOUT => 'Kill the diagnostics or code action processes if they outlive this timeout', - self::PARAM_DIAGNOSTIC_IGNORE_CODES => 'Ignore diagnostics that have the codes listed here, e.g. ["fix_namespace_class_name"]. The codes match those shown in the LSP client.', - self::PARAM_FILE_EVENTS => 'Register to receive file events', - self::PARAM_DIAGNOSTIC_EXCLUDE_PATHS => 'List of paths to exclude from diagnostics, e.g. `vendor/**/*`', - self::PARAM_SHUTDOWN_GRACE_PERIOD => 'Amount of time (in milliseconds) to wait before responding to a shutdown notification', - self::PARAM_SELF_DESTRUCT_TIMEOUT => 'Wait this amount of time (in milliseconds) after a shutdown request before self-destructing', - self::PARAM_PHPACTOR_BIN => 'Internal use only - name path to Phpactor binary', - ]); - } - - - public function load(ContainerBuilder $container): void - { - $this->registerServer($container); - $this->registerCommand($container); - $this->registerSession($container); - $this->registerEventDispatcher($container); - $this->registerCommandDispatcher($container); - $this->registerServiceManager($container); - $this->registerMiddleware($container); - $this->registerDiagnostics($container); - $this->registerHandlers($container); - $this->registerServices($container); - $this->registerTelemetry($container); - } - - private function registerServer(ContainerBuilder $container): void - { - $container->register(ServerStats::class, function (Container $container) { - return new ServerStats(); - }); - - $container->register(LanguageServerBuilder::class, function (Container $container) { - $builder = LanguageServerBuilder::create( - new PhpactorDispatcherFactory($container), - $this->logger($container) - ); - - return $builder; - }); - - $container->register(ClientLogger::class, function (Container $container) { - return new ClientLogger( - $container->get(ClientApi::class), - $this->logger($container), - ); - }); - } - - private function registerCommand(ContainerBuilder $container): void - { - if (!class_exists(ConsoleExtension::class)) { - return; - } - - $container->register('language_server.command.lsp_start', function (Container $container) { - return new StartCommand($container->get(LanguageServerBuilder::class)); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => StartCommand::NAME ]]); - - $container->register(DiagnosticsCommand::class, function (Container $container) { - /** @var AggregateDiagnosticsProvider $provider */ - $provider = $container->get(AggregateDiagnosticsProvider::class . '.outsourced'); - - return new DiagnosticsCommand( - $provider, - $container->get(WorkspaceIndex::class), - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => DiagnosticsCommand::NAME ]]); - - $container->register(CodeActionsCommand::class, function (Container $container) { - $provider = $container->get(AggregateCodeActionProvider::class); - - return new CodeActionsCommand( - $provider, - $container->get(WorkspaceIndex::class), - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => CodeActionsCommand::NAME ]]); - } - - private function registerSession(ContainerBuilder $container): void - { - $container->register(self::SERVICE_SESSION_WORKSPACE, function (Container $container) { - return new Workspace($this->logger($container)); - }); - - $container->register(WorkspaceListener::class, function (Container $container) { - if ($container->parameter(self::PARAM_ENABLE_WORKPACE)->bool() === false) { - return null; - } - - return new WorkspaceListener($this->workspace($container)); - }, [ - self::TAG_LISTENER_PROVIDER => [], - ]); - - $container->register(InvalidConfigListener::class, function (Container $container) { - return new InvalidConfigListener( - $container->get(ClientApi::class), - $container->has(ResolverErrors::class) ? $container->get(ResolverErrors::class) : new ResolverErrors([]) - ); - }, [ - self::TAG_LISTENER_PROVIDER => [], - ]); - - $container->register(ProjectConfigTrustListener::class, function (Container $container) { - if (false === $container->parameter(self::PARAM_ENABLE_TRUST_CHECK)->bool()) { - return null; - } - return new ProjectConfigTrustListener( - $container->get(ClientApi::class), - $container->parameter(PhpactorCoreExtension::PARAM_PROJECT_CONFIG_CANDIDATES)->listOfString(), - /** @phpstan-ignore argument.type */ - $container->parameter(PhpactorCoreExtension::PARAM_TRUST)->value(), - ); - }, [ - self::TAG_LISTENER_PROVIDER => [], - ]); - - $container->register(SelfDestructListener::class, function (Container $container) { - return new SelfDestructListener($container->parameter(self::PARAM_SELF_DESTRUCT_TIMEOUT)->int()); - }, [ - self::TAG_LISTENER_PROVIDER => [], - ]); - - $container->register(DidChangeWatchedFilesListener::class, function (Container $container) { - return new DidChangeWatchedFilesListener( - $container->get(ClientApi::class), - /** @phpstan-ignore-next-line */ - $container->parameter(self::PARAM_FILE_EVENT_GLOBS)->value(), - $container->get(ClientCapabilities::class), - ); - }, [ - self::TAG_LISTENER_PROVIDER => [], - ]); - - $container->register('language_server.session.handler.session', function (Container $container) { - $providers = []; - foreach ($container->getServiceIdsForTag(self::TAG_STATUS_PROVIDER) as $serviceId => $_) { - $providers[] = $container->get($serviceId); - } - return new DebugHandler( - $container, - $container->get(ClientApi::class), - $this->workspace($container), - $container->get(ServerStats::class), - $container->get(ServiceManager::class), - $container->get(AggregateDiagnosticsProvider::class), - $providers - ); - }, [ self::TAG_METHOD_HANDLER => []]); - - $container->register(ServiceHandler::class, function (Container $container) { - return new ServiceHandler($container->get(ServiceManager::class), $container->get(ClientApi::class)); - }, [ self::TAG_METHOD_HANDLER => []]); - - $container->register(CommandHandler::class, function (Container $container) { - return new CommandHandler($container->get(CommandDispatcher::class)); - }, [ self::TAG_METHOD_HANDLER => []]); - - $container->register(DidChangeWatchedFilesHandler::class, function (Container $container) { - return new DidChangeWatchedFilesHandler($container->get(EventDispatcherInterface::class)); - }, [ - self::TAG_METHOD_HANDLER => [], - ]); - } - - private function registerEventDispatcher(ContainerBuilder $container): void - { - $container->register(EventDispatcherInterface::class, function (Container $container) { - $aggregate = new LazyAggregateProvider( - $container, - $this->resolveListeners($container) - ); - - return new EventDispatcher($aggregate); - }); - } - - private function registerCommandDispatcher(ContainerBuilder $container): void - { - $container->register(CommandDispatcher::class, function (Container $container) { - $map = []; - foreach ($container->getServiceIdsForTag(self::TAG_COMMAND) as $serviceId => $attrs) { - if (!isset($attrs['name'])) { - throw new RuntimeException(sprintf( - 'Cannot register command with service ID "%s" Each command must define a "name" attribute', - $serviceId - )); - } - assert(is_string($attrs['name'])); - $map[$attrs['name']] = $container->get($serviceId); - } - - return new CommandDispatcher($map); - }); - } - - private function registerServiceManager(ContainerBuilder $container): void - { - $container->register(ServiceListener::class, function (Container $container) { - return new ServiceListener($container->get(ServiceManager::class)); - }, [ - self::TAG_LISTENER_PROVIDER => [], - ]); - - $container->register(ServiceManager::class, function (Container $container) { - return new ServiceManager( - $container->get(ServiceProviders::class), - $container->get(ClientLogger::class) - ); - }); - $container->register(ServiceProviders::class, function (Container $container) { - $providers = []; - foreach ($container->getServiceIdsForTag(self::TAG_SERVICE_PROVIDER) as $serviceId => $attrs) { - $provider = $container->get($serviceId); - if (!$provider instanceof ServiceProvider) { - throw new RuntimeException(sprintf( - 'Tagged service provider "%s" does not implement ServiceProvider interface, is a "%s"', - $serviceId, - get_debug_type($provider), - )); - } - $providers[] = $provider; - } - - return new ServiceProviders(...$providers); - }); - } - - private function registerMiddleware(ContainerBuilder $container): void - { - $container->register(MiddlewareDispatcher::class, function (Container $container) { - $stack = []; - - if ($container->parameter(self::PARAM_PROFILE)->bool()) { - $stack[] = new ProfilerMiddleware($this->logger($container)); - } - - foreach ($container->getServiceIdsForTag(self::TAG_MIDDLEWARE) as $serviceId => $_) { - $service = $container->get($serviceId); - if (null === $service) { - continue; - } - $stack[] = $service; - } - - if ($container->parameter(self::PARAM_TRACE)->bool()) { - $stack[] = new TraceMiddleware($this->logger($container)); - } - - if ($container->parameter(self::PARAM_CATCH_ERRORS)->bool()) { - $stack[] = new ErrorHandlingMiddleware($this->logger($container)); - } - - $stack[] = new InitializeMiddleware( - $container->get(Handlers::class), - $container->get(EventDispatcherInterface::class), - $this->serverInfo() - ); - - $stack[] = new ShutdownMiddleware($container->get(EventDispatcherInterface::class), $container->parameter(self::PARAM_SHUTDOWN_GRACE_PERIOD)->int()); - $stack[] = new CancellationMiddleware($container->get(MethodRunner::class)); - - /** @phpstan-ignore-next-line*/ - $stack[] = new MethodAliasMiddleware($container->parameter(self::PARAM_METHOD_ALIAS_MAP)->value()); - $stack[] = new ResponseHandlingMiddleware($container->get(ResponseWatcher::class)); - - $stack[] = new HandlerMiddleware( - $container->get(MethodRunner::class) - ); - - - return new MiddlewareDispatcher(...$stack); - }); - } - - private function registerHandlers(ContainerBuilder $container): void - { - $container->register(ArgumentResolver::class, function (Container $container) { - return new ChainArgumentResolver( - new LanguageSeverProtocolParamsResolver(), - new DTLArgumentResolver(), - ); - }); - $container->register(MethodRunner::class, function (Container $container) { - return new HandlerMethodRunner( - $container->get(Handlers::class), - $container->get(ArgumentResolver::class), - $this->logger($container) - ); - }); - - $container->register(Handlers::class, function (Container $container) { - $handlers = []; - - foreach (array_keys( - $container->getServiceIdsForTag(LanguageServerExtension::TAG_METHOD_HANDLER) - ) as $serviceId) { - $handler = $container->get($serviceId); - if (null === $handler) { - continue; - } - $handlers[] = $handler; - } - - return new Handlers(...$handlers); - }); - - $container->register(TextDocumentHandler::class, function (Container $container) { - return new TextDocumentHandler($container->get(EventDispatcherInterface::class)); - }, [ self::TAG_METHOD_HANDLER => []]); - - $container->register(StatsHandler::class, function (Container $container) { - return new StatsHandler( - $container->get(ClientApi::class), - $container->get(ServerStats::class) - ); - }, [ self::TAG_METHOD_HANDLER => []]); - - $container->register(CodeActionHandler::class, function (Container $container) { - if ($container->parameter(self::PARAM_CODE_ACTION_OUTSOURCE)->bool()) { - $provider = $container->get(OutsourcedCodeActionProvider::class); - } else { - $provider = $container->get(AggregateCodeActionProvider::class); - } - - return new CodeActionHandler( - new ThereCanOnlyBeOneCodeActionProvider($provider), - /** @phpstan-ignore-next-line */ - $this->workspace($container), - $container->get(ProgressNotifier::class), - ); - }, [ self::TAG_METHOD_HANDLER => []]); - - $container->register(AggregateCodeActionProvider::class, function (Container $container) { - /** @var SplPriorityQueue $services */ - $services = new SplPriorityQueue(); - $profile = $container->parameter(self::PARAM_PROFILE)->bool(); - foreach ($container->getServiceIdsForTag(self::TAG_CODE_ACTION_PROVIDER) as $serviceId => $attributes) { - $provider = new TolerantCodeActionProvider( - $container->expect($serviceId, CodeActionProvider::class), - $container->has(ClientApi::class) ? $container->get(ClientApi::class) : null, - ); - if ($profile) { - $provider = new ProfilingCodeActionProvider($provider, $this->logger($container)); - } - - /** @var int $prio */ - $prio = $attributes['priority'] ?? 0; - $services->insert($provider, $prio); - } - - return new AggregateCodeActionProvider(...$services); - }); - $container->register(OutsourcedCodeActionProvider::class, function (Container $container) { - /** @var PathResolver $resolver */ - $resolver = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER); - $projectPath = $resolver->resolve('%project_root%'); - - return new OutsourcedCodeActionProvider( - [ - $container->parameter(self::PARAM_PHPACTOR_BIN)->string(), - 'language-server:code-action' - ], - $projectPath, - $this->logger($container), - $container->get(AggregateCodeActionProvider::class), - $container->parameter(self::PARAM_DIAGNOSTIC_OUTSOURCE_TIMEOUT)->int(), - ); - }, [ - ]); - - $container->register(FormattingHandler::class, function (Container $container) { - $formatter = null; - foreach ($container->getServiceIdsForTag(self::TAG_FORMATTER) as $seviceId => $_) { - $formatter = $container->get($seviceId); - if (null === $formatter) { - continue; - } - break; - } - - if ($formatter === null) { - return null; - } - - return new FormattingHandler( - $this->workspace($container), - $formatter, - $container->get(ProgressNotifier::class), - ); - }, [ - LanguageServerExtension::TAG_METHOD_HANDLER => [ - ], - ]); - } - - private function registerServices(ContainerBuilder $container): void - { - $container->register(DiagnosticsService::class, function (Container $container) { - return new DiagnosticsService( - $container->get(DiagnosticsEngine::class), - $container->parameter(self::PARAM_DIAGNOSTIC_ON_UPDATE)->bool(), - $container->parameter(self::PARAM_DIAGNOSTIC_ON_SAVE)->bool(), - $this->workspace($container), - true, - $container->parameter(self::PARAM_DIAGNOSTIC_ON_OPEN)->bool() - ); - }, [ - self::TAG_SERVICE_PROVIDER => [], - self::TAG_LISTENER_PROVIDER => [], - ]); - } - - private function registerDiagnostics(ContainerBuilder $container): void - { - $container->register(DiagnosticsEngine::class, function (Container $container) { - $providers = $this->collectDiagnosticProviders( - $container, - outsourced: $container->parameter(self::PARAM_DIAGNOSTIC_OUTSOURCE)->bool() ? false : null, - ); - - $projectRoot = $container->parameter(FilePathResolverExtension::PARAM_PROJECT_ROOT)->string(); - - /** - * @var string[] $excludePaths - */ - $excludePaths = $container->parameter(self::PARAM_DIAGNOSTIC_EXCLUDE_PATHS)->value(); - - if (count($excludePaths)) { - $providers = array_map(function (DiagnosticsProvider $provider) use ($projectRoot, $excludePaths) { - return new PathExcludingDiagnosticsProvider( - $provider, - // make all the exclude paths absolute before passing to the provider - array_map(fn (string $path) => Path::join($projectRoot, $path), $excludePaths) - ); - }, $providers); - } - - $ignoreCodes = $container->parameter(self::PARAM_DIAGNOSTIC_IGNORE_CODES)->listOfString(); - - if (count($ignoreCodes)) { - $providers = array_map(function (DiagnosticsProvider $provider) use ($ignoreCodes) { - return new CodeFilteringDiagnosticProvider( - $provider, - $ignoreCodes, - ); - }, $providers); - } - - return new DiagnosticsEngine( - $container->get(ClientApi::class), - $this->logger($container, 'LSPDIAG'), - $providers, - $container->parameter(self::PARAM_DIAGNOSTIC_SLEEP_TIME)->int() - ); - }); - - $container->register(AggregateDiagnosticsProvider::class, function (Container $container) { - $providers = $this->collectDiagnosticProviders( - $container, - outsourced: $container->parameter(self::PARAM_DIAGNOSTIC_OUTSOURCE)->bool() ? false : null, - ); - - return new AggregateDiagnosticsProvider( - $this->logger($container, 'LSPDIAG'), - ...array_values($providers) - ); - }); - - $container->register(AggregateDiagnosticsProvider::class.'.outsourced', function (Container $container) { - $providers = $this->collectDiagnosticProviders($container, true); - - return new AggregateDiagnosticsProvider( - $this->logger($container, 'OUTLSPDIAG'), - ...array_values($providers) - ); - }); - - $container->register(CodeActionDiagnosticsProvider::class, function (Container $container) { - return new CodeActionDiagnosticsProvider( - ...$this->taggedServices($container, self::TAG_CODE_ACTION_DIAGNOSTICS_PROVIDER, CodeActionProvider::class) - ); - }, [ - self::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('code-action', outsource: true), - ]); - - $container->register(OutsourcedDiagnosticsProvider::class, function (Container $container) { - /** @var PathResolver $resolver */ - $resolver = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER); - $projectPath = $resolver->resolve('%project_root%'); - // only register this if we should call out to an external process for diagnostics - if (!$container->parameter(self::PARAM_DIAGNOSTIC_OUTSOURCE)->bool()) { - return null; - } - - return new OutsourcedDiagnosticsProvider([ - $container->parameter(self::PARAM_PHPACTOR_BIN)->string(), - 'language-server:diagnostics', - ], $projectPath, $this->logger($container), $container->parameter(self::PARAM_DIAGNOSTIC_OUTSOURCE_TIMEOUT)->int()); - }, [ - self::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('outsourced'), - ]); - } - - /** - * @template TType - * @param null|class-string $fqn - * @return ($fqn is class-string ? list : list) - */ - private function taggedServices(Container $container, string $tag, ?string $fqn = null): array - { - $providers = []; - foreach (array_keys($container->getServiceIdsForTag($tag)) as $serviceId) { - $providers[] = $container->get($serviceId); - } - return $providers; - } - - /** - * @return string[] - */ - private function resolveListeners(Container $container): array - { - return array_filter(array_keys($container->getServiceIdsForTag(self::TAG_LISTENER_PROVIDER)), function (string $service) use ($container) { - if (false === $container->parameter(self::PARAM_FILE_EVENTS)->bool() && $service === DidChangeWatchedFilesListener::class) { - return false; - } - return true; - }); - } - - private function logger(Container $container, string $name = self::LOG_CHANNEL): LoggerInterface - { - return LoggingExtension::channelLogger($container, $name); - } - - private function workspace(Container $container): Workspace - { - return $container->expect(self::SERVICE_SESSION_WORKSPACE, Workspace::class); - } - - /** - * @return array{name:string,version:string,version:string} - */ - private function serverInfo(): array - { - $package = InstalledVersions::getRootPackage(); - return [ - 'name' => $package['name'], - 'version' => $package['pretty_version'], - ]; - } - - /** - * @return DiagnosticsProvider[] - */ - private function collectDiagnosticProviders(Container $container, ?bool $outsourced): array - { - $providers = []; - foreach ($container->getServiceIdsForTag(self::TAG_DIAGNOSTICS_PROVIDER) as $serviceId => $attrs) { - Assert::isArray($attrs, 'Attributes must be an array, got "%s"'); - - if (null !== $outsourced && ($attrs[DiagnosticProviderTag::OUTSOURCE] ?? false) !== $outsourced) { - continue; - } - - $provider = $container->get($serviceId); - - if (null === $provider) { - continue; - } - - $providers[$attrs[DiagnosticProviderTag::NAME] ?? $serviceId] = $provider; - } - - $enabled = $container->getParameter(self::PARAM_DIAGNOSTIC_PROVIDERS); - - if (null !== $enabled) { - Assert::isArray($enabled); - - if ($diff = array_diff($enabled, array_keys($providers))) { - throw new RuntimeException(sprintf( - 'Unknown diagnostic provider(s) "%s", known providers: "%s"', - implode('", "', $diff), - implode('", "', array_keys($providers)) - )); - } - $providers = array_intersect_key($providers, array_flip($enabled)); - } - - /** @var DiagnosticsProvider[] $providers */ - return $providers; - } - - private function registerTelemetry(ContainerBuilder $container): void - { - $container->register(LanguageServerTelemetry::class, function (Container $container) { - return new LanguageServerTelemetry(); - }, [OpenTelemetryExtension::TAG_HOOK_PROVIDER => []]); - } -} diff --git a/lib/Extension/LanguageServer/LanguageServerSessionExtension.php b/lib/Extension/LanguageServer/LanguageServerSessionExtension.php deleted file mode 100644 index e7407783e4..0000000000 --- a/lib/Extension/LanguageServer/LanguageServerSessionExtension.php +++ /dev/null @@ -1,70 +0,0 @@ -register(ClientCapabilities::class, function (Container $container) { - return $this->initializeParams->capabilities; - }); - - $container->register(InitializeParams::class, function (Container $container) { - return $this->initializeParams; - }); - - $container->register(MessageTransmitter::class, function (Container $container) { - return $this->transmitter; - }); - - $container->register(ResponseWatcher::class, function (Container $container) { - return new DeferredResponseWatcher(); - }); - - $container->register(ClientApi::class, function (Container $container) { - return new ClientApi($container->get(RpcClient::class)); - }); - - $container->register(RpcClient::class, function (Container $container) { - return new JsonRpcClient($this->transmitter, $container->get(ResponseWatcher::class)); - }); - - $container->register(ProgressNotifier::class, function (Container $container) { - $capabilities = $container->get(ClientCapabilities::class); - if ($capabilities?->window?->workDoneProgress ?? false) { - return new WorkDoneProgressNotifier($container->get(ClientApi::class)); - } - - return new MessageProgressNotifier($container->get(ClientApi::class)); - }); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServer/Listener/InvalidConfigListener.php b/lib/Extension/LanguageServer/Listener/InvalidConfigListener.php deleted file mode 100644 index 3d7b7c5107..0000000000 --- a/lib/Extension/LanguageServer/Listener/InvalidConfigListener.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ - public function getListenersForEvent(object $event): iterable - { - if ($event instanceof Initialized) { - return [$this->handleInvalidConfig(...)]; - } - - return []; - } - - /** - * @return Success - */ - public function handleInvalidConfig(): Promise - { - if ($this->errors->errors()) { - $this->clientApi->window()->showMessage()->warning(sprintf( - 'Phpactor configuration error: %s', - implode(', ', array_map(function (InvalidMap $error) { - if ($error instanceof UnknownKeys) { - $suggestions = $this->suggestions($error); - if (count($suggestions)) { - return sprintf( - 'Unknown configuration keys: "%s", did you mean any of: "%s"', - implode('", "', $error->additionalKeys()), - implode('", "', $suggestions), - ); - } - - return sprintf( - 'Unknown configuration keys: "%s"', - implode('", "', $error->additionalKeys()), - ); - } - return $error->getMessage(); - }, $this->errors->errors())) - )); - } - return new Success(); - } - - /** - * @return list - */ - private function suggestions(UnknownKeys $error): array - { - $suggestions = array_filter($error->allowedKeys(), function (string $allowed) use ($error) { - foreach ($error->additionalKeys() as $key) { - if (levenshtein($key, $allowed) < 10) { - return true; - } - } - - return false; - }); - - return array_values($suggestions); - } -} diff --git a/lib/Extension/LanguageServer/Listener/ProjectConfigTrustListener.php b/lib/Extension/LanguageServer/Listener/ProjectConfigTrustListener.php deleted file mode 100644 index 58adb3a1c5..0000000000 --- a/lib/Extension/LanguageServer/Listener/ProjectConfigTrustListener.php +++ /dev/null @@ -1,101 +0,0 @@ - $projectConfigCandidates - */ - public function __construct( - private ClientApi $clientApi, - private array $projectConfigCandidates, - private Trust $trust, - ) { - } - - /** - * @return iterable - */ - public function getListenersForEvent(object $event): iterable - { - if ($event instanceof Initialized) { - return [$this->handleTrustConfig(...)]; - } - - return []; - } - - /** - * @return Success - */ - public function handleTrustConfig(): Promise - { - return call(function () { - foreach ($this->projectConfigCandidates as $path) { - $dir = dirname($path); - - if (!file_exists($path)) { - continue; - } - - // directory is trusted - if ($this->trust->hasTrust($dir)) { - continue; - } - - $yes = new MessageActionItem(self::RESP_YES); - $no = new MessageActionItem(self::RESP_NO); - $notSure = new MessageActionItem(self::RESP_MAYBE); - $response = yield $this->clientApi->window()->showMessageRequest()->warning( - sprintf( - <<<'EOT' - Directory "%s" has a "%s" configuration file that could be used for arbitrary - code execution. Do you trust this file? - EOT, - $dir, - basename($path) - ), - $yes, - $no, - $notSure, - ); - assert($response instanceof MessageActionItem); - if ($response == $notSure) { - return new Success(); - } - $trust = ($response == $yes ? true : false); - - $this->trust->setTrusted($dir, $trust); - - if (false === $trust) { - $this->clientApi->window()->showMessage()->info(sprintf( - 'Config not trusted and will not be loaded. You can change this decision by editing "%s" or running `phpactor config:trust`', - $this->trust->path, - )); - return new Success(); - } - - $this->clientApi->window()->showMessage()->info( - 'Config has been trusted. Restart the language server for changes to take affect' - ); - } - - return new Success(); - }); - } -} diff --git a/lib/Extension/LanguageServer/Listener/SelfDestructListener.php b/lib/Extension/LanguageServer/Listener/SelfDestructListener.php deleted file mode 100644 index fb2bcb1d42..0000000000 --- a/lib/Extension/LanguageServer/Listener/SelfDestructListener.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ - public function getListenersForEvent(object $event): iterable - { - if ($event instanceof WillShutdown) { - return [ - $this->handleShutdown(...), - ]; - } - - return []; - } - - public function handleShutdown(WillShutdown $willShutdown): void - { - asyncCall(function () { - yield delay($this->selfDestructTimeout); - throw new ExitSession(sprintf( - 'Waited "%s" milliseconds after shutdown request for exit notification but did not get one so I\'m self destructing.', - $this->selfDestructTimeout - )); - }); - } -} diff --git a/lib/Extension/LanguageServer/Logger/ClientLogger.php b/lib/Extension/LanguageServer/Logger/ClientLogger.php deleted file mode 100644 index b182e21261..0000000000 --- a/lib/Extension/LanguageServer/Logger/ClientLogger.php +++ /dev/null @@ -1,73 +0,0 @@ -client->window()->logMessage()->error($message); - $this->innerLogger->emergency($message, $context); - } - - - public function alert($message, array $context = []): void - { - $this->client->window()->logMessage()->error($message); - $this->innerLogger->alert($message, $context); - } - - - public function critical($message, array $context = []): void - { - $this->client->window()->logMessage()->error($message); - $this->innerLogger->critical($message, $context); - } - - - public function error($message, array $context = []): void - { - $this->client->window()->showMessage()->error($message); - $this->innerLogger->error($message, $context); - } - - - public function warning($message, array $context = []): void - { - $this->innerLogger->warning($message, $context); - } - - - public function notice($message, array $context = []): void - { - $this->innerLogger->notice($message, $context); - } - - - public function info($message, array $context = []): void - { - $this->innerLogger->info($message, $context); - } - - - public function debug($message, array $context = []): void - { - $this->innerLogger->debug($message, $context); - } - - - public function log($level, $message, array $context = []): void - { - $this->innerLogger->log($level, $message, $context); - } -} diff --git a/lib/Extension/LanguageServer/Middleware/ProfilerMiddleware.php b/lib/Extension/LanguageServer/Middleware/ProfilerMiddleware.php deleted file mode 100644 index 0af5bd2070..0000000000 --- a/lib/Extension/LanguageServer/Middleware/ProfilerMiddleware.php +++ /dev/null @@ -1,74 +0,0 @@ -trace) { - $context['trace'] = true; - $context['body'] = json_encode($request); - } - if ($request instanceof NotificationMessage) { - $this->info(sprintf('PROF >> notification [%s]', $request->method), $context); - } - - if ($request instanceof RequestMessage) { - $this->info(sprintf( - 'PROF >> request #%d [%s]', - $request->id, - $request->method, - ), $context); - } - - $start = microtime(true); - $response = yield $handler->handle($request); - $elapsed = microtime(true) - $start; - - if ($this->trace) { - $context['trace'] = true; - $context['body'] = json_encode($response); - } - - if ($request instanceof NotificationMessage) { - $this->info(sprintf('PROF %-6s << notification [%s]', number_format($elapsed, 4), $request->method), $context); - } - - if ($request instanceof RequestMessage) { - $this->info(sprintf( - 'PROF %-6s << request #%d [%s]', - number_format($elapsed, 4), - $request->id, - $request->method, - ), $context); - } - - return $response; - }); - } - /** - * @param array $context - */ - private function info(string $message, array $context): void - { - $this->logger->info($message, $context); - } -} diff --git a/lib/Extension/LanguageServer/Middleware/TraceMiddleware.php b/lib/Extension/LanguageServer/Middleware/TraceMiddleware.php deleted file mode 100644 index 383e2d0571..0000000000 --- a/lib/Extension/LanguageServer/Middleware/TraceMiddleware.php +++ /dev/null @@ -1,53 +0,0 @@ -logger->info($this->format($request), (array)$request); - $response = yield $handler->handle($request); - if ($response !== null) { - $this->logger->info($this->format($response), (array)$response); - } - - return $response; - }); - } - - private function format(?Message $request): string - { - $encoded = json_encode($request); - - if (false === $encoded) { - return ''; - } - - $direction = '>>'; - - if ($request instanceof ResponseMessage) { - $direction = '<<'; - } - - return sprintf('TRAC %s %s', $direction, (function (string $value) { - if (strlen($value) > 80) { - return substr($value, 0, 79).'⋯'; - } - return $value; - })($encoded)); - } -} diff --git a/lib/Extension/LanguageServer/Status/StatusProvider.php b/lib/Extension/LanguageServer/Status/StatusProvider.php deleted file mode 100644 index 262237d154..0000000000 --- a/lib/Extension/LanguageServer/Status/StatusProvider.php +++ /dev/null @@ -1,14 +0,0 @@ - value status report - * - * @return array - */ - public function provide(): array; -} diff --git a/lib/Extension/LanguageServer/Telemetry/LanguageServerTelemetry.php b/lib/Extension/LanguageServer/Telemetry/LanguageServerTelemetry.php deleted file mode 100644 index 110d2eaebb..0000000000 --- a/lib/Extension/LanguageServer/Telemetry/LanguageServerTelemetry.php +++ /dev/null @@ -1,68 +0,0 @@ -param(0); - assert($message instanceof Message); - return $tracer->spanBuilder( - $context, - self::resolveSpanName($message), - )->startSpan(); - }, - function (TracerContext $tracer, PostContext $context) { - return call(function () use ($tracer, $context) { - $return = yield $context->returnValue; - $scope = $tracer->storage()->scope(); - assert($scope instanceof ScopeInterface); - $scope->detach(); - $span = Span::fromContext($scope->context()); - assert($span instanceof SpanInterface); - $span->end(); - - return $return; - }); - }, - ); - } - - private static function resolveSpanName(Message $message): string - { - if ($message instanceof RequestMessage) { - return sprintf('request %s', $message->method); - } - - if ($message instanceof NotificationMessage) { - return sprintf('notification %s', $message->method); - } - - if ($message instanceof ResponseMessage) { - return 'response'; - } - - return $message::class; - } -} diff --git a/lib/Extension/LanguageServer/Tests/Example/TestExtension.php b/lib/Extension/LanguageServer/Tests/Example/TestExtension.php deleted file mode 100644 index 6b4f55ad65..0000000000 --- a/lib/Extension/LanguageServer/Tests/Example/TestExtension.php +++ /dev/null @@ -1,157 +0,0 @@ -register('test.handler', function (Container $container) { - return new class() implements Handler { - public function methods(): array - { - return ['test' => 'test']; - } - - public function test() - { - return new Success(new NotificationMessage('window/showMessage', [ - 'type' => MessageType::INFO, - 'message' => 'Hallo', - ])); - } - }; - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => []]); - - $container->register('test.service', function (Container $container) { - return new class($container->get(ClientApi::class)) implements ServiceProvider { - public function __construct(private ClientApi $api) - { - } - - public function services(): array - { - return ['test']; - } - - public function test() - { - $this->api->window()->showmessage()->info('service started'); - return new Success(new NotificationMessage('window/showMessage', [ - 'type' => MessageType::INFO, - 'message' => 'Hallo', - ])); - } - }; - }, [ LanguageServerExtension::TAG_SERVICE_PROVIDER => []]); - - $container->register('test.command', function (Container $container) { - return new class() implements CoreCommand { - public function __invoke(string $text): Promise - { - return new Success($text); - } - }; - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => 'echo', - ], - ]); - - $container->register('test.code_action_provider', function (Container $container) { - return new class() implements CodeActionProvider { - public function describe(): string - { - return 'foobar'; - } - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return new Success([ - CodeAction::fromArray([ - 'title' => 'Alice', - 'command' => new Command('Hello Alice', 'phpactor.say_hello', [ - 'Alice', - ]) - ]), - CodeAction::fromArray([ - 'title' => 'Bob', - 'command' => new Command('Hello Bob', 'phpactor.say_hello', [ - 'Bob', - ]) - ]) - ]); - } - - - public function kinds(): array - { - return ['example']; - } - }; - }, [ LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => []]); - - $container->register('test.diagnostic_provider', function (Container $container) { - return new class() implements DiagnosticsProvider { - /** - * @return Promise> - */ - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return new Success([ - ]); - } - - public function name(): string - { - return 'dp1'; - } - }; - }, [ LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('dp1', false)]); - - $container->register('test.diagnostic_provider.outsourced', function (Container $container) { - return new class() implements DiagnosticsProvider { - /** - * @return Promise> - */ - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return new Success([ - ]); - } - - public function name(): string - { - return 'dp2'; - } - }; - }, [ LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('dp2.outsourced', true)]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/CodeAction/OutsourcedCodeActionProviderTest.php b/lib/Extension/LanguageServer/Tests/Unit/CodeAction/OutsourcedCodeActionProviderTest.php deleted file mode 100644 index 4490a064df..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/CodeAction/OutsourcedCodeActionProviderTest.php +++ /dev/null @@ -1,51 +0,0 @@ -workspace()->reset(); - } - - public function testLogMessageWhenTokenIsAlreadyCancelled(): void - { - $logger = new TestLogger(); - $provider = new OutsourcedCodeActionProvider( - [ - __DIR__ . '/../../../../../../bin/phpactor', - 'language-server:code-action', - ], - $this->workspace()->path(), - $logger, - new AggregateCodeActionProvider( - new ClosureCodeActionProvider(function () { - return new Success([new CodeAction(title: 'hello')]); - }) - ), - ); - $cancelSource = (new CancellationTokenSource()); - $cancelSource->cancel(); - $codeActions = wait($provider->provideActionsFor( - ProtocolFactory::textDocumentItem('file:///foo', 'getToken() - )); - - self::assertCount(0, $codeActions); - /** @phpstan-ignore-next-line */ - self::assertStringContainsString('Could not write to stdin', $logger->recordsByLevel['debug'][0]['message']); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/Command/CodeActionsCommandTest.php b/lib/Extension/LanguageServer/Tests/Unit/Command/CodeActionsCommandTest.php deleted file mode 100644 index bf1f670bd8..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/Command/CodeActionsCommandTest.php +++ /dev/null @@ -1,53 +0,0 @@ -workspace()->reset(); - } - - public function testDiagnostics(): void - { - $actions = $this->codeActionsFor('workspace()->path(), [], $sourceCode); - $process->mustRun(); - - $actions = array_map(function (mixed $array): CodeAction { - if (!is_array($array)) { - throw new RuntimeException('nope'); - } - /** @phpstan-ignore argument.type */ - return CodeAction::fromArray($array); - }, (array)json_decode($process->getOutput(), true)); - - return $actions; - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/Command/DiagnosticsCommandTest.php b/lib/Extension/LanguageServer/Tests/Unit/Command/DiagnosticsCommandTest.php deleted file mode 100644 index 2dc31336ec..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/Command/DiagnosticsCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -workspace()->reset(); - } - - public function testDiagnostics(): void - { - $diagnostics = $this->diagnosticsFor('message); - } - - /** - * @return Diagnostic[] - */ - private function diagnosticsFor(string $sourceCode): array - { - $process = new Process([ - PHP_BINARY, - __DIR__ . '/../../../../../../bin/phpactor', - 'language-server:diagnostics', - '--uri=file:///foo', - ], $this->workspace()->path(), [], $sourceCode); - $process->mustRun(); - - $diagnostics = array_map(function (mixed $diagnostic): Diagnostic { - if (!is_array($diagnostic)) { - throw new RuntimeException('nope'); - } - return Diagnostic::fromArray($diagnostic); - }, (array)json_decode($process->getOutput(), true)); - - return $diagnostics; - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/Command/StartCommandTest.php b/lib/Extension/LanguageServer/Tests/Unit/Command/StartCommandTest.php deleted file mode 100644 index cc69eba6ce..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/Command/StartCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -createContainer([]); - $this->tester = new CommandTester($container->get('language_server.command.lsp_start')); - } - - public function testCommandStarts(): void - { - $exitCode = $this->tester->execute([ - '--no-loop' => true, - '--address' => '127.0.0.1:0', - ]); - self::assertEquals(0, $exitCode); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/AggregateDiagnosticProviderTest.php b/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/AggregateDiagnosticProviderTest.php deleted file mode 100644 index 1b00bfbc17..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/AggregateDiagnosticProviderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -createProvider([ - ProtocolFactory::diagnostic( - ProtocolFactory::range(1, 1, 2, 2), - 'one' - ), - ProtocolFactory::diagnostic( - ProtocolFactory::range(1, 1, 2, 2), - 'two' - ) - ]), - $this->createProvider([ - ProtocolFactory::diagnostic( - ProtocolFactory::range(1, 1, 2, 2), - 'three' - ), - ]), - ]; - - $aggregate = $this->createAggregate(...$providers); - $cancel = (new CancellationTokenSource())->getToken(); - $diagnostics = wait($aggregate->provideDiagnostics(ProtocolFactory::textDocumentItem('file:///', 'text'), $cancel)); - self::assertCount(3, $diagnostics); - $diagnostic = $diagnostics[0] ?? null; - self::assertInstanceOf(Diagnostic::class, $diagnostic); - self::assertEquals('test', $diagnostic->code, 'Uses provider ID as code by default'); - } - - public function testReturnsAggregateName(): void - { - $aggregate = $this->createAggregate(...[ - $this->createProvider([], 'one'), - $this->createProvider([], 'two'), - ]); - self::assertEquals('one, two', $aggregate->name()); - } - - private function createAggregate(ClosureDiagnosticsProvider ...$providers): AggregateDiagnosticsProvider - { - return new AggregateDiagnosticsProvider(new NullLogger(), ...$providers); - } - - /** - * @param Diagnostic[] $diagnostics - */ - private function createProvider(array $diagnostics, string $name = 'test'): ClosureDiagnosticsProvider - { - return new ClosureDiagnosticsProvider(function () use ($diagnostics) { - return new Success($diagnostics); - }, $name); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/CodeFilteringDiagnosticProviderTest.php b/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/CodeFilteringDiagnosticProviderTest.php deleted file mode 100644 index d93ac8e782..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/CodeFilteringDiagnosticProviderTest.php +++ /dev/null @@ -1,43 +0,0 @@ -provideDiagnostics( - ProtocolFactory::textDocumentItem('file:///foo', ''), - (new CancellationTokenSource())->getToken() - )); - self::assertCount(1, $diagnostics); - $diagnostic = $diagnostics[0] ?? null; - self::assertInstanceOf(Diagnostic::class, $diagnostic); - self::assertEquals('foo', $diagnostic->code); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/OutsourcedDiagnosticsProvierTest.php b/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/OutsourcedDiagnosticsProvierTest.php deleted file mode 100644 index a21039b587..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/OutsourcedDiagnosticsProvierTest.php +++ /dev/null @@ -1,47 +0,0 @@ -workspace()->reset(); - } - - public function testDiagnostics(): void - { - $provider = new OutsourcedDiagnosticsProvider([ - __DIR__ . '/../../../../../../bin/phpactor', - 'language-server:diagnostics', - ], $this->workspace()->path(), new NullLogger()); - $diagnostics = wait($provider->provideDiagnostics( - ProtocolFactory::textDocumentItem('file:///foo', 'getToken() - )); - self::assertCount(1, $diagnostics); - self::assertEquals('Class "Hello" not found', $diagnostics[0]->message); - } - - public function testAlreadyCancelledDiagnostics(): void - { - $provider = new OutsourcedDiagnosticsProvider([ - __DIR__ . '/../../../../../../bin/phpactor', - 'language-server:diagnostics', - ], $this->workspace()->path(), new NullLogger()); - $source = (new CancellationTokenSource()); - $source->cancel(); - $diagnostics = wait($provider->provideDiagnostics( - ProtocolFactory::textDocumentItem('file:///foo', 'getToken() - )); - self::assertCount(0, $diagnostics); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/PathExcludingDiagnosticsProviderTest.php b/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/PathExcludingDiagnosticsProviderTest.php deleted file mode 100644 index d7d7266534..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/PathExcludingDiagnosticsProviderTest.php +++ /dev/null @@ -1,81 +0,0 @@ - $excludePatterns - */ - #[DataProvider('provideProvide')] - public function testProvide(TextDocumentItem $item, array $excludePatterns, int $expectedCount): void - { - $cancel = new CancellationTokenSource(); - $diagnostics = wait((new PathExcludingDiagnosticsProvider( - new ClosureDiagnosticsProvider(function () { - return new Success([ - ProtocolFactory::diagnostic(ProtocolFactory::range(1, 1, 2, 2), 'test'), - ]); - }), - $excludePatterns, - ))->provideDiagnostics($item, $cancel->getToken())); - self::assertCount($expectedCount, $diagnostics); - } - /** - * @return Generator,int}> - */ - public static function provideProvide(): Generator - { - yield 'match pattern' => [ - ProtocolFactory::textDocumentItem( - 'file:///home/daniel/www/foobar/barfoo/vendor/dan/test.php', - ' [ - ProtocolFactory::textDocumentItem( - 'file:///home/daniel/www/foobar/barfoo/vendor/dan/test.php', - ' [ - ProtocolFactory::textDocumentItem( - 'file:///home/daniel/www/foobar/barfoo/vendor/dan/test.php', - ' [ - ProtocolFactory::textDocumentItem( - 'file:///home/daniel/www/foobar/barfoo/vendor/dan/test.php', - 'createMock(CodeActionProvider::class); - $provider->method('provideActionsFor')->willThrowException(new Exception('Oh no!')); - $tester = LanguageServerTesterBuilder::create(); - (new TolerantCodeActionProvider($provider, $tester->clientApi()))->provideActionsFor( - ProtocolFactory::textDocumentItem('', ''), - ProtocolFactory::range(1, 1, 1, 1), - (new CancellationTokenSource())->getToken(), - ); - $message = $tester->transmitter()->shift(); - self::assertInstanceOf(NotificationMessage::class, $message); - $message = $message->params['message'] ?? null; - self::assertIsString($message); - self::assertStringContainsString('failed: Oh no!', $message); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/Handler/DebugHandlerTest.php b/lib/Extension/LanguageServer/Tests/Unit/Handler/DebugHandlerTest.php deleted file mode 100644 index eb4e99361b..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/Handler/DebugHandlerTest.php +++ /dev/null @@ -1,40 +0,0 @@ -createTester(); - $response = $tester->mustRequestAndWait(DebugHandler::METHOD_DEBUG_CONFIG, []); - $this->assertSuccess($response); - } - - public function testDumpConfigReturningAsJson(): Void - { - $tester = $this->createTester(); - $response = $tester->mustRequestAndWait(DebugHandler::METHOD_DEBUG_CONFIG, [ - 'return' => true, - ]); - $this->assertSuccess($response); - self::assertJson($response->result); - } - - public function testDumpWorkspace(): void - { - $tester = $this->createTester(); - $response = $tester->mustRequestAndWait(DebugHandler::METHOD_DEBUG_WORKSPACE, []); - $this->assertSuccess($response); - } - - public function testStatus(): void - { - $tester = $this->createTester(); - $response = $tester->mustRequestAndWait(DebugHandler::METHOD_DEBUG_STATUS, []); - $this->assertSuccess($response); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/LanguageServerExtensionTest.php b/lib/Extension/LanguageServer/Tests/Unit/LanguageServerExtensionTest.php deleted file mode 100644 index 29e3f235d4..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/LanguageServerExtensionTest.php +++ /dev/null @@ -1,223 +0,0 @@ -createTester(); - $result = $serverTester->initialize(); - self::assertNotNull($result->serverInfo); - self::assertEquals('phpactor/phpactor', $result->serverInfo['name']); - } - - /** - * @covers PhpactorDispatcherFactory::resolveRootUri - */ - public function testInitializeInDirWithSpecialChars(): void - { - $this->workspace()->reset(); - $this->workspace()->put('test & path/foobar/src/Foo.php', 'createTester(new InitializeParams( - capabilities: new ClientCapabilities(), - rootUri: sprintf('file:///%s/%s', $this->workspace()->path(), urlencode('test & path')), - )); - $result = $serverTester->initialize(); - $result = wait($serverTester->request('phpactor/status', [])); - assert($result instanceof ResponseMessage); - assert(is_string($result->result)); - self::assertStringContainsString('test & path', $result->result); - } - - public function testLoadsTextDocuments(): void - { - $serverTester = $this->createTester(); - $serverTester->textDocument()->open(__FILE__, (string)file_get_contents(__FILE__)); - } - - public function testLoadsHandlers(): void - { - $serverTester = $this->createTester(); - $response = $serverTester->mustRequestAndWait('test', []); - $this->assertSuccess($response); - } - - public function testLoadsAllDiagnosticProvidersIfOutsourceIfFalse(): void - { - $container = $this->createContainer([ - LanguageServerExtension::PARAM_DIAGNOSTIC_OUTSOURCE => false, - ]); - $providers = $container->get(AggregateDiagnosticsProvider::class); - self::assertContains('dp1', $providers->names()); - self::assertContains('dp2', $providers->names()); - } - - public function testLoadsOnlyNonOutsourcedProvidersIfOutsourceIsTrue(): void - { - $container = $this->createContainer([ - LanguageServerExtension::PARAM_DIAGNOSTIC_OUTSOURCE => true, - ]); - $providers = $container->get(AggregateDiagnosticsProvider::class); - self::assertContains('dp1', $providers->names()); - self::assertNotContains('dp2.outsourced', $providers->names()); - } - - public function testReturnsStats(): void - { - $serverTester = $this->createTester(); - $response = $serverTester->mustRequestAndWait('phpactor/stats', []); - $this->assertSuccess($response); - $message = $serverTester->transmitter()->shift(); - self::assertNotNull($message); - assert($message instanceof NotificationMessage); - self::assertArrayHasKey('message', $message->params ?? []); - self::assertIsString($message->params['message']); - self::assertStringContainsString('requests: 0', $message->params['message']); - } - - public function testStartsServices(): void - { - $serverTester = $this->createTester(null, [ - LanguageServerExtension::PARAM_FILE_EVENTS =>false, - ]); - $serverTester->initialize(); - wait(delay(10)); - $message = $serverTester->transmitter()->shift(); - self::assertNotNull($message); - assert($message instanceof NotificationMessage); - self::assertArrayHasKey('message', $message->params ?? []); - self::assertEquals('service started', $message->params['message']); - } - - public function testExit(): void - { - $this->expectException(ExitSession::class); - - $serverTester = $this->createTester(); - $serverTester->notifyAndWait('exit', []); - } - - public function testDebug(): void - { - $this->expectException(ExitSession::class); - - $serverTester = $this->createTester(); - $serverTester->notifyAndWait('exit', []); - } - - public function testRegistersCommands(): void - { - $serverTester = $this->createTester(); - $response = $serverTester->mustRequestAndWait('workspace/executeCommand', [ - 'command' => 'echo', - 'arguments' => [ - 'hello', - ], - ]); - $this->assertSuccess($response); - $this->assertEquals('hello', $response->result); - } - - public function testRegistersCodeActionProvider(): void - { - $serverTester = $this->createTester(); - $serverTester->textDocument()->open('file:///foo', 'bar'); - $response = $serverTester->mustRequestAndWait(CodeActionRequest::METHOD, [ - 'textDocument' => [ - 'uri' => 'file:///foo' - ], - 'range' => [ - 'start' => [ 'line' => 0, 'character' => 0, ], - 'end' => [ 'line' => 0, 'character' => 0, ], - ], - 'context' => [ - 'diagnostics' => [], - ], - ]); - $this->assertSuccess($response); - self::assertIsArray($response->result); - self::assertCount(2, $response->result); - } - - public function testNullPath(): void - { - $this->expectException(ExitSession::class); - - $this->createTester(InitializeParams::fromArray([ - 'capabilities' => [], - 'rootUri' => null, - ])); - } - - public function testDisablesWorkspaceListener(): void - { - // workspace is enabled by default - $container = $this->createContainer(); - self::assertInstanceOf(WorkspaceListener::class, $container->get(WorkspaceListener::class)); - - // if disabled it returns NULL and will not be registered - $container = $this->createContainer([ - LanguageServerExtension::PARAM_ENABLE_WORKPACE => false, - ]); - $container->get(WorkspaceListener::class); - } - - public function testExceptionWhenEnablingUnknownDiagProvider(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Unknown diagnostic'); - $serverTester = $this->createTester(null, [ - LanguageServerExtension::PARAM_DIAGNOSTIC_PROVIDERS => ['asd'], - ]); - $serverTester->initialize(); - } - - public function testFilterDiagnosticProviders(): void - { - $serverTester = $this->createTester(null, [ - LanguageServerExtension::PARAM_DIAGNOSTIC_PROVIDERS => [ - 'code-action' - ], - ]); - $serverTester->initialize(); - } - - public function testEnableFileEvents(): void - { - $params = ProtocolFactory::initializeParams($this->workspace()->path('/')); - $params->capabilities = new ClientCapabilities( - workspace: new WorkspaceClientCapabilities( - didChangeWatchedFiles: new DidChangeWatchedFilesClientCapabilities( - dynamicRegistration: true - ) - ), - ); - $serverTester = $this->createTester($params, [ - LanguageServerExtension::PARAM_FILE_EVENTS => true - ]); - $serverTester->initialize(); - wait(delay(10)); - $message = $serverTester->transmitter()->shift(); - self::assertNotNull($message); - assert($message instanceof RequestMessage); - self::assertEquals('client/registerCapability', $message->method); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/LanguageServerTestCase.php b/lib/Extension/LanguageServer/Tests/Unit/LanguageServerTestCase.php deleted file mode 100644 index 10296712cb..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/LanguageServerTestCase.php +++ /dev/null @@ -1,74 +0,0 @@ - $params - */ - protected function createContainer(array $params = []): Container - { - return PhpactorContainer::fromExtensions([ - TestExtension::class, - ConsoleExtension::class, - LanguageServerExtension::class, - LoggingExtension::class, - FilePathResolverExtension::class, - CoreExtension::class, - ], array_merge([ - LanguageServerExtension::PARAM_CATCH_ERRORS => false, - ], $params)); - } - /** - * @param array $config - */ - protected function createTester(?InitializeParams $params = null, array $config = []): LanguageServerTester - { - $builder = $this->createContainer(array_merge([ - LanguageServerExtension::PARAM_DIAGNOSTIC_OUTSOURCE => false, - LanguageServerExtension::PARAM_CODE_ACTION_OUTSOURCE => false, - ], $config))->get( - LanguageServerBuilder::class - ); - - $this->assertInstanceOf(LanguageServerBuilder::class, $builder); - - return $builder->tester($params ?? ProtocolFactory::initializeParams($this->workspace()->path('/'))); - } - - protected function assertSuccess(ResponseMessage $response): void - { - if (!$response->error) { - return; - } - - throw new RuntimeException(sprintf( - 'Response was not successful: [%s] %s: %s', - $response->error->code, - $response->error->message, - $response->error->data - )); - } -} diff --git a/lib/Extension/LanguageServer/Tests/Unit/Listener/InvalidConfigListenerTest.php b/lib/Extension/LanguageServer/Tests/Unit/Listener/InvalidConfigListenerTest.php deleted file mode 100644 index e9b43e588c..0000000000 --- a/lib/Extension/LanguageServer/Tests/Unit/Listener/InvalidConfigListenerTest.php +++ /dev/null @@ -1,27 +0,0 @@ -createTester(Invoke::new(InitializeParams::class, [ - 'capabilities' => new ClientCapabilities(), - 'rootUri' => 'file:///', - 'initializationOptions' => [ - 'language_server.trce' => 'barfoo', - 'path' => 'barfoo', - ] - ])); - $response = $tester->initialize(); - $message = $tester->transmitter()->filterByMethod('window/showMessage')->shiftNotification(); - self::assertNotNull($message); - self::assertStringContainsString('did you mean any of', $message->params['message']); - } -} diff --git a/lib/Extension/LanguageServerBlackfire/BlackfireProfiler.php b/lib/Extension/LanguageServerBlackfire/BlackfireProfiler.php deleted file mode 100644 index a441db19d4..0000000000 --- a/lib/Extension/LanguageServerBlackfire/BlackfireProfiler.php +++ /dev/null @@ -1,73 +0,0 @@ -probing) { - return; - } - if ($this->probe) { - $this->probe->enable(); - $this->probing = true; - } - } - - public function disable(): void - { - if ($this->probe && $this->probing) { - $this->probe->disable(); - $this->probing = false; - } - } - - /** - * Return URL of profile - */ - public function done(): string - { - $profile = $this->blackfire->endProbe($this->probe); - $this->probing = false; - $this->probe = null; - return $profile->getUrl(); - } - - public function start(): void - { - $this->getProbe(); - $this->profiling = true; - } - - /** - * Return true if profiling has been started - */ - public function started(): bool - { - return $this->profiling; - } - - private function getProbe(): Probe - { - if ($this->probe) { - return $this->probe; - } - - $this->probe = $this->blackfire->createProbe(null, false); - return $this->probe; - } -} diff --git a/lib/Extension/LanguageServerBlackfire/Handler/BlackfireHandler.php b/lib/Extension/LanguageServerBlackfire/Handler/BlackfireHandler.php deleted file mode 100644 index 249f68118f..0000000000 --- a/lib/Extension/LanguageServerBlackfire/Handler/BlackfireHandler.php +++ /dev/null @@ -1,55 +0,0 @@ - 'start', - 'blackfire/finish' => 'finish', - ]; - } - - /** - * @return Promise - */ - public function start(): Promise - { - $this->client->window()->showMessage()->info( - 'Blackfire profiling started', - ); - $this->profiler->start(); - - return new Success(null); - } - - /** - * @return Promise - */ - public function finish(): Promise - { - $this->client->window()->showMessage()->info( - 'Blackfire profile creating....', - ); - $url = $this->profiler->done(); - $this->client->window()->showMessage()->info(sprintf( - 'Blackfire profile created: %s', - $url - )); - return new Success(null); - } -} diff --git a/lib/Extension/LanguageServerBlackfire/LanguageServerBlackfireExtension.php b/lib/Extension/LanguageServerBlackfire/LanguageServerBlackfireExtension.php deleted file mode 100644 index 9d30ff295d..0000000000 --- a/lib/Extension/LanguageServerBlackfire/LanguageServerBlackfireExtension.php +++ /dev/null @@ -1,51 +0,0 @@ -register(BlackfireHandler::class, function (Container $container) { - return new BlackfireHandler( - $container->get(BlackfireProfiler::class), - $container->get(ClientApi::class) - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => []]); - - $container->register(BlackfireProfiler::class, function (Container $container) { - if (!class_exists(Client::class)) { - throw new RuntimeException( - 'Blackfire blackfire/php-sdk package is not installed, maybe you need to ensure Phpactor is installed with dev dependencies?' - ); - } - return new BlackfireProfiler(new Client()); - }); - - $container->register(BlackfireMiddleware::class, function (Container $container) { - return new BlackfireMiddleware($container->get(BlackfireProfiler::class)); - }, [ - LanguageServerExtension::TAG_MIDDLEWARE => [] - ]); - } - - public function configure(Resolver $schema): void - { - } - - public function name(): string - { - return 'blackfire'; - } -} diff --git a/lib/Extension/LanguageServerBlackfire/Middleware/BlackfireMiddleware.php b/lib/Extension/LanguageServerBlackfire/Middleware/BlackfireMiddleware.php deleted file mode 100644 index 7771ae2f47..0000000000 --- a/lib/Extension/LanguageServerBlackfire/Middleware/BlackfireMiddleware.php +++ /dev/null @@ -1,34 +0,0 @@ -profiler->started()) { - return $handler->handle($request); - } - $this->profiler->enable(); - - $response = yield $handler->handle($request); - - if ($this->profiler->started()) { - $this->profiler->disable(); - } - return $response; - }); - } -} diff --git a/lib/Extension/LanguageServerBridge/Converter/Exception/CouldNotLoadFileContents.php b/lib/Extension/LanguageServerBridge/Converter/Exception/CouldNotLoadFileContents.php deleted file mode 100644 index 36d2c58313..0000000000 --- a/lib/Extension/LanguageServerBridge/Converter/Exception/CouldNotLoadFileContents.php +++ /dev/null @@ -1,9 +0,0 @@ - - */ - public function toLspLocations(Locations $locations): array - { - $lspLocations = []; - foreach ($locations as $location) { - try { - $lspLocations[] = $this->toLspLocation($location); - } catch (TextDocumentNotFound) { - continue; - } catch (CouldNotLoadFileContents) { - continue; - } - } - - return $lspLocations; - } - - public function toLspLocation(Location $location): LspLocation - { - $textDocument = $this->locator->get($location->uri()); - - return new LspLocation( - $location->uri()->__toString(), - RangeConverter::toLspRange($location->range(), (string) $textDocument) - ); - } -} diff --git a/lib/Extension/LanguageServerBridge/Converter/PositionConverter.php b/lib/Extension/LanguageServerBridge/Converter/PositionConverter.php deleted file mode 100644 index b5dad0f704..0000000000 --- a/lib/Extension/LanguageServerBridge/Converter/PositionConverter.php +++ /dev/null @@ -1,62 +0,0 @@ -toInt() > strlen($text)) { - $offset = ByteOffset::fromInt(strlen($text)); - } - - $lineCol = LineCol::fromByteOffset($text, $offset, true); - - return new Position($lineCol->line() - 1, $lineCol->col() - 1); - } - - /** - * Convert UTF-16 position to byteoffset. - */ - public static function positionToByteOffset(Position $position, string $text): ByteOffset - { - // get byte offset position of line start - $lineCol = new LineCol($position->line + 1, 1); - $byteOffset = $lineCol->toByteOffset($text); - - // convert line to UTF-16 as Position character is UTF-16 code unit position - $rest = substr($text, $byteOffset->toInt()); - $lineEnd = strpos($rest, "\n"); - if ($lineEnd !== false) { - $rest = substr($rest, 0, $lineEnd); - } - $rest = self::normalizeUtf16($rest); - $seg = substr($rest, 0, $position->character * 2); - $utf8 = \mb_convert_encoding($seg, 'UTF-8', 'UTF-16BE'); - - return ByteOffset::fromInt($byteOffset->toInt() + strlen($utf8)); - } - - /** - * Stolen from: https://github.com/symfony/symfony/issues/45459#issuecomment-1045502304 - */ - private static function normalizeUtf16(string $string): string - { - $utf16 = \mb_convert_encoding($string, 'UTF-16BE', 'UTF-8'); - if (!is_string($utf16)) { - throw new RuntimeException('String cannot be converted to UTF-16'); - } - - return $utf16; - } -} diff --git a/lib/Extension/LanguageServerBridge/Converter/RangeConverter.php b/lib/Extension/LanguageServerBridge/Converter/RangeConverter.php deleted file mode 100644 index 20f3f0d7fe..0000000000 --- a/lib/Extension/LanguageServerBridge/Converter/RangeConverter.php +++ /dev/null @@ -1,25 +0,0 @@ -start(), $text), - PositionConverter::byteOffsetToPosition($range->end(), $text), - ); - } - - public static function toPhpactorRange(Range $range, string $text): ByteOffsetRange - { - return new ByteOffsetRange( - PositionConverter::positionToByteOffset($range->start, $text), - PositionConverter::positionToByteOffset($range->end, $text), - ); - } -} diff --git a/lib/Extension/LanguageServerBridge/Converter/TextDocumentConverter.php b/lib/Extension/LanguageServerBridge/Converter/TextDocumentConverter.php deleted file mode 100644 index 56f5efe253..0000000000 --- a/lib/Extension/LanguageServerBridge/Converter/TextDocumentConverter.php +++ /dev/null @@ -1,21 +0,0 @@ -text); - if ($item->uri) { - $builder->uri($item->uri); - } - $builder->language($item->languageId); - - return $builder->build(); - } -} diff --git a/lib/Extension/LanguageServerBridge/Converter/TextEditConverter.php b/lib/Extension/LanguageServerBridge/Converter/TextEditConverter.php deleted file mode 100644 index 22a88d8c5b..0000000000 --- a/lib/Extension/LanguageServerBridge/Converter/TextEditConverter.php +++ /dev/null @@ -1,31 +0,0 @@ - $textEdits - * @return array - */ - public static function toLspTextEdits(TextEdits $textEdits, string $text): array - { - $edits = []; - foreach ($textEdits as $textEdit) { - $range = new Range( - PositionConverter::byteOffsetToPosition($textEdit->start(), $text), - PositionConverter::byteOffsetToPosition($textEdit->end(), $text), - ); - - // deduplicate text edits - $edits[] = new LspTextEdit($range, $textEdit->replacement()); - } - - return array_values($edits); - } -} diff --git a/lib/Extension/LanguageServerBridge/Converter/WorkspaceEditConverter.php b/lib/Extension/LanguageServerBridge/Converter/WorkspaceEditConverter.php deleted file mode 100644 index aa985a6189..0000000000 --- a/lib/Extension/LanguageServerBridge/Converter/WorkspaceEditConverter.php +++ /dev/null @@ -1,26 +0,0 @@ -uri()->__toString()] = TextEditConverter::toLspTextEdits( - $edit->textEdits(), - $this->locator->get($edit->uri())->__toString() - ); - } - return new WorkspaceEdit($lspEdits); - } -} diff --git a/lib/Extension/LanguageServerBridge/LanguageServerBridgeExtension.php b/lib/Extension/LanguageServerBridge/LanguageServerBridgeExtension.php deleted file mode 100644 index cd0670985a..0000000000 --- a/lib/Extension/LanguageServerBridge/LanguageServerBridgeExtension.php +++ /dev/null @@ -1,56 +0,0 @@ -register(LocationConverter::class, function (Container $container) { - return new LocationConverter( - $container->get(TextDocumentLocator::class) - ); - }); - - $container->register(TextEditConverter::class, function (Container $container) { - return new TextEditConverter(); - }); - - $container->register(WorkspaceEditConverter::class, function (Container $container) { - return new WorkspaceEditConverter($container->get(TextDocumentLocator::class)); - }); - - $container->register(FilesystemTextDocumentLocator::class, function (Container $container) { - return new FilesystemTextDocumentLocator(); - }); - - $container->register(WorkspaceTextDocumentLocator::class, function (Container $container) { - return new WorkspaceTextDocumentLocator($container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE)); - }); - - $container->register(TextDocumentLocator::class, function (Container $container) { - return new ChainDocumentLocator([ - $container->get(WorkspaceTextDocumentLocator::class), - $container->get(FilesystemTextDocumentLocator::class) - ]); - }); - } -} diff --git a/lib/Extension/LanguageServerBridge/Tests/Converter/LocationConverterTest.php b/lib/Extension/LanguageServerBridge/Tests/Converter/LocationConverterTest.php deleted file mode 100644 index 4b9d334fa8..0000000000 --- a/lib/Extension/LanguageServerBridge/Tests/Converter/LocationConverterTest.php +++ /dev/null @@ -1,153 +0,0 @@ -workspace()->reset(); - - $this->converter = new LocationConverter(new FilesystemTextDocumentLocator()); - } - - public function testConvertsPhpactorLocationsToLspLocations(): void - { - $this->workspace()->put('test.php', '012345678'); - - $locations = new Locations([ - Location::fromPathAndOffsets($this->workspace()->path('test.php'), 2, 10) - ]); - - $expected = [ - new LspLocation((string)TextDocumentUri::fromString($this->workspace()->path('test.php')), new Range( - new Position(0, 2), - new Position(0, 9), - )) - ]; - - self::assertEquals($expected, $this->converter->toLspLocations($locations)); - } - - public function testIgnoresNonExistingFiles(): void - { - $this->workspace()->put('test.php', '12345678'); - - $locations = new Locations([ - Location::fromPathAndOffsets($this->workspace()->path('test.php'), 2, 4), - Location::fromPathAndOffsets($this->workspace()->path('test-no.php'), 2, 4) - ]); - - $expected = [ - new LspLocation((string)TextDocumentUri::fromString($this->workspace()->path('test.php')), new Range( - new Position(0, 2), - new Position(0, 4), - )) - ]; - - self::assertEquals($expected, $this->converter->toLspLocations($locations)); - } - - #[DataProvider('provideDiskLocations')] - #[DataProvider('provideMultibyte')] - #[DataProvider('provideOutOfRange')] - public function testLocationToLspLocation(string $text, int $start, int $end, Range $expectedRange): void - { - $this->workspace()->put('test.php', $text); - - $location = Location::fromPathAndOffsets($this->workspace()->path('test.php'), $start, $end); - - $uri = (string)TextDocumentUri::fromString($this->workspace()->path('test.php')); - - self::assertEquals( - expected: new LspLocation($uri, $expectedRange), - actual: $this->converter->toLspLocation($location) - ); - } - - /** - * @return Generator - */ - public function provideOutOfRange(): Generator - { - yield 'out of upper range' => [ - '12345', - 10, - 15, - $this->createRange(0, 5, 0, 5) - ]; - } - - /** - * @return Generator - */ - public function provideMultibyte(): Generator - { - yield '4 byte char 1st char' => [ - '😼😼😼😼😼', - 2, - 2, - $this->createRange(0, 1, 0, 1) - ]; - - yield '4 byte char 2nd char' => [ - '😼😼😼😼😼', - 2, - 5, - $this->createRange(0, 1, 0, 3) - ]; - - yield '4 byte char 4th char' => [ - '😼😼😼😼😼', - 2, - 16, - $this->createRange(0, 1, 0, 8) - ]; - } - - /** - * @return Generator - */ - public function provideDiskLocations(): Generator - { - yield 'single line' => [ - '12345678', - 2, - 4, - $this->createRange(0, 2, 0, 4) - ]; - - yield 'second line' => [ - "12\n345\n678", - 4, - 5, - $this->createRange(1, 1, 1, 2) - ]; - - yield 'third line first char' => [ - "12\n345\n678", - 8, - 10, - $this->createRange(2, 1, 2, 3) - ]; - } - - private function createRange(int $line1, int $offset1, int $line2, int $offset2): Range - { - return new Range(new Position($line1, $offset1), new Position($line2, $offset2)); - } -} diff --git a/lib/Extension/LanguageServerBridge/Tests/Converter/RangeConverterTest.php b/lib/Extension/LanguageServerBridge/Tests/Converter/RangeConverterTest.php deleted file mode 100644 index ac447492dd..0000000000 --- a/lib/Extension/LanguageServerBridge/Tests/Converter/RangeConverterTest.php +++ /dev/null @@ -1,30 +0,0 @@ -workspace()->reset(); - $this->workspace = new Workspace(); - $this->converter = new TextEditConverter(new LocationConverter(new WorkspaceTextDocumentLocator($this->workspace))); - } - - public function testConvertsTextEdits(): void - { - $text = '1234567890'; - self::assertEquals([ - new LspTextEdit(new Range( - new Position(0, 1), - new Position(0, 4), - ), 'foo'), - ], $this->converter->toLspTextEdits(TextEdits::one(TextEdit::create(1, 3, 'foo')), $text)); - } -} diff --git a/lib/Extension/LanguageServerBridge/Tests/IntegrationTestCase.php b/lib/Extension/LanguageServerBridge/Tests/IntegrationTestCase.php deleted file mode 100644 index aa0a30c3bf..0000000000 --- a/lib/Extension/LanguageServerBridge/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,14 +0,0 @@ -sessionExtension = new LanguageServerSessionExtension( - $transmitter, - ProtocolFactory::initializeParams() - ); - } - - - public function load(ContainerBuilder $container): void - { - $this->sessionExtension->load($container); - } - - - public function configure(Resolver $schema): void - { - $this->sessionExtension->configure($schema); - } -} diff --git a/lib/Extension/LanguageServerBridge/Tests/Unit/Converter/PositionConverterTest.php b/lib/Extension/LanguageServerBridge/Tests/Unit/Converter/PositionConverterTest.php deleted file mode 100644 index 1557ecbb3f..0000000000 --- a/lib/Extension/LanguageServerBridge/Tests/Unit/Converter/PositionConverterTest.php +++ /dev/null @@ -1,87 +0,0 @@ -workspace->get($uri->__toString())); - } catch (UnknownDocument) { - } - - throw TextDocumentNotFound::fromUri($uri); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/ByteOffsetRefactorProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/ByteOffsetRefactorProvider.php deleted file mode 100644 index ce46101236..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/ByteOffsetRefactorProvider.php +++ /dev/null @@ -1,60 +0,0 @@ -refactor->refactor( - TextDocumentConverter::fromLspTextItem($textDocument), - RangeConverter::toPhpactorRange($range, $textDocument->text)->start() - ); - - if (count($edits) === 0) { - return new Success([]); - } - - return new Success([ - new CodeAction( - title: $this->title, - kind: $this->kind, - diagnostics: [], - isPreferred: false, - edit: new WorkspaceEdit([ - $textDocument->uri => TextEditConverter::toLspTextEdits($edits, $textDocument->text) - ]) - ) - ]); - } - - public function kinds(): array - { - return [$this->kind]; - } - public function describe(): string - { - return $this->description; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/CorrectUndefinedVariableCodeAction.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/CorrectUndefinedVariableCodeAction.php deleted file mode 100644 index 104ea5aab5..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/CorrectUndefinedVariableCodeAction.php +++ /dev/null @@ -1,82 +0,0 @@ -reflector->diagnostics( - TextDocumentConverter::fromLspTextItem($textDocument) - ))->byClass( - UndefinedVariableDiagnostic::class - ) as $diagnostic) { - assert($diagnostic instanceof UndefinedVariableDiagnostic); - foreach ($diagnostic->suggestions() as $suggestion) { - if ($cancel->isRequested()) { - return $actions; - } - $actions[] = new CodeAction( - title: sprintf('Correct undefined variable "$%s" to "$%s"', $diagnostic->undefinedVariableName(), $suggestion), - kind: CodeActionKind::QUICK_FIX, - diagnostics: null, - isPreferred: null, - disabled: null, - edit: new WorkspaceEdit( - documentChanges: [ - new TextDocumentEdit( - new OptionalVersionedTextDocumentIdentifier($textDocument->uri, $textDocument->version), - [ - new TextEdit( - range: RangeConverter::toLspRange($diagnostic->range(), $textDocument->text), - newText: '$' . $suggestion - ), - ] - ) - ], - ), - command: null, - ); - } - } - return $actions; - }); - } - - public function kinds(): array - { - return [ - self::KIND - ]; - } - - public function describe(): string - { - return 'correct undefined variable name'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/CreateClassProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/CreateClassProvider.php deleted file mode 100644 index 4c2e8c0484..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/CreateClassProvider.php +++ /dev/null @@ -1,108 +0,0 @@ -getDiagnostics($textDocument)); - } - - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument) { - $diagnostics = $this->getDiagnostics($textDocument); - - if ($diagnostics === []) { - return []; - } - - $actions = []; - - foreach ($this->generators as $name => $generator) { - $title = sprintf('Create new "%s" class', $name); - $actions[] = CodeAction::fromArray([ - 'title' => $title, - 'kind' => self::KIND, - 'diagnostics' => $diagnostics, - 'command' => new Command( - $title, - CreateClassCommand::NAME, - [ - $textDocument->uri, - $name - ] - ) - ]); - } - - return $actions; - }); - } - - public function name(): string - { - return 'create-class'; - } - - public function describe(): string - { - return 'create class in empty file'; - } - - /** - * @return array - */ - private function getDiagnostics(TextDocumentItem $textDocument): array - { - if ('' !== trim($textDocument->text)) { - return []; - } - - return [ - new Diagnostic( - range: new Range( - new Position(1, 1), - new Position(1, 1) - ), - message: sprintf( - 'Empty file (use create-class code action to create a new class)', - ), - severity: DiagnosticSeverity::INFORMATION, - source: 'phpactor' - ) - ]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/CreateUnresolvableClassProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/CreateUnresolvableClassProvider.php deleted file mode 100644 index 29ad585101..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/CreateUnresolvableClassProvider.php +++ /dev/null @@ -1,90 +0,0 @@ -reflector->diagnostics(TextDocumentConverter::fromLspTextItem($textDocument)))->byClass( - UnresolvableNameDiagnostic::class - )->containingRange( - RangeConverter::toPhpactorRange($range, $textDocument->text) - ); - $actions = []; - - foreach ($diagnostics as $diagnostic) { - assert($diagnostic instanceof UnresolvableNameDiagnostic); - if ($diagnostic->type() !== UnresolvableNameDiagnostic::TYPE_CLASS) { - continue; - } - - foreach ($this->classToFile->classToFileCandidates(ClassName::fromString($diagnostic->name())) as $candidate) { - assert($candidate instanceof FilePath); - foreach ($this->generators as $name => $_) { - $title = sprintf('Create %s file for "%s"', $name, $diagnostic->name()->__toString()); - $actions[] = CodeAction::fromArray([ - 'title' => $title, - 'kind' => self::KIND, - 'diagnostics' => [ - ProtocolFactory::diagnostic(RangeConverter::toLspRange($diagnostic->range(), $textDocument->text), $diagnostic->message()) - ], - 'command' => new Command( - $title, - CreateClassCommand::NAME, - [ - TextDocumentUri::fromString($candidate->__toString())->__toString(), - $name - ] - ) - ]); - } - } - } - - return $actions; - }); - } - - public function kinds(): array - { - return [ - self::KIND - ]; - } - - public function describe(): string - { - return 'create class for any class which cannot be found'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractConstantProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractConstantProvider.php deleted file mode 100644 index 744de5a956..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractConstantProvider.php +++ /dev/null @@ -1,66 +0,0 @@ -extractConstant->canExtractConstant( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(), - )) { - return []; - } - - return [ - CodeAction::fromArray([ - 'title' => 'Extract constant', - 'kind' => self::KIND, - 'diagnostics' => [], - 'command' => new Command( - 'Extract constant', - ExtractConstantCommand::NAME, - [ - $textDocument->uri, - PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(), - PositionConverter::positionToByteOffset($range->end, $textDocument->text)->toInt() - ] - ) - ]) - ]; - }); - } - public function describe(): string - { - return 'extract constant'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractExpressionProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractExpressionProvider.php deleted file mode 100644 index 1cd85e8430..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractExpressionProvider.php +++ /dev/null @@ -1,68 +0,0 @@ -extractExpression->canExtractExpression( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(), - PositionConverter::positionToByteOffset($range->end, $textDocument->text)->toInt() - )) { - return []; - } - - return [ - CodeAction::fromArray([ - 'title' => 'Extract expression', - 'kind' => self::KIND, - 'diagnostics' => [], - 'command' => new Command( - 'Extract method', - ExtractExpressionCommand::NAME, - [ - $textDocument->uri, - PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(), - PositionConverter::positionToByteOffset($range->end, $textDocument->text)->toInt() - ] - ) - ]) - ]; - }); - } - public function describe(): string - { - return 'extract expression'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractMethodProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractMethodProvider.php deleted file mode 100644 index c3bc80b9ef..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractMethodProvider.php +++ /dev/null @@ -1,67 +0,0 @@ -extractMethod->canExtractMethod( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(), - PositionConverter::positionToByteOffset($range->end, $textDocument->text)->toInt() - )) { - return []; - } - - return [ - CodeAction::fromArray([ - 'title' => 'Extract method', - 'kind' => self::KIND, - 'diagnostics' => [], - 'command' => new Command( - 'Extract method', - ExtractMethodCommand::NAME, - [ - $textDocument->uri, - PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(), - PositionConverter::positionToByteOffset($range->end, $textDocument->text)->toInt() - ] - ) - ]) - ]; - }); - } - public function describe(): string - { - return 'extract method'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateConstructorProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateConstructorProvider.php deleted file mode 100644 index 1eb346eea3..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateConstructorProvider.php +++ /dev/null @@ -1,59 +0,0 @@ -generateConstructor->generateMethod( - TextDocumentConverter::fromLspTextItem($textDocument), - RangeConverter::toPhpactorRange($range, $textDocument->text)->start() - ); - - if (count($edits) === 0) { - return new Success([]); - } - - return new Success([ - new CodeAction( - title: 'Generate constructor', - kind: self::KIND, - diagnostics: [], - isPreferred: false, - edit: $this->converter->toLspWorkspaceEdit($edits) - ) - ]); - } - - public function kinds(): array - { - return [self::KIND]; - } - - public function describe(): string - { - return 'generate constructor for new object instantiation'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateDecoratorProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateDecoratorProvider.php deleted file mode 100644 index e8d0516150..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateDecoratorProvider.php +++ /dev/null @@ -1,86 +0,0 @@ -reflector->reflectClassesIn(TextDocumentConverter::fromLspTextItem($textDocument)); - if (count($classes->classes()) !== 1) { - return []; - } - - $class = $classes->classes()->first(); - - if (!$class instanceof ReflectionClass) { - return []; - } - - assert($class instanceof ReflectionClass); - - $interfaces = $class->interfaces(); - - if (count($interfaces) !== 1) { - return []; - } - - if (count($class->methods()) > 0) { - return []; - } - - if ($class->parent()) { - return []; - } - - $interfaceFQN = (string) $interfaces->first()->type(); - - return [ - CodeAction::fromArray([ - 'title' => sprintf('Decorate "%s"', $interfaceFQN), - 'kind' => self::KIND, - 'command' => new Command( - 'Generate decorator', - GenerateDecoratorCommand::NAME, - [ - $textDocument->uri, - $interfaceFQN, - ] - ) - ]), - ]; - }); - } - - public function describe(): string - { - return 'convert an empty class that implements an interface into a decorator'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php deleted file mode 100644 index e6b4458c86..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php +++ /dev/null @@ -1,126 +0,0 @@ -getDiagnostics($textDocument); - } - - - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument) { - $diagnostics = yield $this->getDiagnostics($textDocument); - - return array_map(function (Diagnostic $diagnostic) use ($textDocument) { - return CodeAction::fromArray([ - 'title' => sprintf('Fix "%s"', $diagnostic->message), - 'kind' => self::KIND, - 'diagnostics' => [ - $diagnostic - ], - 'command' => new Command( - 'Generate member', - GenerateMemberCommand::NAME, - [ - $textDocument->uri, - PositionConverter::positionToByteOffset( - $diagnostic->range->start, - $textDocument->text - )->toInt() - ] - ) - ]); - }, $diagnostics); - }); - } - - public function name(): string - { - return 'generate-member'; - } - - public function describe(): string - { - return 'generate non-existing member'; - } - - /** - * @return Promise> - */ - private function getDiagnostics(TextDocumentItem $textDocument): Promise - { - return call(function () use ($textDocument) { - $methods = yield $this->missingMethodFinder->find( - TextDocumentConverter::fromLspTextItem($textDocument) - ); - $diagnostics = []; - - foreach ($methods as $method) { - $diagnostics[] = new Diagnostic( - range: RangeConverter::toLspRange($method->range(), $textDocument->text), - message: sprintf('%s "%s" does not exist', ucfirst($method->memberType()), $method->name()), - severity: DiagnosticSeverity::WARNING, - source: 'phpactor', - ); - } - - usort($diagnostics, function (Diagnostic $a, Diagnostic $b) { - if ($a->range->start->line > $b->range->start->line) { - return 1; - } - - if ($a->range->start->line < $b->range->start->line) { - return -1; - } - - if ($a->range->start->character > $b->range->start->character) { - return 1; - } - - if ($a->range->start->character < $b->range->start->character) { - return -1; - } - - return 0; - }); - - return $diagnostics; - }); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php deleted file mode 100644 index 0a9e2a1067..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php +++ /dev/null @@ -1,184 +0,0 @@ -finder->importCandidates($item) as $candidate) { - $actions[] = $this->codeActionForFqn($candidate->unresolvedName(), $candidate->candidateFqn(), $item); - yield delay(1); - } - - if (count($actions) > 1) { - array_unshift($actions, $this->addImportAllAction($item)); - } - - return $actions; - }); - } - - - public function kinds(): array - { - return [ - 'quickfix.import_class' - ]; - } - - - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument) { - $diagnostics = []; - $hasCandidatesHash = []; - foreach (yield $this->finder->unresolved($textDocument) as $unresolvedName) { - assert($unresolvedName instanceof NameWithByteOffset); - $nameString = (string)$unresolvedName->name(); - [ - $hasCandidates, - $diagnostic - ] = $this->diagnosticsFromUnresolvedName( - $unresolvedName, - $textDocument, - isset($hasCandidatesHash[$nameString]) ? $hasCandidatesHash[$nameString] : null - ); - $hasCandidatesHash[$nameString] = $hasCandidates; - if ($diagnostic !== null) { - $diagnostics[] = $diagnostic; - } - } - - return $diagnostics; - }); - } - - public function name(): string - { - return 'import-name'; - } - - public function describe(): string - { - return 'import unresolvable class names'; - } - - private function diagnosticsFromUnresolvedName(NameWithByteOffset $unresolvedName, TextDocumentItem $item, ?bool $hasCandidates = null): array - { - $range = new Range( - PositionConverter::byteOffsetToPosition($unresolvedName->byteOffset(), $item->text), - PositionConverter::intByteOffsetToPosition( - $unresolvedName->byteOffset()->toInt() + strlen($unresolvedName->name()->head()->__toString()), - $item->text - ) - ); - - if (null === $hasCandidates) { - $hasCandidates = $this->finder->candidatesForUnresolvedName($unresolvedName)->current() !== null; - } - - if (false === $hasCandidates) { - if ($this->reportNonExistingClasses === false) { - return [false, null]; - } - return [ - false, - new Diagnostic( - range: $range, - message: sprintf( - '%s "%s" does not exist', - ucfirst($unresolvedName->type()), - $unresolvedName->name()->head()->__toString() - ), - severity: DiagnosticSeverity::ERROR, - source: 'phpactor' - ) - ]; - } - - return [ - true, - new Diagnostic( - range: $range, - message: sprintf( - '%s "%s" has not been imported', - ucfirst($unresolvedName->type()), - $unresolvedName->name()->head()->__toString() - ), - severity: DiagnosticSeverity::HINT, - source: 'phpactor' - ) - ]; - } - - private function codeActionForFqn(NameWithByteOffset $unresolvedName, string $fqn, TextDocumentItem $item): CodeAction - { - $diagnostics = $this->diagnosticsFromUnresolvedName($unresolvedName, $item, true); - return CodeAction::fromArray([ - 'title' => sprintf( - 'Import %s "%s"', - $unresolvedName->type(), - $fqn - ), - 'kind' => 'quickfix.import_class', - 'isPreferred' => false, - 'diagnostics' => ($diagnostics[1] !== null) ? [$diagnostics[1]] : null, - 'command' => new Command( - 'Import name', - ImportNameCommand::NAME, - [ - $item->uri, - $unresolvedName->byteOffset()->toInt(), - $unresolvedName->type(), - $fqn - ] - ) - ]); - } - - private function addImportAllAction(TextDocumentItem $item): CodeAction - { - return CodeAction::fromArray([ - 'title' => sprintf( - 'Import all unresolved names', - ), - 'kind' => 'quickfix.import_all_unresolved_names', - 'isPreferred' => true, - 'diagnostics' => [], - 'command' => new Command( - 'Import all unresolved names', - ImportAllUnresolvedNamesCommand::NAME, - [ - $item->uri, - ] - ) - ]); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/OverrideMethodProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/OverrideMethodProvider.php deleted file mode 100644 index 38e8bcb19d..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/OverrideMethodProvider.php +++ /dev/null @@ -1,69 +0,0 @@ -finder->find(TextDocumentConverter::fromLspTextItem($item)); - if (count($overridables) === 0) { - return []; - } - $actions = []; - $actions[] = CodeAction::fromArray([ - 'title' => sprintf( - 'Override one of %d methods', - count($overridables), - ), - 'kind' => 'quickfix.override_method', - 'isPreferred' => false, - 'command' => new Command( - 'Override method dialogue', - OverrideMethodCommand::NAME, - [ - $item->uri, - ] - ) - ]); - - return $actions; - }); - } - - public function kinds(): array - { - return [ - 'quickfix.override_method' - ]; - } - - public function name(): string - { - return 'override-method'; - } - - public function describe(): string - { - return 'override method from parent class'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/PropertyAccessGeneratorProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/PropertyAccessGeneratorProvider.php deleted file mode 100644 index 164afeeb52..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/PropertyAccessGeneratorProvider.php +++ /dev/null @@ -1,99 +0,0 @@ -kind, - ]; - } - - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return call(function () use ($range, $textDocument) { - // CoC will select the entire document if no range selected - if ($range->start->line === 0 && $range->start->character === 0) { - return []; - } - $startOffset = PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(); - $endOffset = PositionConverter::positionToByteOffset($range->end, $textDocument->text)->toInt(); - - $classes = $this->reflector->reflectClassesIn(TextDocumentConverter::fromLspTextItem($textDocument)); - - if ($classes->count() === 0) { - return []; - } - - // TODO: Class at offset - $reflectionClass = $classes->first(); - - if (!$reflectionClass instanceof ReflectionClass) { - return []; - } - - $propertyNames = []; - foreach ($reflectionClass->properties() as $property) { - assert($property instanceof ReflectionProperty); - if ($property->position()->start()->toInt() < $startOffset || $property->position()->end()->toInt() > $endOffset) { - continue; - } - $propertyNames[] = $property->name(); - } - - if (empty($propertyNames)) { - return []; - } - - $title = sprintf( - 'Generate %s %s(s)', - count($propertyNames), - $this->generatorRole - ); - - return [ - CodeAction::fromArray([ - 'title' => $title, - 'kind' => $this->kind, - 'command' => new Command( - $title, - $this->command, - [ - $textDocument->uri, - $startOffset, - $propertyNames, - ] - ) - ]) - ]; - }); - } - public function describe(): string - { - return 'add properties that are assigned to but not present'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/ReplaceQualifierWithImportProvider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/ReplaceQualifierWithImportProvider.php deleted file mode 100644 index 4883e2c0b9..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/ReplaceQualifierWithImportProvider.php +++ /dev/null @@ -1,65 +0,0 @@ -replaceQualifierWithImport->canReplaceWithImport( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(), - )) { - return []; - } - - return [ - CodeAction::fromArray([ - 'title' => 'Replace qualifier with import', - 'kind' => self::KIND, - 'diagnostics' => [], - 'command' => new Command( - 'Replace qualifier with import', - ReplaceQualifierWithImportCommand::NAME, - [ - $textDocument->uri, - PositionConverter::positionToByteOffset($range->start, $textDocument->text)->toInt(), - ] - ) - ]) - ]; - }); - } - - public function describe(): string - { - return 'replace qualifier with importer'; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/CodeAction/TransformerCodeActionPovider.php b/lib/Extension/LanguageServerCodeTransform/CodeAction/TransformerCodeActionPovider.php deleted file mode 100644 index 0108b35d33..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/CodeAction/TransformerCodeActionPovider.php +++ /dev/null @@ -1,104 +0,0 @@ -kind() - ]; - } - - - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return $this->getDiagnostics($textDocument); - } - - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument) { - $diagnostics = yield $this->getDiagnostics($textDocument); - if (0 === count($diagnostics)) { - return []; - } - - return [ - CodeAction::fromArray([ - 'title' => $this->title, - 'kind' => $this->kind(), - 'diagnostics' => $diagnostics, - 'command' => new Command( - $this->title, - TransformCommand::NAME, - [ - $textDocument->uri, - $this->name - ] - ) - ]) - ]; - }); - } - - public function name(): string - { - return $this->name; - } - - public function describe(): string - { - return sprintf('"%s" transformer', $this->name); - } - - /** - * @return Promise> - */ - private function getDiagnostics(TextDocumentItem $textDocument): Promise - { - return call(function () use ($textDocument) { - $phpactorTextDocument = TextDocumentConverter::fromLspTextItem($textDocument); - - return array_map(function (Diagnostic $diagnostic) { - $diagnostic->message = sprintf('%s (fix with "%s" code action)', $diagnostic->message, $this->title); - return $diagnostic; - }, DiagnosticsConverter::toLspDiagnostics( - $phpactorTextDocument, - yield $this->transformers->get($this->name)->diagnostics( - SourceCode::fromTextDocument(TextDocumentConverter::fromLspTextItem($textDocument)) - ) - )); - }); - } - - private function kind(): string - { - return 'quickfix.'.$this->name; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Converter/DiagnosticsConverter.php b/lib/Extension/LanguageServerCodeTransform/Converter/DiagnosticsConverter.php deleted file mode 100644 index 79f1028a91..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Converter/DiagnosticsConverter.php +++ /dev/null @@ -1,36 +0,0 @@ - new Range( - PositionConverter::byteOffsetToPosition($diagnostic->range()->start(), $textDocument->__toString()), - PositionConverter::byteOffsetToPosition($diagnostic->range()->end(), $textDocument->__toString()) - ), - 'message' => $diagnostic->message(), - 'source' => 'phpactor', - 'severity' => $diagnostic->severity() - ]); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php b/lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php deleted file mode 100644 index 7c32c21aa7..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php +++ /dev/null @@ -1,552 +0,0 @@ -registerCommands($container); - $this->registerCodeActions($container); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_REPORT_NON_EXISTING_NAMES => true, - ]); - $schema->setDescriptions([ - self::PARAM_REPORT_NON_EXISTING_NAMES => 'Show an error if a diagnostic name cannot be resolved - can produce false positives', - ]); - } - - private function registerCommands(ContainerBuilder $container): void - { - $container->register(NameImporter::class, function (Container $container) { - return new NameImporter($container->get(ImportName::class)); - }); - $container->register(ImportNameCommand::class, function (Container $container) { - return new ImportNameCommand( - $container->get(NameImporter::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(ClientApi::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => ImportNameCommand::NAME - ], - ]); - - $container->register(TransformCommand::class, function (Container $container) { - return new TransformCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->expect('code_transform.transformers', Transformers::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => TransformCommand::NAME - ], - ]); - - $container->register(CreateClassCommand::class, function (Container $container) { - return new CreateClassCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->expect(CodeTransformExtension::SERVICE_CLASS_GENERATORS, Generators::class), - $container->expect(ClassToFileExtension::SERVICE_CONVERTER, FileToClass::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => CreateClassCommand::NAME - ], - ]); - - $container->register(GenerateMemberCommand::class, function (Container $container) { - return new GenerateMemberCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(GenerateMember::class), - $container->get(TextDocumentLocator::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => GenerateMemberCommand::NAME - ], - ]); - - $container->register(ExtractMethodCommand::class, function (Container $container) { - return new ExtractMethodCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(ExtractMethod::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => ExtractMethodCommand::NAME - ], - ]); - - $container->register(ReplaceQualifierWithImportCommand::class, function (Container $container) { - return new ReplaceQualifierWithImportCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(ReplaceQualifierWithImport::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => ReplaceQualifierWithImportCommand::NAME - ], - ]); - - $container->register(ExtractConstantCommand::class, function (Container $container) { - return new ExtractConstantCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(ExtractConstant::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => ExtractConstantCommand::NAME - ], - ]); - $container->register('language_server_code_transform.generate_accessors_command', function (Container $container) { - return new PropertyAccessGeneratorCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->expect('code_transform.generate_accessor', PropertyAccessGenerator::class), - 'Generate accessors' - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => 'generate_accessors' - ], - ]); - - $container->register('language_server_code_transform.generate_mutators_command', function (Container $container) { - return new PropertyAccessGeneratorCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get('code_transform.generate_mutator'), - 'Generate mutators' - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => 'generate_mutators' - ], - ]); - - $container->register(ImportAllUnresolvedNamesCommand::class, function (Container $container) { - return new ImportAllUnresolvedNamesCommand( - $container->get(CandidateFinder::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(ImportNameCommand::class), - $container->get(ClientApi::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => ImportAllUnresolvedNamesCommand::NAME - ], - ]); - - $container->register(ExtractExpressionCommand::class, function (Container $container) { - return new ExtractExpressionCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(ExtractExpression::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => ExtractExpressionCommand::NAME - ], - ]); - - $container->register(GenerateDecoratorCommand::class, function (Container $container) { - return new GenerateDecoratorCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(GenerateDecorator::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => GenerateDecoratorCommand::NAME - ], - ]); - - $container->register(OverrideMethodCommand::class, function (Container $container) { - return new OverrideMethodCommand( - $container->get(ClientApi::class), - $container->get(OverrideMethod::class), - $container->get(OverridableMethodFinder::class), - $container->get(TextDocumentLocator::class) - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => OverrideMethodCommand::NAME - ], - ]); - } - - private function registerCodeActions(ContainerBuilder $container): void - { - $container->register(CandidateFinder::class, function (Container $container) { - return new CandidateFinder( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(SearchClient::class), - ); - }); - - $container->register(ReplaceQualifierWithImportProvider::class, function (Container $container) { - return new ReplaceQualifierWithImportProvider( - $container->get(ReplaceQualifierWithImport::class) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100], - ]); - - $container->register(ImportNameProvider::class, function (Container $container) { - return new ImportNameProvider( - $container->get(CandidateFinder::class), - $container->getParameter(self::PARAM_REPORT_NON_EXISTING_NAMES) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100], - ]); - $container->register(OverridableMethodFinder::class, function (Container $container) { - return new OverridableMethodFinder( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - ); - }); - $container->register(OverrideMethodProvider::class, function (Container $container) { - return new OverrideMethodProvider( - $container->get(OverridableMethodFinder::class), - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => ['priority' => -200], - ]); - - $container->register(TransformerCodeActionPovider::class.'promote_constructor_private', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'promote_constructor', - 'Promote Constructor (private)' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100] - ]); - - $container->register(TransformerCodeActionPovider::class.'promote_constructor_public', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'promote_constructor_public', - 'Promote Constructor (public)' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100] - ]); - - $container->register(TransformerCodeActionPovider::class.'complete_constructor_private', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'complete_constructor', - 'Complete Constructor (private)' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100] - ]); - - $container->register(TransformerCodeActionPovider::class.'complete_constructor_public', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'complete_constructor_public', - 'Complete Constructor (public)' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100] - ]); - $container->register(TransformerCodeActionPovider::class.'add_missing_class_generic', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'add_missing_class_generic', - 'Add missing class generic tag(s)' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(CreateClassProvider::class, function (Container $container) { - return new CreateClassProvider( - $container->get(CodeTransformExtension::SERVICE_CLASS_GENERATORS) - ); - }, [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('create-class', outsource: true), - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(CreateUnresolvableClassProvider::class, function (Container $container) { - return new CreateUnresolvableClassProvider( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(CodeTransformExtension::SERVICE_CLASS_GENERATORS), - $container->get(ClassToFileExtension::SERVICE_CONVERTER) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(CorrectUndefinedVariableCodeAction::class, function (Container $container) { - return new CorrectUndefinedVariableCodeAction( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(TransformerCodeActionPovider::class.'add_missing_properties', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'add_missing_properties', - 'Add missing properties' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(TransformerCodeActionPovider::class.'implement_contracts', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'implement_contracts', - 'Implement contracts' - ); - }, [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('implement-contracts', outsource: true), - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(TransformerCodeActionPovider::class.'fix_namespace_class_name', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'fix_namespace_class_name', - 'Fix PSR namespace and class name' - ); - }, [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('transformer', true), - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(TransformerCodeActionPovider::class.'add_missing_docblocks_return', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'add_missing_docblocks_return', - 'Add missing @return tags' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - $container->register(TransformerCodeActionPovider::class.'add_missing_params', function (Container $container) { - return new TransformerCodeActionPovider( - $container->get('code_transform.transformers'), - 'add_missing_params', - 'Add missing @param tags' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(TransformerCodeActionPovider::class.'add_missing_return_types', function (Container $container) { - return new TransformerCodeActionPovider( - $container->expect('code_transform.transformers', Transformers::class), - 'add_missing_return_types', - 'Add missing return types' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(TransformerCodeActionPovider::class.'add_override_attribute', function (Container $container) { - return new TransformerCodeActionPovider( - $container->expect('code_transform.transformers', Transformers::class), - 'add_override_attribute', - 'Add missing #[\Override] attributes' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(TransformerCodeActionPovider::class.'remove_unused_imports', function (Container $container) { - return new TransformerCodeActionPovider( - $container->expect('code_transform.transformers', Transformers::class), - 'remove_unused_imports', - 'Remove unused imports' - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100] - ]); - - $container->register(GenerateMemberProvider::class, function (Container $container) { - return new GenerateMemberProvider( - $container->get(MissingMemberFinder::class) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(ExtractMethodProvider::class, function (Container $container) { - return new ExtractMethodProvider( - $container->get(ExtractMethod::class) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - $container->register(ExtractConstantProvider::class, function (Container $container) { - return new ExtractConstantProvider( - $container->get(ExtractConstant::class) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register('language_server_code_transform.generate_accessors_provider', function (Container $container) { - return new PropertyAccessGeneratorProvider( - 'quickfix.generate_accessors', - 'generate_accessors', - 'accessor', - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register('language_server_code_transform.generate_mutators_provider', function (Container $container) { - return new PropertyAccessGeneratorProvider( - 'quickfix.generate_mutators', - 'generate_mutators', - 'mutator', - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(ExtractExpressionProvider::class, function (Container $container) { - return new ExtractExpressionProvider( - $container->get(ExtractExpression::class) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(ByteOffsetRefactorProvider::class.'.fill_object', function (Container $container) { - return new ByteOffsetRefactorProvider( - $container->get(WorseFillObject::class), - 'quickfix.fill.object', - 'Fill object', - 'fill new object construct with named parameters', - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100] - ]); - $container->register(ByteOffsetRefactorProvider::class.'.fill_match_arms', function (Container $container) { - return new ByteOffsetRefactorProvider( - $container->get(WorseFillMatchArms::class), - 'quickfix.fill.matchArms', - 'Fill match arms', - 'fill missing match arms for an enum', - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - $container->register(ByteOffsetRefactorProvider::class.'.here_doc_provider', function (Container $container) { - return new ByteOffsetRefactorProvider( - $container->get(TolerantHereDoc::class), - 'quickfix.here_doc_provider', - 'Convert HereDoc', - 'replace string with HereDoc', - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [ 'priority' => 100] - ]); - - $container->register(GenerateConstructorProvider::class, function (Container $container) { - return new GenerateConstructorProvider( - $container->get(GenerateConstructor::class), - $container->get(WorkspaceEditConverter::class), - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(GenerateDecoratorProvider::class, function (Container $container) { - return new GenerateDecoratorProvider( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class) - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/CreateClassCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/CreateClassCommand.php deleted file mode 100644 index dd79c2d83b..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/CreateClassCommand.php +++ /dev/null @@ -1,69 +0,0 @@ - - */ - public function __invoke(string $uri, string $transform): Promise - { - $documentChanges = []; - if (!$this->workspace->has($uri)) { - $textDocument = new TextDocumentItem($uri, 'php', 0, ''); - $documentChanges[] = new CreateFile('create', $uri, new CreateFileOptions(false, true)); - } else { - $textDocument = $this->workspace->get($uri); - } - $generator = $this->generators->get($transform); - assert($generator instanceof GenerateNew); - - $className = $this->fileToClass->fileToClassCandidates( - FilePath::fromString(TextDocumentUri::fromString($uri)->path()) - ); - - $sourceCode = $generator->generateNew(ClassName::fromString($className->best()->__toString())); - $textEdits = TextEdits::one( - TextEdit::create(0, PHP_INT_MAX, $sourceCode->__toString()) - ); - - $message = 'Class created'; - if (count($documentChanges)) { - $message = sprintf('Class registered at "%s"', $uri); - } - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits, $textDocument->text) - ]), $message); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractConstantCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractConstantCommand.php deleted file mode 100644 index e25b8be8f1..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractConstantCommand.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ - public function __invoke(string $uri, int $offset): Promise - { - $textDocument = $this->workspace->get($uri); - - try { - $textEdits = $this->extractConstant->extractConstant( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - $offset, - self::DEFAULT_VARIABLE_NAME - ); - } catch (TransformException $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits->textEdits(), $textDocument->text) - ]), 'Extract constant'); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractExpressionCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractExpressionCommand.php deleted file mode 100644 index da1bd3ddea..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractExpressionCommand.php +++ /dev/null @@ -1,52 +0,0 @@ - - */ - public function __invoke(string $uri, int $startOffset, int $endOffset): Promise - { - $textDocument = $this->workspace->get($uri); - - try { - $textEdits = $this->extractExpression->extractExpression( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - $startOffset, - $endOffset, - self::DEFAULT_VARIABLE_NAME - ); - } catch (TransformException $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits, $textDocument->text) - ]), 'Extract expression'); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractMethodCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractMethodCommand.php deleted file mode 100644 index e57980c9dc..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/ExtractMethodCommand.php +++ /dev/null @@ -1,52 +0,0 @@ - - */ - public function __invoke(string $uri, int $startOffset, int $endOffset): Promise - { - $textDocument = $this->workspace->get($uri); - - try { - $textEdits = $this->extractMethod->extractMethod( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - $startOffset, - $endOffset, - self::DEFAULT_METHOD_NAME - ); - } catch (TransformException $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits->textEdits(), $textDocument->text) - ]), 'Extract method'); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/GenerateDecoratorCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/GenerateDecoratorCommand.php deleted file mode 100644 index 253a2ad8f0..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/GenerateDecoratorCommand.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ - public function __invoke(string $uri, string $interfaceFQN): Promise - { - $textDocument = $this->workspace->get($uri); - $source = SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri); - - $textEdits = $this->generateDecorator->getTextEdits($source, $interfaceFQN); - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits, $textDocument->text) - ]), 'Generate decoration'); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/GenerateMemberCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/GenerateMemberCommand.php deleted file mode 100644 index b972fd8245..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/GenerateMemberCommand.php +++ /dev/null @@ -1,63 +0,0 @@ - - */ - public function __invoke(string $uri, int $offset): Promise - { - $document = $this->workspace->get($uri); - $sourceCode = SourceCode::fromStringAndPath( - $document->text, - $document->uri - ); - - $textEdits = null; - try { - $textEdits = $this->generateMember->generateMember($sourceCode, $offset); - } catch (TransformException $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } catch (NotFound $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } - - return $this->clientApi->workspace()->applyEdit( - new WorkspaceEdit([ - $textEdits->uri()->__toString() => TextEditConverter::toLspTextEdits( - $textEdits->textEdits(), - $this->locator->get($textEdits->uri())->__toString() - ) - ]), - 'Generate method' - ); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php deleted file mode 100644 index 04746622ec..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php +++ /dev/null @@ -1,105 +0,0 @@ - - */ - public function __invoke( - string $uri - ): Promise { - return call(function () use ($uri) { - $item = $this->workspace->get($uri); - foreach ((yield $this->candidateFinder->unresolved($item))->onlyUniqueNames() as $unresolvedName) { - assert($unresolvedName instanceof NameWithByteOffset); - $candidates = $this->candidates($this->candidateFinder->candidatesForUnresolvedName($unresolvedName)); - $candidate = yield $this->resolveCandidate($unresolvedName, $candidates); - if (null === $candidate) { - $this->client->window()->showMessage()->warning(sprintf( - 'Class "%s" has no candidates', - $unresolvedName->name()->__toString() - )); - continue; - } - - yield $this->importName->__invoke( - $uri, - $unresolvedName->byteOffset()->toInt(), - $unresolvedName->type(), - $candidate->candidateFqn() - ); - } - }); - } - - /** - * @return Promise - */ - private function resolveCandidate(NameWithByteOffset $unresolved, array $candidates): Promise - { - return call(function () use ($unresolved, $candidates) { - foreach ($candidates as $candidate) { - if (count($candidates) === 1) { - return $candidate; - } - break; - } - - if (count($candidates) === 0) { - return null; - } - - $choice = yield $this->client->window()->showMessageRequest()->info(sprintf( - 'Ambiguous class "%s":', - $unresolved->name()->__toString() - ), ...array_map(function (NameCandidate $candidate) { - return new MessageActionItem($candidate->candidateFqn()); - }, $candidates)); - - foreach ($candidates as $candidate) { - if ($candidate->candidateFqn() === $choice->title) { - return $candidate; - } - } - - return null; - }); - } - - /** - * @param Generator $candidates - */ - private function candidates(Generator $candidates): array - { - $filtered = []; - foreach ($candidates as $candidate) { - assert($candidate instanceof NameCandidate); - $filtered[$candidate->candidateFqn()] = $candidate; - } - - return array_values($filtered); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/ImportNameCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/ImportNameCommand.php deleted file mode 100644 index 7bb353e0a1..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/ImportNameCommand.php +++ /dev/null @@ -1,49 +0,0 @@ -workspace->get($uri); - $result = $this->nameImporter->__invoke($document, $offset, $type, $fqn, true, $alias); - - if ($result->isSuccess()) { - if (!$result->hasTextEdits()) { - return new Success(null); - } - - $textEdits = $result->getTextEdits(); - return $this->client->workspace()->applyEdit(new WorkspaceEdit([ - $uri => $textEdits - ]), 'Import class'); - } - - $error = $result->getError(); - $this->client->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php deleted file mode 100644 index d8da3853b7..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php +++ /dev/null @@ -1,119 +0,0 @@ - - */ - public function __invoke(string $uri): Promise - { - return call(function () use ($uri) { - $document = $this->locator->get(TextDocumentUri::fromString($uri)); - $sourceCode = SourceCode::fromStringAndPath( - $document->__toString(), - $document->uriOrThrow()->__toString(), - ); - - $method = yield $this->resolveClassMethodName($document); - - if (null === $method) { - return null; - } - - [$className, $methodName] = [ $method->class()->name()->__toString(), $method->name()]; - - $textEdits = null; - try { - $textEdits = $this->overrideMethod->overrideMethod($sourceCode, $className, $methodName); - } catch (TransformException $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } catch (NotFound $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } - - return $this->clientApi->workspace()->applyEdit( - new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits( - $textEdits, - $document->__toString(), - ) - ]), - 'Override method' - ); - }); - } - - /** - * @return Promise - */ - private function resolveClassMethodName(TextDocument $document): Promise - { - return call(function () use ($document) { - $methods = $this->finder->find($document); - usort($methods, function (ReflectionMethod $a, ReflectionMethod $b) { - return $a->name() <=> $b->name(); - }); - - $choice = yield $this->clientApi->window()->showMessageRequest()->info('Choose method:', ...array_map( - fn (ReflectionMethod $method) => new MessageActionItem($this->formatName($method)), - $methods - )); - - if ($choice === null) { - return null; - } - - - foreach ($methods as $method) { - if ($this->formatName($method) === $choice->title) { - return $method; - } - } - - return null; - }); - } - - private function formatName(ReflectionMethod $method): string - { - return sprintf( - '%s%s%s', - $method->class()->name()->short(), - $method->isStatic() ? '::' : '->', - $method->name() - ); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/PropertyAccessGeneratorCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/PropertyAccessGeneratorCommand.php deleted file mode 100644 index ce3ad4c56f..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/PropertyAccessGeneratorCommand.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ - public function __invoke(string $uri, int $startOffset, array $propertyNames): Promise - { - $textDocument = $this->workspace->get($uri); - - try { - $textEdits = $this->generateAccessor->generate( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - $propertyNames, - $startOffset - ); - } catch (TransformException $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits, $textDocument->text) - ]), $this->editLabel); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/ReplaceQualifierWithImportCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/ReplaceQualifierWithImportCommand.php deleted file mode 100644 index 9e1696ba3a..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/ReplaceQualifierWithImportCommand.php +++ /dev/null @@ -1,48 +0,0 @@ - - */ - public function __invoke(string $uri, int $offset): Promise - { - $textDocument = $this->workspace->get($uri); - - try { - $textEdits = $this->replaceQualifierWithImport->getTextEdits( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri), - $offset - ); - } catch (TransformException $error) { - $this->clientApi->window()->showMessage()->warning($error->getMessage()); - return new Success(null); - } - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits->textEdits(), $textDocument->text) - ]), 'Expand Class'); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/LspCommand/TransformCommand.php b/lib/Extension/LanguageServerCodeTransform/LspCommand/TransformCommand.php deleted file mode 100644 index 42d25bf8cf..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/LspCommand/TransformCommand.php +++ /dev/null @@ -1,48 +0,0 @@ - - */ - public function __invoke(string $uri, string $transform): Promise - { - return call(function () use ($uri, $transform) { - $textDocument = $this->workspace->get($uri); - $transformer = $this->transformers->get($transform); - assert($transformer instanceof Transformer); - $textEdits = yield $transformer->transform( - SourceCode::fromStringAndPath( - $textDocument->text, - $textDocument->uri - ), - ); - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits, $textDocument->text) - ]), 'Apply source code transformation'); - }); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php b/lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php deleted file mode 100644 index 9d190121b5..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php +++ /dev/null @@ -1,129 +0,0 @@ - - */ - public function unresolved(TextDocumentItem $item): Promise - { - return call(function () use ($item) { - $diagnostics = (yield $this->reflector->diagnostics(TextDocumentConverter::fromLspTextItem($item)))->byClass(UnresolvableNameDiagnostic::class); - assert(is_iterable($diagnostics)); - - return new NameWithByteOffsets(...array_map(function (UnresolvableNameDiagnostic $diagnostic): NameWithByteOffset { - return new NameWithByteOffset($diagnostic->name(), $diagnostic->range()->start(), $diagnostic->type()); - }, iterator_to_array($diagnostics))); - }); - } - - /** - * @return Promise> - */ - public function importCandidates( - TextDocumentItem $item - ): Promise { - return call(function () use ($item) { - $candidates = []; - $seen = []; - foreach (yield $this->unresolved($item) as $unresolvedName) { - assert($unresolvedName instanceof NameWithByteOffset); - $nameString = (string)$unresolvedName->name(); - if (isset($seen[$nameString])) { - continue; - } - $seen[$nameString] = true; - foreach ($this->candidatesForUnresolvedName($unresolvedName) as $candidate) { - assert($candidate instanceof NameCandidate); - $nameString = (string)$candidate->candidateFqn(); - if (isset($seen[$nameString])) { - continue; - } - $seen[$nameString] = true; - $candidates[] = $candidate; - } - } - return $candidates; - }); - } - - /** - * @return Generator - */ - public function candidatesForUnresolvedName(NameWithByteOffset $unresolvedName): Generator - { - if ($this->isUnresolvedGlobalFunction($unresolvedName)) { - yield new NameCandidate($unresolvedName, $unresolvedName->name()->head()->__toString()); - return; - } - assert($unresolvedName instanceof NameWithByteOffset); - - foreach ($this->findCandidates($unresolvedName) as $candidate) { - assert($candidate instanceof HasFullyQualifiedName); - - // skip constants for now - if ($candidate instanceof ConstantRecord) { - continue; - } - - $fqn = $candidate->fqn()->__toString(); - yield new NameCandidate($unresolvedName, $candidate->fqn()); - } - } - - private function isUnresolvedGlobalFunction(NameWithByteOffset $unresolvedName): bool - { - if ($unresolvedName->type() !== NameWithByteOffset::TYPE_FUNCTION) { - return false; - } - - try { - $s = $this->reflector->sourceCodeForFunction( - $unresolvedName->name()->head()->__toString() - ); - return true; - } catch (NotFound) { - } - - return false; - } - - /** - * @return Generator - */ - private function findCandidates(NameWithByteOffset $unresolvedName): Generator - { - yield from $this->client->search(Criteria::and( - Criteria::or( - Criteria::isConstant(), - Criteria::isClass(), - Criteria::isFunction() - ), - Criteria::exactShortName($unresolvedName->name()->head()->__toString()) - )); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameCandidate.php b/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameCandidate.php deleted file mode 100644 index 34c064f0f3..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameCandidate.php +++ /dev/null @@ -1,24 +0,0 @@ -candidateFqn; - } - - public function unresolvedName(): NameWithByteOffset - { - return $this->unresolvedName; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporter.php b/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporter.php deleted file mode 100644 index b3637d4bd9..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporter.php +++ /dev/null @@ -1,97 +0,0 @@ -text, - TextDocumentUri::fromString($document->uri)->__toString() - ); - - $nameImport = $type === 'function' ? - NameImport::forFunction($fqn, $alias) : - NameImport::forClass($fqn, $alias); - - try { - $textEdits = $this->importNameTextEdits($sourceCode, $offset, $nameImport, $updateReferences); - $lspTextEdits = TextEditConverter::toLspTextEdits($textEdits, $document->text); - return NameImporterResult::createResult($nameImport, $lspTextEdits); - } catch (NameAlreadyImportedException $error) { - if ($error->existingFQN() === $fqn) { - return $this->createResultForAlreadyImportedFQN($nameImport, $error); - } - - $name = FullyQualifiedName::fromString($fqn); - $prefix = 'Aliased'; - if (isset($name->toArray()[0])) { - $prefix = $name->toArray()[0]; - } - - return $this->__invoke($document, $offset, $type, $fqn, $updateReferences, $prefix . $error->name()); - } catch (AliasAlreadyUsedException $error) { - $prefix = 'Aliased'; - return $this->__invoke($document, $offset, $type, $fqn, $updateReferences, $prefix . $error->name()); - } catch (TransformException $error) { - return NameImporterResult::createErrorResult($error); - } - } - - private function importNameTextEdits( - SourceCode $sourceCode, - int $offset, - NameImport $nameImport, - bool $updateReferences - ): TextEdits { - $byteOffset = ByteOffset::fromInt($offset); - - if ($updateReferences) { - return $this->importName->importName($sourceCode, $byteOffset, $nameImport); - } - - return $this->importName->importNameOnly($sourceCode, $byteOffset, $nameImport); - } - - private function createResultForAlreadyImportedFQN( - NameImport $nameImport, - NameAlreadyImportedException $error - ): NameImporterResult { - $alias = null; - - if ($error->existingName() !== $nameImport->name()->head()->__toString()) { - $alias = $error->existingName(); - } - - $nameImport = $nameImport->type() === 'function' ? - NameImport::forFunction($error->existingFQN(), $alias) : - NameImport::forClass($error->existingFQN(), $alias); - - return NameImporterResult::createResult($nameImport, null); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporterResult.php b/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporterResult.php deleted file mode 100644 index 79cc3cb1e1..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporterResult.php +++ /dev/null @@ -1,75 +0,0 @@ -textEdits !== []; - } - - /** - * @return array|null - */ - public function getTextEdits(): ?array - { - return $this->textEdits; - } - - public function getNameImport(): ?NameImport - { - return $this->nameImport; - } - - public function isSuccess(): bool - { - return $this->success; - } - - public function isSuccessAndHasAliasedNameImport(): bool - { - return $this->isSuccess() === true - && $this->getNameImport() !== null - && $this->getNameImport()->alias() !== null; - } - - public function getError(): ?Throwable - { - return $this->error; - } - - public static function createEmptyResult(): NameImporterResult - { - return new NameImporterResult(true, null, null, null); - } - - /** - * @param array|null $textEdits - */ - public static function createResult( - NameImport $nameImport, - ?array $textEdits - ): NameImporterResult { - return new NameImporterResult(true, $nameImport, $textEdits, null); - } - - public static function createErrorResult(Throwable $error): NameImporterResult - { - return new NameImporterResult(false, null, null, $error); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Model/OverrideMethod/OverridableMethodFinder.php b/lib/Extension/LanguageServerCodeTransform/Model/OverrideMethod/OverridableMethodFinder.php deleted file mode 100644 index df6415a5e7..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Model/OverrideMethod/OverridableMethodFinder.php +++ /dev/null @@ -1,53 +0,0 @@ -reflector->reflectClassesIn($document) as $class) { - if (!$class instanceof ReflectionClass) { - continue; - } - $parent = $class->parent(); - if (!$parent) { - continue; - } - $methods = $parent->methods()->byVisibilities([ - Visibility::protected(), - Visibility::public(), - ]); - if ($methods->count() === 0) { - continue; - } - - $ownMethods = $class->methods()->belongingTo($class->name()); - foreach ($methods as $method) { - if ($ownMethods->has($method->name())) { - continue; - } - $method = $method->withClass($class); - if (!$method instanceof ReflectionMethod) { - continue; - } - $overrideables[] = $method; - } - } - - return array_values($overrideables); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Benchmark/CodeAction/ImportNameProviderBench.php b/lib/Extension/LanguageServerCodeTransform/Tests/Benchmark/CodeAction/ImportNameProviderBench.php deleted file mode 100644 index 03ee155e21..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Benchmark/CodeAction/ImportNameProviderBench.php +++ /dev/null @@ -1,124 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest( - <<<'EOT' - // File: Barfoo.php - provider = $this->container()->get(ImportNameProvider::class); - } - - /** - * @BeforeMethods({"setUp"}) - */ - public function benchDiagnostics(): void - { - $subject = $this->workspace()->getContents('subject.php'); - - [ $source, $offset ] = ExtractOffset::fromSource($subject); - - $cancel = (new CancellationTokenSource())->getToken(); - $this->provider->provideDiagnostics( - ProtocolFactory::textDocumentItem('file:///foobar', $subject), - $cancel - ); - } - - /** - * @BeforeMethods({"setUp"}) - */ - public function benchCodeActions(): void - { - $subject = $this->workspace()->getContents('subject.php'); - - [ $source, $offset ] = ExtractOffset::fromSource($subject); - $cancel = (new CancellationTokenSource())->getToken(); - - $this->provider->provideActionsFor( - ProtocolFactory::textDocumentItem('file:///foobar', $subject), - ProtocolFactory::range(0, 0, 0, 0), - $cancel - ); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Empty/empty b/lib/Extension/LanguageServerCodeTransform/Tests/Empty/empty deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Empty/sprintf.php b/lib/Extension/LanguageServerCodeTransform/Tests/Empty/sprintf.php deleted file mode 100644 index 4d58d5b27c..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Empty/sprintf.php +++ /dev/null @@ -1,5 +0,0 @@ -workspace()->put('index/.foo', ''); - $container = PhpactorContainer::fromExtensions([ - LoggingExtension::class, - LanguageServerExtension::class, - FilePathResolverExtension::class, - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - CodeTransformExtension::class, - LanguageServerCodeTransformExtension::class, - WorseReflectionExtension::class, - IndexerExtension::class, - LanguageServerIndexerExtension::class, - LanguageServerWorseReflectionExtension::class, - PhpExtension::class, - LanguageServerBridgeExtension::class, - TestLanguageServerSessionExtension::class, - ], array_merge([ - LanguageServerExtension::PARAM_DIAGNOSTIC_OUTSOURCE => false, - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ .'/../../', - WorseReflectionExtension::PARAM_STUB_DIR => __DIR__. '/Empty', - WorseReflectionExtension::PARAM_STUB_CACHE_DIR => __DIR__ . '/Workspace/wr-cache', - IndexerExtension::PARAM_STUB_PATHS => [__DIR__. '/Stub'], - CodeTransformExtension::PARAM_TEMPLATE_PATHS => [], - FilePathResolverExtension::PARAM_PROJECT_ROOT => $this->workspace()->path(), - IndexerExtension::PARAM_INDEX_PATH => $this->workspace()->path('index'), - LoggingExtension::PARAM_ENABLED => true, - IndexerExtension::PARAM_ENABLED_WATCHERS => [], - LanguageServerExtension::PARAM_DIAGNOSTIC_SLEEP_TIME => 0, - LanguageServerExtension::PARAM_ENABLE_TRUST_CHECK => false, - ], $config)); - - return $container; - } - - protected function workspace(): Workspace - { - return Workspace::create(__DIR__ . '/Workspace'); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/LanguageServerCodeTransformExtensionTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/LanguageServerCodeTransformExtensionTest.php deleted file mode 100644 index d692e3d7bb..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/LanguageServerCodeTransformExtensionTest.php +++ /dev/null @@ -1,15 +0,0 @@ -container(); - - foreach ($container->getServiceIds() as $serviceId) { - } - $this->addToAssertionCount(1); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Stub/Generator.php b/lib/Extension/LanguageServerCodeTransform/Tests/Stub/Generator.php deleted file mode 100644 index 570f357f69..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Stub/Generator.php +++ /dev/null @@ -1,18 +0,0 @@ -sessionExtension = new LanguageServerSessionExtension( - $transmitter, - ProtocolFactory::initializeParams() - ); - } - - - public function load(ContainerBuilder $container): void - { - $this->sessionExtension->load($container); - } - - - public function configure(Resolver $schema): void - { - $this->sessionExtension->configure($schema); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CorrectUndefinedVariableCodeActionTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CorrectUndefinedVariableCodeActionTest.php deleted file mode 100644 index 346547740e..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CorrectUndefinedVariableCodeActionTest.php +++ /dev/null @@ -1,24 +0,0 @@ -addDiagnosticProvider(new UndefinedVariableProvider())->build(); - $range = ProtocolFactory::range(0, 0, 10, 10); - $cancel = (new CancellationTokenSource())->getToken(); - $actions = wait((new CorrectUndefinedVariableCodeAction($reflector))->provideActionsFor($textDocument, $range, $cancel)); - self::assertCount(1, $actions); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php deleted file mode 100644 index f62ca11977..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php +++ /dev/null @@ -1,93 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest($manifest); - - $tester = $this->container([])->get(LanguageServerBuilder::class)->tester( - ProtocolFactory::initializeParams($this->workspace()->path()) - ); - $tester->initialize(); - assert($tester instanceof LanguageServerTester); - - $subject = $this->workspace()->getContents('subject.php'); - [ $source, $offset ] = ExtractOffset::fromSource($subject); - - $tester->textDocument()->open('file:///foobar', $source); - - $result = $tester->requestAndWait(CodeActionRequest::METHOD, new CodeActionParams( - ProtocolFactory::textDocumentIdentifier('file:///foobar'), - new Range( - ProtocolFactory::position(0, 0), - PositionConverter::intByteOffsetToPosition((int)$offset, $source) - ), - new CodeActionContext([]) - )); - - $tester->assertSuccess($result); - - $tester->textDocument()->save('file:///foobar'); - - $result = $tester->requestAndWait(CodeActionRequest::METHOD, new CodeActionParams( - ProtocolFactory::textDocumentIdentifier('file:///foobar'), - new Range( - ProtocolFactory::position(0, 0), - PositionConverter::intByteOffsetToPosition((int)$offset, $source) - ), - new CodeActionContext([]) - )); - - $tester->assertSuccess($result); - - self::assertCount($expectedCount, $result->result, 'Number of code actions'); - - $diagnostics = $tester->transmitter()->filterByMethod('textDocument/publishDiagnostics')->shiftNotification(); - self::assertNotNull($diagnostics); - $diagnostics = $diagnostics->params['diagnostics']; - self::assertEquals($expectedDiagnosticCount, count($diagnostics), 'Number of diagnostics'); - } - - /** - * @return Generator - */ - public static function provideClassCreateProvider(): Generator - { - yield 'empty file' => [ - <<<'EOT' - // File: subject.php - - EOT - , 4, 0 - ]; - - yield 'non empty file' => [ - <<<'EOT' - // File: subject.php - prophesize(GenerateNew::class); - $classToFile = $this->prophesize(ClassToFile::class); - - $classToFile->classToFileCandidates( - ClassName::fromString('Foo') - )->willReturn(FilePathCandidates::fromFilePaths([FilePath::fromString('/foo')])); - - $reflector = ReflectorBuilder::create()->addDiagnosticProvider(new UnresolvableNameProvider(false))->build(); - - $provider = new CreateUnresolvableClassProvider( - $reflector, - new Generators([ - 'foobar' => $generateNew->reveal(), - ]), - $classToFile->reveal() - ); - $actions = wait($provider->provideActionsFor( - ProtocolFactory::textDocumentItem('file:///foo', $source), - RangeConverter::toLspRange(ByteOffsetRange::fromInts((int)$start, (int)$end), $source), - (new CancellationTokenSource())->getToken(), - )); - $assertion(...$actions); - } - - /** - * @return Generator - */ - public static function provideCodeAction(): Generator - { - yield 'empty file' => [ - '<<>?php <>', - function (CodeAction ...$actions): void { - self::assertCount(0, $actions); - } - ]; - yield 'In range' => [ - 'o<>();', - function (CodeAction ...$actions): void { - self::assertCount(1, $actions); - self::assertEquals('Create foobar file for "Foo"', $actions[0]->title); - } - ]; - yield 'Out of range' => [ - ' <>new Foo();', - function (CodeAction ...$actions): void { - self::assertCount(0, $actions); - } - ]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractConstantProviderTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractConstantProviderTest.php deleted file mode 100644 index 919e65f4e7..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractConstantProviderTest.php +++ /dev/null @@ -1,93 +0,0 @@ - - */ - private ObjectProphecy $extractConstant; - - public function setUp(): void - { - $this->extractConstant = $this->prophesize(ExtractConstant::class); - } - - #[DataProvider('provideActionsData')] - public function testProvideActions(bool $shouldSucceed, array $expectedValue): void - { - $textDocumentItem = new TextDocumentItem(self::EXAMPLE_FILE, 'php', 1, self::EXAMPLE_SOURCE); - $range = ProtocolFactory::range(0, 0, 0, 5); - - $this->extractConstant - ->canExtractConstant( - SourceCode::fromStringAndPath($textDocumentItem->text, $textDocumentItem->uri), - $range->start->character, - ) - ->willReturn($shouldSucceed) - ->shouldBeCalled(); - - $cancel = (new CancellationTokenSource())->getToken(); - $this->assertEquals( - $expectedValue, - wait($this->createProvider()->provideActionsFor( - $textDocumentItem, - $range, - $cancel - )) - ); - } - - public static function provideActionsData(): Generator - { - yield 'Fail' => [ - false, - [] - ]; - yield 'Success' => [ - true, - [ - CodeAction::fromArray([ - 'title' => 'Extract constant', - 'kind' => ExtractConstantProvider::KIND, - 'diagnostics' => [], - 'command' => new Command( - 'Extract constant', - ExtractConstantCommand::NAME, - [ - self::EXAMPLE_FILE, - 0, - 5 - ] - ) - ]) - ] - ]; - } - - private function createProvider(): ExtractConstantProvider - { - return new ExtractConstantProvider($this->extractConstant->reveal()); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractExpressionProviderTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractExpressionProviderTest.php deleted file mode 100644 index 60cac62b84..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractExpressionProviderTest.php +++ /dev/null @@ -1,94 +0,0 @@ - - */ - private ObjectProphecy $extractExpression; - - public function setUp(): void - { - $this->extractExpression = $this->prophesize(ExtractExpression::class); - } - - #[DataProvider('provideActionsData')] - public function testProvideActions(bool $shouldSucceed, array $expectedValue): void - { - $textDocumentItem = new TextDocumentItem(self::EXAMPLE_FILE, 'php', 1, self::EXAMPLE_SOURCE); - $range = ProtocolFactory::range(0, 0, 0, 5); - - $this->extractExpression - ->canExtractExpression( - SourceCode::fromStringAndPath($textDocumentItem->text, $textDocumentItem->uri), - $range->start->character, - $range->end->character - ) - ->willReturn($shouldSucceed) - ->shouldBeCalled(); - - $cancel = (new CancellationTokenSource())->getToken(); - $this->assertEquals( - $expectedValue, - wait($this->createProvider()->provideActionsFor( - $textDocumentItem, - $range, - $cancel - )) - ); - } - - public static function provideActionsData(): Generator - { - yield 'Fail' => [ - false, - [] - ]; - yield 'Success' => [ - true, - [ - CodeAction::fromArray([ - 'title' => 'Extract expression', - 'kind' => ExtractExpressionProvider::KIND, - 'diagnostics' => [], - 'command' => new Command( - 'Extract method', - ExtractExpressionCommand::NAME, - [ - self::EXAMPLE_FILE, - 0, - 5 - ] - ) - ]) - ] - ]; - } - - private function createProvider(): ExtractExpressionProvider - { - return new ExtractExpressionProvider($this->extractExpression->reveal()); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractMethodProviderTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractMethodProviderTest.php deleted file mode 100644 index 45984d87e5..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractMethodProviderTest.php +++ /dev/null @@ -1,92 +0,0 @@ -extractMethod = $this->prophesize(ExtractMethod::class); - } - - #[DataProvider('provideActionsData')] - public function testProvideActions(bool $shouldSucceed, array $expectedValue): void - { - $textDocumentItem = new TextDocumentItem(self::EXAMPLE_FILE, 'php', 1, self::EXAMPLE_SOURCE); - $range = ProtocolFactory::range(0, 0, 0, 5); - - $this->extractMethod - ->canExtractMethod( - SourceCode::fromStringAndPath($textDocumentItem->text, $textDocumentItem->uri), - $range->start->character, - $range->end->character - ) - ->willReturn($shouldSucceed) - ->shouldBeCalled(); - - $cancel = (new CancellationTokenSource())->getToken(); - $this->assertEquals( - $expectedValue, - wait($this->createProvider()->provideActionsFor( - $textDocumentItem, - $range, - $cancel - )) - ); - } - - public static function provideActionsData(): Generator - { - yield 'Fail' => [ - false, - [] - ]; - yield 'Success' => [ - true, - [ - CodeAction::fromArray([ - 'title' => 'Extract method', - 'kind' => ExtractMethodProvider::KIND, - 'diagnostics' => [], - 'command' => new Command( - 'Extract method', - ExtractMethodCommand::NAME, - [ - self::EXAMPLE_FILE, - 0, - 5 - ] - ) - ]) - ] - ]; - } - - private function createProvider(): ExtractMethodProvider - { - // @phpstan-ignore-next-line - return new ExtractMethodProvider($this->extractMethod->reveal()); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateDecoratorProviderTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateDecoratorProviderTest.php deleted file mode 100644 index 992c9fee1e..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateDecoratorProviderTest.php +++ /dev/null @@ -1,100 +0,0 @@ -workspace()->reset(); - [$source, $offset] = ExtractOffset::fromSource($source); - - $tester = $this->container([])->get(LanguageServerBuilder::class)->tester( - ProtocolFactory::initializeParams($this->workspace()->path()) - ); - $tester->textDocument()->open('file:///foobar', $source); - $tester->initialize(); - - $result = $tester->requestAndWait(CodeActionRequest::METHOD, new CodeActionParams( - ProtocolFactory::textDocumentIdentifier('file:///foobar'), - new Range( - PositionConverter::intByteOffsetToPosition((int)$offset, $source), - PositionConverter::intByteOffsetToPosition((int)$offset, $source) - ), - new CodeActionContext([]) - )); - self::assertNotNull($result); - $tester->assertSuccess($result); - $actions = array_filter((array)$result->result, function (mixed $action) { - assert($action instanceof CodeAction); - return $action->kind === GenerateDecoratorProvider::KIND; - }); - - self::assertCount($expectedCount, $actions, 'Number of code actions'); - } - - /** - * @return Generator - */ - public static function provideGenerateDecoratorProvider(): Generator - { - yield 'class with no interfaces' => [ - <<<'EOT' - baz {} - - EOT - , 0 - ]; - - yield 'class with one interface' => [ - <<<'EOT' - meInterface {} - - EOT - , 1 - ]; - - yield 'interface provides no actions' => [ - <<<'EOT' - omeInterface {public function foo(): void {}} - - EOT - , 0 - ]; - - yield 'class with multiple interfaces' => [ - <<<'EOT' - herInterface {} - - EOT - , 0 - ]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php deleted file mode 100644 index 82e7cde040..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php +++ /dev/null @@ -1,143 +0,0 @@ - - */ - private ObjectProphecy $finder; - - protected function setUp(): void - { - $this->finder = $this->prophesize(MissingMemberFinder::class); - } - - #[DataProvider('provideDiagnosticsTestData')] - public function testDiagnostics(array $missingMethods, array $expectedDiagnostics): void - { - $this->finder->find(Argument::type(TextDocument::class))->willReturn(new Success($missingMethods)); - $provider = $this->createProvider(); - - $cancel = (new CancellationTokenSource())->getToken(); - self::assertEquals( - $expectedDiagnostics, - wait($provider->provideDiagnostics( - new TextDocumentItem(self::EXAMPLE_FILE, 'php', 1, self::EXAMPLE_SOURCE), - $cancel - )) - ); - } - - /** - * @return Generator, array})> - */ - public static function provideDiagnosticsTestData(): Generator - { - yield 'No missing methods' => [ - [], - [] - ]; - - yield 'Missing method' => [ - [ - new MissingMember(self::EXAMPLE_SOURCE, ByteOffsetRange::fromInts(0, 5), 'method') - ], - [ - new Diagnostic( - range: ProtocolFactory::range(0, 0, 0, 5), - message: 'Method "foobar" does not exist', - severity: DiagnosticSeverity::WARNING, - source: 'phpactor', - ) - ] - ]; - } - - #[DataProvider('provideActionsTestData')] - public function testProvideActions(array $missingMethods, array $expectedActions): void - { - $this->finder->find(Argument::type(TextDocument::class))->willReturn(new Success($missingMethods)); - $provider = $this->createProvider(); - $cancel = (new CancellationTokenSource())->getToken(); - self::assertEquals( - $expectedActions, - wait($provider->provideActionsFor( - new TextDocumentItem(self::EXAMPLE_FILE, 'php', 1, self::EXAMPLE_SOURCE), - ProtocolFactory::range(0, 0, 0, 0), - $cancel - )) - ); - } - - /** - * @return Generator, array})> - */ - public static function provideActionsTestData(): Generator - { - yield 'No missing methods' => [ - [], - [] - ]; - - yield 'Missing method' => [ - [ - new MissingMember(self::EXAMPLE_SOURCE, ByteOffsetRange::fromInts(0, 5), 'method') - ], - [ - CodeAction::fromArray([ - 'title' => 'Fix "Method "foobar" does not exist"', - 'kind' => GenerateMemberProvider::KIND, - 'diagnostics' => [ - new Diagnostic( - range: ProtocolFactory::range(0, 0, 0, 5), - message: 'Method "foobar" does not exist', - severity: DiagnosticSeverity::WARNING, - source: 'phpactor', - ) - ], - 'command' => new Command( - 'Generate member', - GenerateMemberCommand::NAME, - [ - self::EXAMPLE_FILE, - 0 - ] - ) - ]) - ] - ]; - } - - private function createProvider(): GenerateMemberProvider - { - return new GenerateMemberProvider($this->finder->reveal()); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ImportNameProviderTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ImportNameProviderTest.php deleted file mode 100644 index 3b684bf7e8..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ImportNameProviderTest.php +++ /dev/null @@ -1,210 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest($manifest); - - $tester = $this->container([ - WorseReflectionExtension::PARAM_IMPORT_GLOBALS => $imprtGlobals, - LanguageServerCodeTransformExtension::PARAM_REPORT_NON_EXISTING_NAMES => true, - LanguageServerExtension::PARAM_CODE_ACTION_OUTSOURCE => false, - ])->get(LanguageServerBuilder::class)->tester( - ProtocolFactory::initializeParams($this->workspace()->path()) - ); - - assert($tester instanceof LanguageServerTester); - $subject = $this->workspace()->getContents('subject.php'); - [ $source, $offset ] = ExtractOffset::fromSource($subject); - - $tester->textDocument()->open('file:///foobar', $source); - $tester->initialize(); - - // give the indexer a chance to index - wait(delay(10)); - - $result = $tester->requestAndWait(CodeActionRequest::METHOD, new CodeActionParams( - ProtocolFactory::textDocumentIdentifier('file:///foobar'), - new Range( - ProtocolFactory::position(0, 0), - PositionConverter::intByteOffsetToPosition((int)$offset, $source) - ), - new CodeActionContext([]) - )); - self::assertNotNull($result); - $tester->assertSuccess($result); - - $transmitter = $tester->transmitter()->filterByMethod('textDocument/publishDiagnostics'); - $diagnostics = $transmitter->shiftNotification(); - $diagnostics = $transmitter->shiftNotification(); - $diagnostics = $diagnostics->params['diagnostics'] ?? []; - $assertion($result->result, $diagnostics); - } - - /** - * @return Generator - */ - public static function provideImportProvider(): Generator - { - // this test is very flakey - //yield 'code action + diagnostic for non-imported name' => [ - // <<<'EOT' - // // File: subject.php - // [ - <<<'EOT' - // File: subject.php - [ - <<<'EOT' - // File: subject.php - [ - <<<'EOT' - // File: subject.php - [ - <<<'EOT' - // File: subject.php - [ - <<<'EOT' - // File: subject.php - [ - <<<'EOT' - // File: subject.php - [ - <<<'EOT' - // File: functions.php - // File: subject.php - title); - self::assertEquals('Import function "array_keys"', $codeActions[2]->title); - self::assertEquals('Import all unresolved names', $codeActions[0]->title); - self::assertCount(2, $diagnostics); - }, true - ]; - - yield 'constant' => [ - <<<'EOT' - // File: subject.php - workspace()->reset(); - $this->workspace()->loadManifest($manifest); - - - [ $source, $offset ] = ExtractOffset::fromSource($this->workspace()->getContents('subject.php')); - $provider = new OverrideMethodProvider(new OverridableMethodFinder(ReflectorBuilder::create()->addLocator( - new BruteForceSourceLocator(ReflectorBuilder::create()->build(), $this->workspace()->path()) - )->build())); - - $cancel = (new CancellationTokenSource())->getToken(); - $item = new TextDocumentItem('/test.php', 'php', 1, $source); - $codeActions = wait($provider->provideActionsFor( - $item, - ProtocolFactory::range(1, 1, 1, 1), - $cancel, - )); - $assertion($codeActions, $codeActions); - } - - /** - * @return Generator - */ - public static function provideOverrideMethod(): Generator - { - yield 'no parent class' => [ - <<<'EOT' - // File: subject.php - [ - <<<'EOT' - // File: foobar.php - [ - <<<'EOT' - // File: foobar.php - [ - <<<'EOT' - // File: foobar.php - createProvider($source); - - $cancel = (new CancellationTokenSource())->getToken(); - self::assertEquals( - $expectedActions, - wait($provider->provideActionsFor( - new TextDocumentItem(self::EXAMPLE_FILE, 'php', 1, $source), - RangeConverter::toLspRange(ByteOffsetRange::fromInts((int)$start, (int)$end), $source), - $cancel - )) - ); - } - - public static function provideActionsTestData(): Generator - { - yield 'provide actions' => [ - 'private $foo;<> }', - [ - CodeAction::fromArray([ - 'title' => 'Generate 1 accessor(s)', - 'kind' => 'quickfix.generate_accessors', - 'command' => new Command( - 'Generate 1 accessor(s)', - 'generate_accessors', - [ - self::EXAMPLE_FILE, - 18, - ['foo'], - ] - ) - ]) - ] - ]; - } - - private function createProvider(string $sourceCode): PropertyAccessGeneratorProvider - { - $reflector = ReflectorBuilder::create()->addSource($sourceCode)->build(); - return new PropertyAccessGeneratorProvider( - 'quickfix.generate_accessors', - 'generate_accessors', - 'accessor', - $reflector, - ); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ReplaceQualifierWithImportProviderTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ReplaceQualifierWithImportProviderTest.php deleted file mode 100644 index 546f888d0f..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ReplaceQualifierWithImportProviderTest.php +++ /dev/null @@ -1,92 +0,0 @@ - - */ - private ObjectProphecy $replaceQualifierWithImport; - - public function setUp(): void - { - $this->replaceQualifierWithImport = $this->prophesize(ReplaceQualifierWithImport::class); - } - - #[DataProvider('provideActionsData')] - public function testProvideActions(bool $shouldSucceed, array $expectedValue): void - { - $textDocumentItem = new TextDocumentItem(self::EXAMPLE_FILE, 'php', 1, self::EXAMPLE_SOURCE); - $range = ProtocolFactory::range(0, 0, 0, 5); - - $this->replaceQualifierWithImport - ->canReplaceWithImport( - SourceCode::fromStringAndPath($textDocumentItem->text, $textDocumentItem->uri), - $range->start->character, - ) - ->willReturn($shouldSucceed) - ->shouldBeCalled(); - - $cancel = (new CancellationTokenSource())->getToken(); - $this->assertEquals( - $expectedValue, - wait($this->createProvider()->provideActionsFor( - $textDocumentItem, - $range, - $cancel - )) - ); - } - - public static function provideActionsData(): Generator - { - yield 'Fail' => [ - false, - [] - ]; - yield 'Success' => [ - true, - [ - CodeAction::fromArray([ - 'title' => 'Replace qualifier with import', - 'kind' => ReplaceQualifierWithImportProvider::KIND, - 'diagnostics' => [], - 'command' => new Command( - 'Replace qualifier with import', - ReplaceQualifierWithImportCommand::NAME, - [ - self::EXAMPLE_FILE, - 0 - ] - ) - ]) - ] - ]; - } - - private function createProvider(): ReplaceQualifierWithImportProvider - { - return new ReplaceQualifierWithImportProvider($this->replaceQualifierWithImport->reveal()); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/CreateClassCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/CreateClassCommandTest.php deleted file mode 100644 index f89af2f014..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/CreateClassCommandTest.php +++ /dev/null @@ -1,98 +0,0 @@ -createTester(); - $tester->textDocument()->open('file:///foobar', 'foobar'); - $promise = $tester->workspace()->executeCommand('create_class', [ - 'file:///foobar', - self::EXAMPLE_VARIANT - ]); - $watcher->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - $response = wait($promise); - self::assertInstanceOf(ResponseMessage::class, $response); - self::assertInstanceOf(ApplyWorkspaceEditResult::class, $response->result); - } - - public function testAppliesTransformForNonExistingClass(): void - { - [$tester, $watcher] = $this->createTester(); - $promise = $tester->workspace()->executeCommand('create_class', [ - 'file:///foobar', - self::EXAMPLE_VARIANT - ]); - $watcher->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - $response = wait($promise); - self::assertInstanceOf(ResponseMessage::class, $response); - self::assertInstanceOf(ApplyWorkspaceEditResult::class, $response->result); - } - - /** - * @return array{LanguageServerTester,TestResponseWatcher} - */ - private function createTester(): array - { - $generator = new TestGenerator(); - $generators = new Generators([ - self::EXAMPLE_VARIANT => $generator - ]); - $fileToClass = new TestFileToClass(); - $tester = LanguageServerTesterBuilder::create(); - $tester->addCommand('create_class', new CreateClassCommand( - $tester->clientApi(), - $tester->workspace(), - $generators, - $fileToClass - )); - $watcher = $tester->responseWatcher(); - $tester = $tester->build(); - return [$tester, $watcher]; - } -} - -class TestGenerator implements GenerateNew -{ - public const EXAMPLE_TEXT = 'hello'; - public const EXAMPLE_PATH = '/path'; - - - public function generateNew(ClassName $targetName): SourceCode - { - return SourceCode::fromStringAndPath(self::EXAMPLE_TEXT, self::EXAMPLE_PATH); - } -} - -class TestFileToClass implements FileToClass -{ - public const TEST_CLASS_NAME = 'Foobar'; - - public function fileToClassCandidates(FilePath $filePath): ClassNameCandidates - { - return ClassNameCandidates::fromClassNames([ - PhpactorClassName::fromString(self::TEST_CLASS_NAME) - ]); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php deleted file mode 100644 index 83336bcd1c..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php +++ /dev/null @@ -1,113 +0,0 @@ -prophesize(ExtractConstant::class); - $extractConstant->extractConstant( - Argument::type(SourceCode::class), - self::EXAMPLE_OFFSET, - ExtractConstantCommand::DEFAULT_VARIABLE_NAME - ) - ->shouldBeCalled() - ->willReturn($textEdits); - - [$tester, $builder] = $this->createTester($extractConstant); - $tester->workspace()->executeCommand('extract_constant', [self::EXAMPLE_URI, self::EXAMPLE_OFFSET]); - $builder->responseWatcher()->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - - $applyEdit = $builder->transmitter()->filterByMethod('workspace/applyEdit')->shiftRequest(); - - self::assertNotNull($applyEdit); - self::assertEquals([ - 'edit' => new WorkspaceEdit([ - self::EXAMPLE_URI => TextEditConverter::toLspTextEdits( - $textEdits->textEdits(), - self::EXAMPLE_SOURCE - ) - ]), - 'label' => 'Extract constant' - ], $applyEdit->params); - } - - #[DataProvider('provideExceptions')] - public function testFailedCall(Exception $exception): void - { - /** @var ObjectProphecy $extractConstant */ - $extractConstant = $this->prophesize(ExtractConstant::class); - $extractConstant->extractConstant( - Argument::type(SourceCode::class), - self::EXAMPLE_OFFSET, - ExtractConstantCommand::DEFAULT_VARIABLE_NAME - ) - ->shouldBeCalled() - ->willThrow($exception); - - [$tester, $builder] = $this->createTester($extractConstant); - $tester->workspace()->executeCommand('extract_constant', [self::EXAMPLE_URI, self::EXAMPLE_OFFSET]); - $showMessage = $builder->transmitter()->filterByMethod('window/showMessage')->shiftNotification(); - - self::assertNotNull($showMessage); - self::assertEquals([ - 'type' => MessageType::WARNING, - 'message' => $exception->getMessage() - ], $showMessage->params); - } - - public static function provideExceptions(): array - { - return [ - TransformException::class => [ new TransformException('Error message!') ], - ]; - } - - /** - * @param ObjectProphecy $extractConstant - */ - private function createTester(ObjectProphecy $extractConstant): array - { - $builder = LanguageServerTesterBuilder::createBare() - ->enableTextDocuments() - ->enableCommands(); - $builder->addCommand(ExtractConstantCommand::NAME, new ExtractConstantCommand( - $builder->clientApi(), - $builder->workspace(), - $extractConstant->reveal() - )); - - $tester = $builder->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_SOURCE); - - return [$tester, $builder]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php deleted file mode 100644 index aed14a7aa2..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php +++ /dev/null @@ -1,104 +0,0 @@ -prophesize(ExtractExpression::class); - $extractExpression->extractExpression(Argument::type(SourceCode::class), 0, self::EXAMPLE_OFFSET, ExtractExpressionCommand::DEFAULT_VARIABLE_NAME) - ->shouldBeCalled() - ->willReturn($textEdits); - - [$tester, $builder] = $this->createTester($extractExpression); - $tester->workspace()->executeCommand('extract_expression', [self::EXAMPLE_URI, 0, self::EXAMPLE_OFFSET]); - $builder->responseWatcher()->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - - $applyEdit = $builder->transmitter()->filterByMethod('workspace/applyEdit')->shiftRequest(); - - self::assertNotNull($applyEdit); - self::assertEquals([ - 'edit' => new WorkspaceEdit([ - self::EXAMPLE_URI => TextEditConverter::toLspTextEdits( - $textEdits, - self::EXAMPLE_SOURCE - ) - ]), - 'label' => 'Extract expression' - ], $applyEdit->params); - } - - #[DataProvider('provideExceptions')] - public function testFailedCall(Exception $exception): void - { - $extractExpression = $this->prophesize(ExtractExpression::class); - $extractExpression->extractExpression(Argument::type(SourceCode::class), 0, self::EXAMPLE_OFFSET, ExtractExpressionCommand::DEFAULT_VARIABLE_NAME) - ->shouldBeCalled() - ->willThrow($exception); - - [$tester, $builder] = $this->createTester($extractExpression); - $tester->workspace()->executeCommand('extract_expression', [self::EXAMPLE_URI, 0, self::EXAMPLE_OFFSET]); - $showMessage = $builder->transmitter()->filterByMethod('window/showMessage')->shiftNotification(); - - self::assertNotNull($showMessage); - self::assertEquals([ - 'type' => MessageType::WARNING, - 'message' => $exception->getMessage() - ], $showMessage->params); - } - - /** - * @return Generator - */ - public static function provideExceptions(): Generator - { - yield TransformException::class => [ new TransformException('Error message!') ]; - } - - /** - * @param ObjectProphecy $extractExpression - */ - private function createTester(ObjectProphecy $extractExpression): array - { - $builder = LanguageServerTesterBuilder::createBare() - ->enableTextDocuments() - ->enableCommands(); - $builder->addCommand('extract_expression', new ExtractExpressionCommand( - $builder->clientApi(), - $builder->workspace(), - $extractExpression->reveal() - )); - - $tester = $builder->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_SOURCE); - - return [$tester, $builder]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php deleted file mode 100644 index fc95426feb..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php +++ /dev/null @@ -1,107 +0,0 @@ -prophesize(ExtractMethod::class); - $extractMethod->extractMethod(Argument::type(SourceCode::class), 0, self::EXAMPLE_OFFSET, ExtractMethodCommand::DEFAULT_METHOD_NAME) - ->shouldBeCalled() - ->willReturn($textEdits); - - [$tester, $builder] = $this->createTester($extractMethod); - $tester->workspace()->executeCommand('extract_method', [self::EXAMPLE_URI, 0, self::EXAMPLE_OFFSET]); - $builder->responseWatcher()->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - - $applyEdit = $builder->transmitter()->filterByMethod('workspace/applyEdit')->shiftRequest(); - - self::assertNotNull($applyEdit); - self::assertEquals([ - 'edit' => new WorkspaceEdit([ - (string)$textEdits->uri() => TextEditConverter::toLspTextEdits( - $textEdits->textEdits(), - self::EXAMPLE_SOURCE - ) - ]), - 'label' => 'Extract method' - ], $applyEdit->params); - } - - #[DataProvider('provideExceptions')] - public function testFailedCall(Exception $exception): void - { - $extractMethod = $this->prophesize(ExtractMethod::class); - $extractMethod->extractMethod(Argument::type(SourceCode::class), 0, self::EXAMPLE_OFFSET, ExtractMethodCommand::DEFAULT_METHOD_NAME) - ->shouldBeCalled() - ->willThrow($exception); - - [$tester, $builder] = $this->createTester($extractMethod); - $tester->workspace()->executeCommand('extract_method', [self::EXAMPLE_URI, 0, self::EXAMPLE_OFFSET]); - $showMessage = $builder->transmitter()->filterByMethod('window/showMessage')->shiftNotification(); - - self::assertNotNull($showMessage); - self::assertEquals([ - 'type' => MessageType::WARNING, - 'message' => $exception->getMessage() - ], $showMessage->params); - } - - /** - * @return Generator - */ - public static function provideExceptions(): Generator - { - yield TransformException::class => [ new TransformException('Error message!') ]; - } - - - private function createTester(ObjectProphecy $extractMethod): array - { - $builder = LanguageServerTesterBuilder::createBare() - ->enableTextDocuments() - ->enableCommands(); - $builder->addCommand('extract_method', new ExtractMethodCommand( - $builder->clientApi(), - $builder->workspace(), - $extractMethod->reveal() - )); - - $tester = $builder->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_SOURCE); - - return [$tester, $builder]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateDecoratorCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateDecoratorCommandTest.php deleted file mode 100644 index ecf6026e0d..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateDecoratorCommandTest.php +++ /dev/null @@ -1,78 +0,0 @@ -prophesize(GenerateDecorator::class); - $generateAccessors->getTextEdits(Argument::type(SourceCode::class), 'SomeInterface') - ->shouldBeCalled() - ->willReturn($textEdits); - - [$tester, $builder] = $this->createTester($generateAccessors); - $tester->workspace()->executeCommand('generate_decorator', [ - self::EXAMPLE_URI, - 'SomeInterface' - ]); - $builder->responseWatcher()->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - - $applyEdit = $builder->transmitter() - ->filterByMethod('workspace/applyEdit') - ->shiftRequest(); - - self::assertNotNull($applyEdit); - self::assertEquals([ - 'edit' => new WorkspaceEdit([ - self::EXAMPLE_URI => TextEditConverter::toLspTextEdits( - $textEdits, - self::EXAMPLE_SOURCE - ) - ]), - 'label' => 'Generate decoration' - ], $applyEdit->params); - } - - /** - * @return array{LanguageServerTester,LanguageServerTesterBuilder} - */ - private function createTester(ObjectProphecy $generateAccessors): array - { - $builder = LanguageServerTesterBuilder::createBare() - ->enableTextDocuments() - ->enableCommands(); - $builder->addCommand('generate_decorator', new GenerateDecoratorCommand( - $builder->clientApi(), - $builder->workspace(), - $generateAccessors->reveal(), - )); - - $tester = $builder->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_SOURCE); - - return [$tester, $builder]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateMethodCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateMethodCommandTest.php deleted file mode 100644 index d5c90c1a30..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateMethodCommandTest.php +++ /dev/null @@ -1,120 +0,0 @@ -prophesize(GenerateMember::class); - $generateMethod->generateMember(Argument::type(SourceCode::class), self::EXAMPLE_OFFSET) - ->shouldBeCalled() - ->willReturn($textEdits); - - [$tester, $builder] = $this->createTester($generateMethod); - $tester->workspace()->executeCommand('generate', [self::EXAMPLE_URI, self::EXAMPLE_OFFSET]); - $builder->responseWatcher()->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - - $applyEdit = $builder->transmitter()->filterByMethod('workspace/applyEdit')->shiftRequest(); - - self::assertNotNull($applyEdit); - self::assertEquals([ - 'edit' => new WorkspaceEdit([ - $textEdits->uri()->__toString() => TextEditConverter::toLspTextEdits( - $textEdits->textEdits(), - self::EXAMPLE_SOURCE - ) - ]), - 'label' => 'Generate method' - ], $applyEdit->params); - } - - #[DataProvider('provideExceptions')] - public function testFailedCall(Exception $exception): void - { - $generateMethod = $this->prophesize(GenerateMember::class); - $generateMethod->generateMember(Argument::type(SourceCode::class), self::EXAMPLE_OFFSET) - ->shouldBeCalled() - ->willThrow($exception); - - [$tester, $builder] = $this->createTester($generateMethod); - $tester->workspace()->executeCommand('generate', [self::EXAMPLE_URI, self::EXAMPLE_OFFSET]); - $showMessage = $builder->transmitter()->filterByMethod('window/showMessage')->shiftNotification(); - - self::assertNotNull($showMessage); - self::assertEquals([ - 'type' => MessageType::WARNING, - 'message' => $exception->getMessage() - ], $showMessage->params); - } - - /** - * @return Generator> - */ - public static function provideExceptions(): Generator - { - yield TransformException::class => [ new TransformException('Error message!') ]; - yield MethodCallNotFound::class => [ new MethodCallNotFound('Error message!') ]; - yield CouldNotResolveNode::class => [ new CouldNotResolveNode('Error message!') ]; - } - - /** - * @param ObjectProphecy $generateMethod - * @return array{LanguageServerTester,LanguageServerTesterBuilder} - */ - private function createTester(ObjectProphecy $generateMethod): array - { - $builder = LanguageServerTesterBuilder::createBare() - ->enableTextDocuments() - ->enableCommands(); - $builder->addCommand('generate', new GenerateMemberCommand( - $builder->clientApi(), - $builder->workspace(), - $generateMethod->reveal(), - InMemoryDocumentLocator::fromTextDocuments([ - TextDocumentBuilder::create('foobar')->uri(self::EXAMPLE_URI)->build() - ]) - )); - - $tester = $builder->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_SOURCE); - - return [$tester, $builder]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportAllUnresolvedNamesCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportAllUnresolvedNamesCommandTest.php deleted file mode 100644 index e506aaedc9..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportAllUnresolvedNamesCommandTest.php +++ /dev/null @@ -1,165 +0,0 @@ - $candidateFinder - */ - private ObjectProphecy $candidateFinder; - - /** - * @var ObjectProphecy $importName - */ - private ObjectProphecy $importName; - - public function setUp(): void - { - $this->candidateFinder = $this->prophesize(CandidateFinder::class); - $this->importName = $this->prophesize(ImportNameCommand::class); - } - - public function testNoUnresolvedNamesDoesNothing(): void - { - $builder = $this->createBuilder(); - $server = $builder->build(); - $server->textDocument()->open(self::EXAMPLE_URI, 'foobar'); - - $this->candidateFinder->unresolved($builder->workspace()->get(self::EXAMPLE_URI))->willReturn(new Success(new NameWithByteOffsets())); - - wait($server->workspace()->executeCommand(ImportAllUnresolvedNamesCommand::NAME, [ - self::EXAMPLE_URI - ])); - $this->addToAssertionCount(1); - } - - public function testNoCandidates(): void - { - $builder = $this->createBuilder(); - $server = $builder->build(); - $server->textDocument()->open(self::EXAMPLE_URI, 'foobar'); - - $unresolvedName = $this->createUnresolvedName(); - $this->candidateFinder->unresolved($builder->workspace()->get(self::EXAMPLE_URI))->willReturn(new Success(new NameWithByteOffsets( - $unresolvedName - ))); - $this->candidateFinder->candidatesForUnresolvedName($unresolvedName)->willYield([]); - - wait($server->workspace()->executeCommand(ImportAllUnresolvedNamesCommand::NAME, [ - self::EXAMPLE_URI - ])); - - $notification = $server->transmitter()->shiftNotification(); - self::assertEquals('Class "Foobar" has no candidates', $notification->params['message']); - } - - public function testIdenticallyNamedCandidates(): void - { - $builder = $this->createBuilder(); - $server = $builder->build(); - $server->textDocument()->open(self::EXAMPLE_URI, 'foobar'); - - $unresolvedName = $this->createUnresolvedName(); - $this->candidateFinder->unresolved($builder->workspace()->get(self::EXAMPLE_URI))->willReturn(new Success(new NameWithByteOffsets( - $unresolvedName, - $this->createUnresolvedName() - ))); - $this->candidateFinder->candidatesForUnresolvedName($unresolvedName)->willYield([]); - - wait($server->workspace()->executeCommand(ImportAllUnresolvedNamesCommand::NAME, [ - self::EXAMPLE_URI - ])); - - $notification = $server->transmitter()->shiftNotification(); - $notification = $server->transmitter()->shiftNotification(); - self::assertNull($notification); - } - - public function testOneCandidate(): void - { - $builder = $this->createBuilder(); - $server = $builder->build(); - $server->textDocument()->open(self::EXAMPLE_URI, 'foobar'); - - $unresolvedName = $this->createUnresolvedName(); - $this->candidateFinder->unresolved($builder->workspace()->get(self::EXAMPLE_URI))->willReturn(new Success(new NameWithByteOffsets( - $unresolvedName - ))); - $this->candidateFinder->candidatesForUnresolvedName($unresolvedName)->willYield([ - new NameCandidate($unresolvedName, self::EXAMPLE_CANDIDATE) - ]); - $this->importName->__invoke(Argument::cetera())->willReturn(new Success(true))->shouldBeCalled(); - - wait($server->workspace()->executeCommand(ImportAllUnresolvedNamesCommand::NAME, [ - self::EXAMPLE_URI - ])); - } - - public function testAsksUserToSelectFromMultipleCandidates(): void - { - $builder = $this->createBuilder(); - $server = $builder->build(); - $server->textDocument()->open(self::EXAMPLE_URI, 'foobar'); - - $unresolvedName = $this->createUnresolvedName(); - $this->candidateFinder->unresolved($builder->workspace()->get(self::EXAMPLE_URI))->willReturn(new Success(new NameWithByteOffsets( - $unresolvedName - ))); - $this->candidateFinder->candidatesForUnresolvedName($unresolvedName)->willYield([ - new NameCandidate($unresolvedName, self::EXAMPLE_CANDIDATE), - new NameCandidate($unresolvedName, 'Barfoo') - ]); - $this->importName->__invoke(Argument::cetera())->willReturn(new Success(true))->shouldBeCalled(); - - $promise = $server->workspace()->executeCommand(ImportAllUnresolvedNamesCommand::NAME, [ - self::EXAMPLE_URI - ]); - $builder->responseWatcher()->resolveLastResponse(new MessageActionItem(self::EXAMPLE_CANDIDATE)); - wait($promise); - } - - private function createBuilder(): LanguageServerTesterBuilder - { - $builder = LanguageServerTesterBuilder::createBare() - ->enableCommands() - ->enableTextDocuments(); - - $builder->addCommand( - ImportAllUnresolvedNamesCommand::NAME, - new ImportAllUnresolvedNamesCommand( - $this->candidateFinder->reveal(), - $builder->workspace(), - $this->importName->reveal(), - $builder->clientApi() - ) - ); - return $builder; - } - - private function createUnresolvedName(): NameWithByteOffset - { - return new NameWithByteOffset(FullyQualifiedName::fromString(self::EXAMPLE_CANDIDATE), ByteOffset::fromInt(10), NameWithByteOffset::TYPE_CLASS); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportNameCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportNameCommandTest.php deleted file mode 100644 index 845315da1c..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportNameCommandTest.php +++ /dev/null @@ -1,123 +0,0 @@ - - */ - private ObjectProphecy $textEditProphecy; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $nameImporterProphecy; - - protected function setUp(): void - { - $this->textEditProphecy = $this->prophesize(TextEdit::class); - $this->nameImporterProphecy = $this->prophesize(NameImporter::class); - $this->workspace = new Workspace(); - $this->rpcClient = TestRpcClient::create(); - $this->command = new ImportNameCommand( - $this->nameImporterProphecy->reveal(), - $this->workspace, - new ClientApi($this->rpcClient) - ); - } - - public function testImportClass(): void - { - $textDoc = new TextDocumentItem(self::EXAMPLE_PATH_URI, 'php', 1, self::EXAMPLE_CONTENT); - $this->workspace->open($textDoc); - - $this->nameImporterProphecy->__invoke( - $textDoc, - self::EXAMPLE_OFFSET, - 'class', - 'Foobar', - true, - null - )->willReturn(NameImporterResult::createResult( - NameImport::forClass('Foobar'), - [$this->textEditProphecy->reveal()] - )); - - $promise = (new CommandDispatcher([ - ImportNameCommand::NAME => $this->command - ]))->dispatch(ImportNameCommand::NAME, [ - self::EXAMPLE_PATH_URI, - self::EXAMPLE_OFFSET, - 'class', - 'Foobar' - ]); - - $this->assertWorkspaceResponse($promise); - } - - public function testNotifyOnError(): void - { - $textDoc = new TextDocumentItem(self::EXAMPLE_PATH_URI, 'php', 1, self::EXAMPLE_CONTENT); - $this->workspace->open($textDoc); - - $this->nameImporterProphecy->__invoke( - $textDoc, - self::EXAMPLE_OFFSET, - 'class', - 'Foobar', - true, - null - )->willReturn(NameImporterResult::createErrorResult(new TransformException('Sorry'))); - - (new CommandDispatcher([ - ImportNameCommand::NAME => $this->command - ]))->dispatch(ImportNameCommand::NAME, [ - self::EXAMPLE_PATH_URI, - self::EXAMPLE_OFFSET, - 'class', - 'Foobar' - ]); - - self::assertNotNull($message = $this->rpcClient->transmitter()->shiftNotification()); - self::assertEquals('Sorry', $message->params['message']); - } - - private function assertWorkspaceResponse(Promise $promise): void - { - $expectedResponse = new ApplyWorkspaceEditResult(true, null); - $this->rpcClient->responseWatcher()->resolveLastResponse($expectedResponse); - $result = wait($promise); - $this->assertEquals($expectedResponse, $result); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/PropertyAccessGeneratorCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/PropertyAccessGeneratorCommandTest.php deleted file mode 100644 index 37da85e1ff..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/PropertyAccessGeneratorCommandTest.php +++ /dev/null @@ -1,87 +0,0 @@ -prophesize(PropertyAccessGenerator::class); - $generateAccessors->generate( - Argument::type(SourceCode::class), - [ - 'foo', - ], - self::EXAMPLE_OFFSET - ) - ->shouldBeCalled() - ->willReturn($textEdits); - - [$tester, $builder] = $this->createTester($generateAccessors); - $tester->workspace()->executeCommand('generate', [ - self::EXAMPLE_URI, - self::EXAMPLE_OFFSET, - [ - 'foo', - ], - ]); - $builder->responseWatcher()->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - - $applyEdit = $builder->transmitter()->filterByMethod('workspace/applyEdit')->shiftRequest(); - - self::assertNotNull($applyEdit); - self::assertEquals([ - 'edit' => new WorkspaceEdit([ - self::EXAMPLE_URI => TextEditConverter::toLspTextEdits( - $textEdits, - self::EXAMPLE_SOURCE - ) - ]), - 'label' => 'Generate accessors' - ], $applyEdit->params); - } - - /** - * @param ObjectProphecy $generateAccessors - * @return array{LanguageServerTester,LanguageServerTesterBuilder} - */ - private function createTester(ObjectProphecy $generateAccessors): array - { - $builder = LanguageServerTesterBuilder::createBare() - ->enableTextDocuments() - ->enableCommands(); - $builder->addCommand('generate', new PropertyAccessGeneratorCommand( - $builder->clientApi(), - $builder->workspace(), - $generateAccessors->reveal(), - 'Generate accessors' - )); - - $tester = $builder->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_SOURCE); - - return [$tester, $builder]; - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/TransformCommandTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/TransformCommandTest.php deleted file mode 100644 index 8bc1e30b04..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/TransformCommandTest.php +++ /dev/null @@ -1,70 +0,0 @@ - $testTransformer - ]); - $tester = LanguageServerTesterBuilder::create(); - $tester->addCommand('transform', new TransformCommand( - $tester->clientApi(), - $tester->workspace(), - $transformers - )); - $watcher = $tester->responseWatcher(); - $tester = $tester->build(); - $tester->textDocument()->open('file:///foobar', 'foobar'); - $promise = $tester->workspace()->executeCommand('transform', [ - 'file:///foobar', - self::EXAMPLE_TRANSFORM_NAME - ]); - $watcher->resolveLastResponse(new ApplyWorkspaceEditResult(true)); - $response = wait($promise); - self::assertInstanceOf(ResponseMessage::class, $response); - self::assertInstanceOf(ApplyWorkspaceEditResult::class, $response->result); - - self::assertNotNull($testTransformer->code); - self::assertEquals('/foobar', $testTransformer->code->uri()->path()); - } -} - -class TestTransformer implements Transformer -{ - public SourceCode $code; - - public function transform(SourceCode $code): Promise - { - $this->code = $code; - return new Success(TextEdits::none()); - } - - - /** - * @return Promise - */ - public function diagnostics(SourceCode $code): Promise - { - return new Success(Diagnostics::none()); - } -} diff --git a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/Model/NameImporter/NameImporterTest.php b/lib/Extension/LanguageServerCodeTransform/Tests/Unit/Model/NameImporter/NameImporterTest.php deleted file mode 100644 index 6a484ba2c0..0000000000 --- a/lib/Extension/LanguageServerCodeTransform/Tests/Unit/Model/NameImporter/NameImporterTest.php +++ /dev/null @@ -1,246 +0,0 @@ - - */ - private ObjectProphecy $importNameProphecy; - - private Workspace $workspace; - - private TextDocumentItem $document; - - /** - * @var array - */ - private array $lspTextEdits; - - private TextEdits $textEdits; - - private SourceCode $sourceCode; - - private ByteOffset $byteOffset; - - private NameImporter $subject; - - protected function setUp(): void - { - $this->document = new TextDocumentItem(self::EXAMPLE_PATH_URI, 'php', 1, self::EXAMPLE_CONTENT); - $this->workspace = new Workspace(); - $this->workspace->open($this->document); - - $this->importNameProphecy = $this->prophesize(RefactorImportName::class); - $this->byteOffset = ByteOffset::fromInt(self::EXAMPLE_OFFSET); - - $this->textEdits = TextEdits::one( - TextEdit::create(23, 6, 'huhuhu') - ); - - $this->lspTextEdits = TextEditConverter::toLspTextEdits($this->textEdits, self::EXAMPLE_CONTENT); - - $this->sourceCode = SourceCode::fromStringAndPath( - self::EXAMPLE_CONTENT, - TextDocumentUri::fromString(self::EXAMPLE_PATH_URI)->path() - ); - - $this->subject = new NameImporter($this->importNameProphecy->reveal()); - } - - public static function provideTestImportData(): Generator - { - yield 'function' => [ - '\in_array', - 'function', - ImportClassNameImport::forFunction('\in_array'), - ]; - - yield 'class' => [ - self::class, - 'class', - ImportClassNameImport::forClass(self::class), - ]; - } - - #[DataProvider('provideTestImportData')] - public function testImport( - string $fqn, - string $importType, - ImportClassNameImport $importClassNameImport - ): void { - $this->importNameProphecy->importName( - $this->sourceCode, - $this->byteOffset, - $importClassNameImport - )->willReturn($this->textEdits); - - $result = $this->subject->__invoke( - $this->document, - self::EXAMPLE_OFFSET, - $importType, - $fqn, - true - ); - - self::assertTrue($result->isSuccess()); - self::assertEquals($importClassNameImport, $result->getNameImport()); - self::assertEquals($this->lspTextEdits, $result->getTextEdits()); - self::assertNull($result->getError()); - } - - public function testImportTransformException(): void - { - $error = new TransformException('error!!'); - - $this->importNameProphecy->importName( - $this->sourceCode, - $this->byteOffset, - ImportClassNameImport::forClass(Exception::class), - )->willThrow($error); - - $result = $this->subject->__invoke( - $this->document, - self::EXAMPLE_OFFSET, - 'class', - Exception::class, - true - ); - - self::assertFalse($result->isSuccess()); - self::assertNull($result->getNameImport()); - self::assertNull($result->getTextEdits()); - self::assertSame($error, $result->getError()); - } - - public function testImportAliasAlreadyUsedException(): void - { - $import = ImportClassNameImport::forClass(Exception::class); - $aliasAlreadyUsedException = new AliasAlreadyUsedException($import); - - $this->importNameProphecy->importName( - $this->sourceCode, - $this->byteOffset, - $import, - )->willThrow($aliasAlreadyUsedException); - - $aliasedNameImport = ImportClassNameImport::forClass(Exception::class, 'AliasedException'); - - $this->importNameProphecy->importName( - $this->sourceCode, - $this->byteOffset, - $aliasedNameImport, - )->willReturn($this->textEdits); - - $result = $this->subject->__invoke( - $this->document, - self::EXAMPLE_OFFSET, - 'class', - Exception::class, - true - ); - - self::assertTrue($result->isSuccess()); - self::assertEquals($aliasedNameImport, $result->getNameImport()); - self::assertEquals($this->lspTextEdits, $result->getTextEdits()); - self::assertNull($result->getError()); - } - - public function testImportNameAlreadyImportedExceptionExisting(): void - { - $import = ImportClassNameImport::forClass(Exception::class); - $nameAlreadyImportedException = new NameAlreadyImportedException( - $import, - 'Exception', - Exception::class - ); - - $this->importNameProphecy->importName( - $this->sourceCode, - $this->byteOffset, - ImportClassNameImport::forClass(Exception::class), - )->willThrow($nameAlreadyImportedException); - - $result = $this->subject->__invoke( - $this->document, - self::EXAMPLE_OFFSET, - 'class', - Exception::class, - true - ); - - self::assertTrue($result->isSuccess()); - self::assertEquals($import, $result->getNameImport()); - self::assertNull($result->getTextEdits()); - self::assertNull($result->getError()); - } - - public function testImportNameAlreadyImportedExceptionNotExisting(): void - { - $import = ImportClassNameImport::forClass(Exception::class); - $nameAlreadyImportedException = new NameAlreadyImportedException( - $import, - 'RuntimeException', - RuntimeException::class - ); - - $this->importNameProphecy->importName( - $this->sourceCode, - $this->byteOffset, - $import, - )->willThrow($nameAlreadyImportedException); - - $aliasedNameImport = ImportClassNameImport::forClass(Exception::class, 'ExceptionException'); - - $this->importNameProphecy->importName( - $this->sourceCode, - $this->byteOffset, - $aliasedNameImport, - )->willReturn($this->textEdits); - - $result = $this->subject->__invoke( - $this->document, - self::EXAMPLE_OFFSET, - 'class', - Exception::class, - true - ); - - self::assertTrue($result->isSuccess()); - self::assertEquals($aliasedNameImport, $result->getNameImport()); - self::assertEquals($this->lspTextEdits, $result->getTextEdits()); - self::assertNull($result->getError()); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php b/lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php deleted file mode 100644 index 86363696c4..0000000000 --- a/lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php +++ /dev/null @@ -1,266 +0,0 @@ - - */ - private array $resolve = []; - - public function __construct( - private Workspace $workspace, - private TypedCompletorRegistry $registry, - private SuggestionNameFormatter $suggestionNameFormatter, - private NameImporter $nameImporter, - private bool $supportSnippets, - private bool $provideTextEdit = false - ) { - } - - public function methods(): array - { - return [ - 'textDocument/completion' => 'completion', - 'completionItem/resolve' => 'resolveItem', - ]; - } - - /** - * @return Promise> - */ - public function completion(CompletionParams $params, CancellationToken $token): Promise - { - return call(function () use ($params, $token) { - $this->resolve = []; - $textDocument = $this->workspace->get($params->textDocument->uri); - - $languageId = $textDocument->languageId ?: 'php'; - $byteOffset = PositionConverter::positionToByteOffset($params->position, $textDocument->text); - $suggestions = $this->registry->completorForType( - $languageId - )->complete( - TextDocumentBuilder::create($textDocument->text)->language($languageId)->uri($textDocument->uri)->build(), - $byteOffset - ); - - $items = []; - $isIncomplete = false; - foreach ($suggestions as $index => $suggestion) { - assert($suggestion instanceof Suggestion); - - $name = $this->suggestionNameFormatter->format($suggestion); - $nameImporterResult = $this->importClassOrFunctionName($suggestion, $params); - - [$insertText, $insertTextFormat] = $this->determineInsertTextAndFormat( - $name, - $suggestion, - $nameImporterResult - ); - - $textEdits = $nameImporterResult->getTextEdits(); - - $item = CompletionItem::fromArray([ - 'label' => $suggestion->label(), - 'kind' => PhpactorToLspCompletionType::fromPhpactorType($suggestion->type()), - 'insertText' => $insertText, - 'sortText' => $this->sortText($suggestion), - 'textEdit' => $this->textEdit($suggestion, $insertText, $textDocument), - 'additionalTextEdits' => $textEdits, - 'insertTextFormat' => $insertTextFormat, - 'data' => $index, - ]); - - $this->resolve[$index] = function (CompletionItem $item) use ($suggestion): CompletionItem { - $documentation = $suggestion->documentation(); - $item->documentation = $documentation ? new MarkupContent(MarkupKind::MARKDOWN, $documentation) : null; - $item->detail = $this->formatShortDescription($suggestion); - return $item; - }; - - $items[] = $item; - - try { - $token->throwIfRequested(); - } catch (CancelledException) { - $this->resolve = []; - $isIncomplete = true; - break; - } - yield new Delayed(0); - } - - - $isIncomplete = $isIncomplete || !$suggestions->getReturn(); - - return new CompletionList($isIncomplete, $items); - }); - } - - /** - * @return Promise - */ - public function resolveItem(RequestMessage $request): Promise - { - /** @phpstan-ignore-next-line */ - return call(function () use ($request) { - /** @phpstan-ignore-next-line */ - $item = CompletionItem::fromArray($request->params); - - if (!(is_string($item->data) || is_int($item->data)) || !array_key_exists($item->data, $this->resolve)) { - return $item; - } - /** @phpstan-ignore-next-line - shouldn't happen but playing safe */ - if (null === $this->resolve[$item->data]) { - return $item; - } - return $this->resolve[$item->data]($item); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->completionProvider = new CompletionOptions([ - ':', - '>', - '$', - '[', - '@', - '(', - '\'', - '"', - '\\' - ]); - $capabilities->signatureHelpProvider = new SignatureHelpOptions(['(', ',']); - $capabilities->completionProvider->resolveProvider = true; - } - - /** - * @return array{string,InsertTextFormat::*} - */ - private function determineInsertTextAndFormat( - string $name, - Suggestion $suggestion, - NameImporterResult $nameImporterResult - ): array { - $insertText = $name; - $insertTextFormat = InsertTextFormat::PLAIN_TEXT; - - if ($this->supportSnippets) { - $insertText = $suggestion->snippet() ?: $name; - $insertTextFormat = $suggestion->snippet() - ? InsertTextFormat::SNIPPET - : InsertTextFormat::PLAIN_TEXT - ; - } - - if ($nameImporterResult->isSuccessAndHasAliasedNameImport()) { - $alias = $nameImporterResult->getNameImport()->alias(); - $insertText = str_replace($name, $alias, $insertText); - } - - return [$insertText, $insertTextFormat]; - } - - private function importClassOrFunctionName( - Suggestion $suggestion, - CompletionParams $params - ): NameImporterResult { - $suggestionNameImport = $suggestion->nameImport(); - - if (!$suggestionNameImport) { - return NameImporterResult::createEmptyResult(); - } - - $suggestionType = $suggestion->type(); - - if (!in_array($suggestionType, [ 'class', 'function'])) { - return NameImporterResult::createEmptyResult(); - } - - $textDocument = $this->workspace->get($params->textDocument->uri); - $offset = PositionConverter::positionToByteOffset($params->position, $textDocument->text); - - return ($this->nameImporter)( - $textDocument, - $offset->toInt(), - $suggestionType, - $suggestionNameImport, - false - ); - } - - private function textEdit( - Suggestion $suggestion, - string $insertText, - TextDocumentItem $textDocument - ): ?TextEdit { - if (false === $this->provideTextEdit) { - return null; - } - - $range = $suggestion->range(); - - if (!$range) { - return null; - } - return new TextEdit( - new Range( - PositionConverter::byteOffsetToPosition($range->start(), $textDocument->text), - PositionConverter::byteOffsetToPosition($range->end(), $textDocument->text), - ), - $insertText - ); - } - - private function formatShortDescription(Suggestion $suggestion): string - { - $prefix = ''; - if ($suggestion->nameImport()) { - $prefix = '↓ '; - } - - return $prefix . $suggestion->shortDescription(); - } - - private function sortText(Suggestion $suggestion): ?string - { - if (null === $suggestion->priority()) { - return null; - } - - return sprintf('%04s-%s', $suggestion->priority(), $suggestion->name()); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Handler/SignatureHelpHandler.php b/lib/Extension/LanguageServerCompletion/Handler/SignatureHelpHandler.php deleted file mode 100644 index 95200063d1..0000000000 --- a/lib/Extension/LanguageServerCompletion/Handler/SignatureHelpHandler.php +++ /dev/null @@ -1,62 +0,0 @@ - 'signatureHelp' - ]; - } - - public function signatureHelp( - TextDocumentIdentifier $textDocument, - Position $position - ): Promise { - return call(function () use ($textDocument, $position) { - $textDocument = $this->workspace->get($textDocument->uri); - - $languageId = $textDocument->languageId ?: 'php'; - - try { - return PhpactorToLspSignature::toLspSignatureHelp($this->helper->signatureHelp( - TextDocumentBuilder::create($textDocument->text)->language($languageId)->uri($textDocument->uri)->build(), - PositionConverter::positionToByteOffset($position, $textDocument->text) - )); - } catch (CouldNotHelpWithSignature) { - return null; - } - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $options = new SignatureHelpOptions(); - $options->triggerCharacters = [ '(', ',', '@' ]; - $capabilities->signatureHelpProvider = $options; - } -} diff --git a/lib/Extension/LanguageServerCompletion/LanguageServerCompletionExtension.php b/lib/Extension/LanguageServerCompletion/LanguageServerCompletionExtension.php deleted file mode 100644 index c766ce007a..0000000000 --- a/lib/Extension/LanguageServerCompletion/LanguageServerCompletionExtension.php +++ /dev/null @@ -1,73 +0,0 @@ -setDefaults([ - self::PARAM_TRIM_LEADING_DOLLAR => false, - ]); - $schema->setDescriptions([ - self::PARAM_TRIM_LEADING_DOLLAR => 'If the leading dollar should be trimmed for variable completion suggestions', - ]); - } - - - public function load(ContainerBuilder $container): void - { - $this->registerHandlers($container); - } - - private function registerHandlers(ContainerBuilder $container): void - { - $container->register('language_server_completion.handler.completion', function (Container $container) { - return new CompletionHandler( - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->expect(CompletionExtension::SERVICE_REGISTRY, TypedCompletorRegistry::class), - $container->get(SuggestionNameFormatter::class), - $container->get(NameImporter::class), - $this->clientCapabilities($container)->textDocument->completion->completionItem['snippetSupport'] ?? false - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => [ - 'methods' => [ - 'textDocument/completion' - ] - ]]); - - $container->register(SuggestionNameFormatter::class, function (Container $container) { - return new SuggestionNameFormatter($container->parameter(self::PARAM_TRIM_LEADING_DOLLAR)->bool()); - }); - - $container->register('language_server_completion.handler.signature_help', function (Container $container) { - return new SignatureHelpHandler( - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->expect(CompletionExtension::SERVICE_SIGNATURE_HELPER, SignatureHelper::class) - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => [] ]); - } - - private function clientCapabilities(Container $container): ClientCapabilities - { - return $container->get(ClientCapabilities::class); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Extension/TestExtension.php b/lib/Extension/LanguageServerCompletion/Tests/Extension/TestExtension.php deleted file mode 100644 index 1375e3ff2d..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Extension/TestExtension.php +++ /dev/null @@ -1,31 +0,0 @@ -registerFilePathExpanders($container); - } - - public function configure(Resolver $schema): void - { - } - - private function registerFilePathExpanders(ContainerBuilder $container): void - { - $container->register('core.file_path_resolver.project_config_expander', function (Container $container) { - $path = $container->getParameter(FilePathResolverExtension::PARAM_PROJECT_ROOT) . '/.phpactor'; - return new ValueExpander('project_config', $path); - }, [ FilePathResolverExtension::TAG_EXPANDER => [] ]); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/MarkdownObjectRendererTest.php b/lib/Extension/LanguageServerCompletion/Tests/Integration/MarkdownObjectRendererTest.php deleted file mode 100644 index ee11acfbee..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/MarkdownObjectRendererTest.php +++ /dev/null @@ -1,1196 +0,0 @@ -workspace()->reset(); - $this->workspace()->mkdir('project'); - $this->locator = new StubSourceLocator(ReflectorBuilder::create()->build(), $this->workspace()->path('project'), $this->workspace()->path('cache')); - $this->reflector = ReflectorBuilder::create() - ->addLocator($this->locator) - ->addMemberProvider(new DocblockMemberProvider()) - ->enableContextualSourceLocation() - ->build(); - $this->renderer = ObjectRendererBuilder::create() - ->addTemplatePath(__DIR__ .'/../../../../../templates/help/markdown') - ->enableInterfaceCandidates() - ->enableAncestoralCandidates() - ->configureTwig(function (Environment $env) { - (new TwigFunctions())->configure($env); - return $env; - }) - ->build(); - } - - /** - * @param Closure(Reflector): HoverInformation $objectFactory - */ - #[DataProvider('provideHoverInformation')] - #[DataProvider('provideClass')] - #[DataProvider('provideInterface')] - #[DataProvider('provideMethod')] - #[DataProvider('provideVariable')] - #[DataProvider('provideProperty')] - #[DataProvider('provideConstant')] - #[DataProvider('provideEnum')] - #[DataProvider('provideEnumCase')] - #[DataProvider('provideTrait')] - #[DataProvider('provideFunction')] - #[DataProvider('provideSymbolOffset')] - #[DataProvider('provideDeclaredConstant')] - #[DataProvider('provideType')] - #[DataProvider('provideMemberDocblock')] - public function testRender(string $manifest, Closure $objectFactory, string $expected, bool $capture = false): void - { - $this->workspace()->loadManifest($manifest); - - $object = $objectFactory->bindTo($this)->__invoke($this->reflector); - $path = __DIR__ . '/expected/'. $expected; - - if (!file_exists($path)) { - file_put_contents($path, ''); - } - - $actual = $this->renderer->render($object); - - if ($capture) { - fwrite(STDOUT, sprintf("\nCaptured %s\n\n>>> START\n%s\n<<< END", $path, $actual)); - file_put_contents($path, $actual); - } - - self::assertEquals(trim(file_get_contents($path)), trim($actual)); - } - - /** - * @return Generator - */ - public function provideHoverInformation(): Generator - { - yield 'empty' => [ - '', - function (Reflector $reflector) { - return new HoverInformation('', '', $this->reflectClassesIn($reflector, 'first()); - }, - 'hover_information1.md', - ]; - - yield 'title no docs' => [ - '', - function (Reflector $reflector) { - return new HoverInformation('This is my title', '', $this->reflectClassesIn($reflector, 'first()); - }, - 'hover_information2.md', - ]; - - yield 'title with docs' => [ - '', - function (Reflector $reflector) { - return new HoverInformation('This is my title', 'There are my docs', $this->reflectClassesIn($reflector, 'first()); - }, - 'hover_information3.md', - ]; - - yield 'docs with HTML tags' => [ - '', - function (Reflector $reflector) { - return new HoverInformation('This is my title', '

There are my docs

', $this->reflectClassesIn($reflector, 'first()); - }, - 'hover_information3.md', - ]; - } - - /** - * @return Generator - */ - public function provideClass(): Generator - { - yield 'simple class' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn($reflector, 'first(); - }, - 'class_reflection1.md' - ]; - - yield 'complex class' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('Concrete'); - }, - 'class_reflection2.md', - ]; - - yield 'class with constants and properties' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('SomeClass'); - }, - 'class_reflection3.md', - ]; - - yield 'too many members' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - sprintf( - ' sprintf('public function fun%s():void{}', $number), - range(1, 53), - )) - ), - )->get('SomeClass'); - }, - 'class_reflection_too_many_members.md', - false, - ]; - - yield 'final class' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn($reflector, 'first(); - }, - 'class_reflection4.md', - ]; - - yield 'class that extends itself' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn($reflector, 'first(); - }, - 'class_reflection5.md', - ]; - - yield 'deprecated class' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn($reflector, 'first(); - }, - 'class_reflection6.md', - ]; - } - - /** - * @return Generator - */ - public function provideInterface(): Generator - { - yield 'complex interface' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('AwesomeInterface'); - }, - 'interface_reflection1.md', - ]; - } - - /** - * @return Generator - */ - public function provideTrait(): Generator - { - yield 'simple trait' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('Blah'); - }, - 'trait1.md', - ]; - } - - /** - * @return Generator - */ - public function provideMethod(): Generator - { - yield 'simple' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->methods()->get('foo'); - }, - 'method1.md', - ]; - - yield 'complex method' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->methods()->get('foo'); - }, - 'method2.md', - ]; - - yield 'private method' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->methods()->get('foo'); - }, - 'method3.md', - ]; - - yield 'static and abstract method' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->methods()->get('foo'); - }, - 'method4.md', - ]; - - yield 'virtual method' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->methods()->get('foobar'); - }, - 'method5.md', - ]; - - yield 'overridden method' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foobar'); - }, - 'method6.md', - ]; - - yield 'overridden method from interface' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foobar'); - }, - 'method7.md', - ]; - - yield 'deprecated method' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foobar'); - }, - 'method8.md' - ]; - - yield 'method variadic ' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foo'); - }, - 'method_variadic.md', - ]; - yield 'method variadic no type' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foo'); - }, - 'method_variadic_no_type.md', - ]; - } - - /** - * @return Generator - */ - public function provideProperty(): Generator - { - yield 'simple property' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->properties()->get('foobar'); - }, - 'property1.md', - ]; - - yield 'complex property' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->properties()->get('foobar'); - }, - 'property2.md', - ]; - - yield 'typed property' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->properties()->get('foobar'); - }, - 'property3.md', - ]; - - yield 'virtual property' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->properties()->get('foobar'); - }, - 'property4.md', - ]; - - yield 'mixed property' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->properties()->get('foobar'); - }, - 'property5.md', - ]; - } - - /** - * @return Generator - */ - public function provideEnum(): Generator - { - yield 'enum' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first(); - }, - 'enum.md', - ]; - - yield 'backed enum' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first(); - }, - 'backed_enum.md', - ]; - } - - /** - * @return Generator - */ - public function provideEnumCase(): Generator - { - yield 'enum case' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->cases()->get('FOOBAR'); - }, - 'enum_case1.md', - ]; - - yield 'backed enum case' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->cases()->get('FOOBAR'); - }, - 'enum_backed_case1.md', - ]; - } - - /** - * @return Generator - */ - public function provideConstant(): Generator - { - yield 'simple constant' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->constants()->get('FOOBAR'); - }, - 'constant1.md', - ]; - - yield 'complex constant' => [ - '', - function (Reflector $reflector) { - return $this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->constants()->get('FOOBAR'); - }, - 'constant2.md', - ]; - } - - /** - * @return Generator - */ - public static function provideFunction(): Generator - { - yield 'simple function' => [ - '', - function (Reflector $reflector) { - return $reflector->reflectFunctionsIn( - TextDocumentBuilder::fromUnknown( - <<<'EOT' - first(); - }, - 'function1.md', - ]; - - yield 'complex function' => [ - '', - function (Reflector $reflector) { - return $reflector->reflectFunctionsIn( - TextDocumentBuilder::fromUnknown( - <<<'EOT' - first(); - }, - 'function2.md', - ]; - } - - /** - * @return Generator - */ - public static function provideDeclaredConstant(): Generator - { - yield 'define constant' => [ - '', - function (Reflector $reflector) { - return $reflector->reflectConstantsIn( - TextDocumentBuilder::fromUnknown( - <<<'EOT' - first(); - }, - 'declared_constant1.md', - ]; - } - - /** - * @return Generator - */ - public static function provideSymbolOffset(): Generator - { - yield 'whitespace' => [ - '', - function (Reflector $reflector) { - return $reflector->reflectOffset( - TextDocumentBuilder::fromUnknown( - <<<'EOT' - [ - '', - function (Reflector $reflector) { - $source = <<<'EOT' - zed; - - EOT - ; - [$source, $offset] = ExtractOffset::fromSource($source); - $source = - TextDocumentBuilder::fromUnknown($source); - return $reflector->reflectOffset($source, $offset); - }, - 'offset2.md', - ]; - } - - /** - * @return Generator - */ - public static function provideType(): Generator - { - yield 'mixed' => [ - '', - function (Reflector $reflector) { - return TypeFactory::mixed(); - }, - 'type1.md', - ]; - yield 'union' => [ - << [ - '', - function (Reflector $reflector) { - return TypeFactory::intersection( - TypeFactory::class('Foobar'), - TypeFactory::class('Barfoo'), - ); - }, - 'type3.md', - ]; - } - - /** - * @return Generator - */ - public function provideMemberDocblock(): Generator - { - yield 'single member with no doc' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->methods()->get('foo')); - }, - 'member_docblock1.md', - ]; - - yield 'single member with doc' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - first()->methods()->get('foo')); - }, - 'member_docblock2.md', - ]; - yield 'member with concrete parent doc' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foo')); - }, - 'member_docblock3.md', - ]; - - yield 'member with multiple concrete parent doc' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foo')); - }, - 'member_docblock4.md', - ]; - - yield 'member with interface parent' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foo')); - }, - 'member_docblock5.md', - ]; - - yield 'member with multiple interface parent' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foo')); - }, - 'member_docblock6.md', - ]; - - yield 'do not repeat interfaces' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - get('OneClass')->methods()->get('foo')); - }, - 'member_docblock7.md', - ]; - - yield 'formatted member docblock' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - $foo - * @throws Foobar - * @unownTag bar - */ - public function foo(Foobar $foo) {} - } - EOT - )->get('OneClass')->methods()->get('foo')); - }, - 'member_docblock8.md', - ]; - - yield 'formatted member docblock bare tag' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - $foo - */ - public function foo(Foobar $foo) {} - } - EOT - )->get('OneClass')->methods()->get('foo')); - }, - 'member_docblock9.md', - ]; - - yield 'formatted member docblock 2' => [ - '', - function (Reflector $reflector) { - return new MemberDocblock($this->reflectClassesIn( - $reflector, - <<<'EOT' - - * Initial array for comparison of the arrays. - *

- * @param array $array2

- * First array to compare keys against. - *

- * @param callable $key_compare_func

- * User supplied callback function to do the comparison. - *

- * @param ...$rest [optional] - * @return array the values of array1 whose keys exist - * in all the arguments. - * @meta - */ - public function foo(Foobar $foo) {} - } - EOT - )->get('OneClass')->methods()->get('foo')); - }, - 'member_docblock10.md', - ]; - } - - /** - * @return Generator - */ - public static function provideVariable(): Generator - { - yield 'variable:' => [ - '', - function (Reflector $reflector) { - $offset = $reflector->reflectOffset(TextDocumentBuilder::fromUnknown('frame()->locals()->byName('foo')->first(); - return $variable; - }, - 'variable1.md', - ]; - } - - private function reflectClassesIn(Reflector $reflector, string $textDocument): ReflectionClassLikeCollection - { - return $reflector->reflectClassesIn(TextDocumentBuilder::fromUnknown($textDocument)); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/backed_enum.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/backed_enum.md deleted file mode 100644 index 20b23a016f..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/backed_enum.md +++ /dev/null @@ -1,6 +0,0 @@ -enum Foobar: string { - public static function cases(): BackedEnumCase[] - public static function from(int|string $value): static(Foobar) - public static function tryFrom(int|string $value): static(Foobar)|null - case FOOBAR = "bar"; -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection1.md deleted file mode 100644 index c48658e5e9..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection1.md +++ /dev/null @@ -1,2 +0,0 @@ -class Foobar { -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection2.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection2.md deleted file mode 100644 index 75bbff2e42..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection2.md +++ /dev/null @@ -1,4 +0,0 @@ -class Concrete extends SomeAbstract implements DoesThis, DoesThat { - public function __construct(string $foo) - public function foobar(string $foo, string|bool|null $bar): Ⓒ SomeAbstract -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection3.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection3.md deleted file mode 100644 index 657eee216a..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection3.md +++ /dev/null @@ -1,5 +0,0 @@ -class SomeClass { - public const FOOBAR = "bar"; - public $foo; - public function foobar(): void -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection4.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection4.md deleted file mode 100644 index 6da5a3a80e..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection4.md +++ /dev/null @@ -1,2 +0,0 @@ -final class Foobar { -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection5.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection5.md deleted file mode 100644 index c48658e5e9..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection5.md +++ /dev/null @@ -1,2 +0,0 @@ -class Foobar { -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection6.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection6.md deleted file mode 100644 index be4d1444d5..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection6.md +++ /dev/null @@ -1,3 +0,0 @@ -// @deprecated This is deprecated -class Foobar { -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection_too_many_members.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection_too_many_members.md deleted file mode 100644 index 87cf6de36b..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/class_reflection_too_many_members.md +++ /dev/null @@ -1,53 +0,0 @@ -class SomeClass { - public function fun1(): void - public function fun2(): void - public function fun3(): void - public function fun4(): void - public function fun5(): void - public function fun6(): void - public function fun7(): void - public function fun8(): void - public function fun9(): void - public function fun10(): void - public function fun11(): void - public function fun12(): void - public function fun13(): void - public function fun14(): void - public function fun15(): void - public function fun16(): void - public function fun17(): void - public function fun18(): void - public function fun19(): void - public function fun20(): void - public function fun21(): void - public function fun22(): void - public function fun23(): void - public function fun24(): void - public function fun25(): void - public function fun26(): void - public function fun27(): void - public function fun28(): void - public function fun29(): void - public function fun30(): void - public function fun31(): void - public function fun32(): void - public function fun33(): void - public function fun34(): void - public function fun35(): void - public function fun36(): void - public function fun37(): void - public function fun38(): void - public function fun39(): void - public function fun40(): void - public function fun41(): void - public function fun42(): void - public function fun43(): void - public function fun44(): void - public function fun45(): void - public function fun46(): void - public function fun47(): void - public function fun48(): void - public function fun49(): void - public function fun50(): void - // and 3 more... -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/constant1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/constant1.md deleted file mode 100644 index 0b0ef91fcf..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/constant1.md +++ /dev/null @@ -1 +0,0 @@ -public const FOOBAR = "barfoo"; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/constant2.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/constant2.md deleted file mode 100644 index 3d062f1e49..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/constant2.md +++ /dev/null @@ -1 +0,0 @@ -private const FOOBAR = ["one",2]; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/declared_constant1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/declared_constant1.md deleted file mode 100644 index 691991ff91..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/declared_constant1.md +++ /dev/null @@ -1 +0,0 @@ -define FOO = "bar" diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum.md deleted file mode 100644 index df51eb1544..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum.md +++ /dev/null @@ -1,4 +0,0 @@ -enum Foobar { - public static function cases(): UnitEnumCase[] - case FOOBAR; -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum_backed_case1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum_backed_case1.md deleted file mode 100644 index ef0bbfd389..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum_backed_case1.md +++ /dev/null @@ -1 +0,0 @@ -case FOOBAR = "foo"; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum_case1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum_case1.md deleted file mode 100644 index f2ccd3ada1..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/enum_case1.md +++ /dev/null @@ -1 +0,0 @@ -case FOOBAR; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/function1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/function1.md deleted file mode 100644 index dd13757921..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/function1.md +++ /dev/null @@ -1 +0,0 @@ -function one() \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/function2.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/function2.md deleted file mode 100644 index 27422de78a..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/function2.md +++ /dev/null @@ -1 +0,0 @@ -function one(string $bar, bool $baz): stdClass \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/hover_information1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/hover_information1.md deleted file mode 100644 index 3aae7d0106..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/hover_information1.md +++ /dev/null @@ -1,7 +0,0 @@ -### - -```php - Initial array for comparison of the arrays.

-- **$array2**: *array*

First array to compare keys against.

-- **$key_compare_func**: *callable*

User supplied callback function to do the comparison.

-- ...$rest [optional] - -**Return** *array*: the values of array1 whose keys exist in all the arguments. - diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock2.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock2.md deleted file mode 100644 index 5eced95754..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock2.md +++ /dev/null @@ -1 +0,0 @@ -Foobar \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock3.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock3.md deleted file mode 100644 index 8b8ecb270b..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock3.md +++ /dev/null @@ -1,5 +0,0 @@ -Barfoo - ---- - -Foobar \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock4.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock4.md deleted file mode 100644 index cd27b0acc7..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock4.md +++ /dev/null @@ -1,9 +0,0 @@ -Doobar - ---- - -Barfoo - ---- - -Foobar \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock5.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock5.md deleted file mode 100644 index 5eced95754..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock5.md +++ /dev/null @@ -1 +0,0 @@ -Foobar \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock6.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock6.md deleted file mode 100644 index 15cddddf08..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock6.md +++ /dev/null @@ -1,5 +0,0 @@ -Bong - ---- - -Foobar \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock7.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock7.md deleted file mode 100644 index 15cddddf08..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock7.md +++ /dev/null @@ -1,5 +0,0 @@ -Bong - ---- - -Foobar \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock8.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock8.md deleted file mode 100644 index 3b781de902..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock8.md +++ /dev/null @@ -1,6 +0,0 @@ -This is my docblock - -- **$foo**: *Foobar* - -**Throws** `Foobar` - diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock9.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock9.md deleted file mode 100644 index 84d77dc4b3..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/member_docblock9.md +++ /dev/null @@ -1,4 +0,0 @@ - - -- **$foo**: *Foobar* - diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method1.md deleted file mode 100644 index a01eee3f23..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method1.md +++ /dev/null @@ -1 +0,0 @@ -public function foo() \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method2.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method2.md deleted file mode 100644 index 8e5f92b78c..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method2.md +++ /dev/null @@ -1 +0,0 @@ -public function foo(string $bar, bool|string $foo, Foobar[] $zed): void \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method3.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method3.md deleted file mode 100644 index bde34296ab..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method3.md +++ /dev/null @@ -1 +0,0 @@ -private function foo(): void \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method4.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method4.md deleted file mode 100644 index 41d1ee7c4b..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method4.md +++ /dev/null @@ -1 +0,0 @@ -abstract public static function foo() \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method5.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method5.md deleted file mode 100644 index 926f9cf0e4..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method5.md +++ /dev/null @@ -1 +0,0 @@ -[virtual] public function foobar(): string \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method6.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method6.md deleted file mode 100644 index 2a77c3cf7f..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method6.md +++ /dev/null @@ -1 +0,0 @@ -Ⓒ public function foobar() \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method7.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method7.md deleted file mode 100644 index 99643bccd6..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method7.md +++ /dev/null @@ -1 +0,0 @@ -ⓘ public function foobar() \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method8.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method8.md deleted file mode 100644 index b3edb949a0..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method8.md +++ /dev/null @@ -1,2 +0,0 @@ -// @deprecated Do not use me - ⚠ public function foobar() \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method_variadic.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method_variadic.md deleted file mode 100644 index 74e5b3fea0..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method_variadic.md +++ /dev/null @@ -1 +0,0 @@ -public function foo(Foobar ...$foo) \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method_variadic_no_type.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method_variadic_no_type.md deleted file mode 100644 index 0c879d062d..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/method_variadic_no_type.md +++ /dev/null @@ -1 +0,0 @@ -public function foo( ...$foo) \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset1.md deleted file mode 100644 index 1d7bb8a5bf..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset1.md +++ /dev/null @@ -1 +0,0 @@ -InlineHtml diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset2.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset2.md deleted file mode 100644 index 82287d0623..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset2.md +++ /dev/null @@ -1 +0,0 @@ -variable zed diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset3.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset3.md deleted file mode 100644 index 1dc1e0ccad..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/offset3.md +++ /dev/null @@ -1,5 +0,0 @@ -`56:108 variable Foo this` - -Frame: - - `$this` __Foo__ _[56:108]_ diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/parsed_docblock1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/parsed_docblock1.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property1.md deleted file mode 100644 index 5f093b2b6f..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property1.md +++ /dev/null @@ -1 +0,0 @@ -public $foobar; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property2.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property2.md deleted file mode 100644 index 40f9222d79..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property2.md +++ /dev/null @@ -1 +0,0 @@ -public Foobar|string $foobar; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property3.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property3.md deleted file mode 100644 index d1f14dce60..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property3.md +++ /dev/null @@ -1 +0,0 @@ -public string $foobar; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property4.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property4.md deleted file mode 100644 index 6ffd6e8378..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property4.md +++ /dev/null @@ -1 +0,0 @@ -[virtual] public string $foobar; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property5.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property5.md deleted file mode 100644 index 76cc28f81c..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/property5.md +++ /dev/null @@ -1 +0,0 @@ -public mixed|foo $foobar; \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/trait1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/trait1.md deleted file mode 100644 index 9682574e47..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/trait1.md +++ /dev/null @@ -1,4 +0,0 @@ -trait Blah { - public function foo() -} - diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type1.md deleted file mode 100644 index 527e950d5f..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type1.md +++ /dev/null @@ -1 +0,0 @@ -mixed \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type2.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type2.md deleted file mode 100644 index a6e49d199a..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type2.md +++ /dev/null @@ -1 +0,0 @@ -Ⓒ Foo|Ⓘ Baz|Ⓣ Trag \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type3.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type3.md deleted file mode 100644 index b509b5da66..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/type3.md +++ /dev/null @@ -1 +0,0 @@ -Foobar&Barfoo \ No newline at end of file diff --git a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/variable1.md b/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/variable1.md deleted file mode 100644 index 196e58797c..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Integration/expected/variable1.md +++ /dev/null @@ -1 +0,0 @@ -"bar" diff --git a/lib/Extension/LanguageServerCompletion/Tests/IntegrationTestCase.php b/lib/Extension/LanguageServerCompletion/Tests/IntegrationTestCase.php deleted file mode 100644 index c04f1ecb9e..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,78 +0,0 @@ -workspace()->reset(); - $container = PhpactorContainer::fromExtensions([ - LoggingExtension::class, - CompletionExtension::class, - LanguageServerExtension::class, - LanguageServerCodeTransformExtension::class, - LanguageServerCompletionExtension::class, - FilePathResolverExtension::class, - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - - CodeTransformExtension::class, - WorseReflectionExtension::class, - CompletionWorseExtension::class, - SourceCodeFilesystemExtension::class, - LanguageServerWorseReflectionExtension::class, - LanguageServerHoverExtension::class, - PhpExtension::class, - TestExtension::class, - IndexerExtension::class, - ReferenceFinderExtension::class, - - LanguageServerBridgeExtension::class, - ObjectRendererExtension::class, - ], [ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ .'/../../../../', - ObjectRendererExtension::PARAM_TEMPLATE_PATHS => [], - IndexerExtension::PARAM_ENABLED_WATCHERS => [], - LanguageServerExtension::PARAM_DIAGNOSTIC_OUTSOURCE => false, - LanguageServerExtension::PARAM_ENABLE_TRUST_CHECK => false, - ]); - - $builder = $container->get(LanguageServerBuilder::class); - $this->assertInstanceOf(LanguageServerBuilder::class, $builder); - - return $builder->tester(ProtocolFactory::initializeParams($this->workspace()->path('/'))); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php b/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php deleted file mode 100644 index c61a0ab6e6..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php +++ /dev/null @@ -1,598 +0,0 @@ -create([]); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertInstanceOf(CompletionList::class, $response->result); - $this->assertEquals([], $response->result->items); - $this->assertFalse($response->result->isIncomplete); - } - - public function testHandleACompleteListOfSuggestions(): void - { - $tester = $this->create([ - Suggestion::create('hello'), - Suggestion::create('goodbye'), - ]); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertInstanceOf(CompletionList::class, $response->result); - $this->assertCompletion([ - self::completionItem('hello', null), - self::completionItem('goodbye', null), - ], $response->result->items); - $this->assertFalse($response->result->isIncomplete); - } - - public function testResolveCompletionItem(): void - { - $tester = $this->create([ - Suggestion::create('hello')->withShortDescription(fn () => 'this is a short description')->withDocumentation(fn () => 'documentation now'), - Suggestion::createWithOptions('hello', [ - 'type' => Suggestion::TYPE_CLASS, - 'name_import' => 'Foobar', - ])->withShortDescription(fn () => 'import class')->withDocumentation(fn () => 'documentation now'), - ]); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertInstanceOf(CompletionList::class, $response->result); - $completionList = $response->result; - assert($completionList instanceof CompletionList); - - // resolve item 0 - $response = $tester->requestAndWait( - 'completionItem/resolve', - $completionList->items[0] - ); - self::assertEquals('this is a short description', $response->result->detail); - self::assertEquals('documentation now', $response->result->documentation->value); - - // resolve item 1 - $response = $tester->requestAndWait( - 'completionItem/resolve', - $completionList->items[1] - ); - self::assertEquals('↓ import class', $response->result->detail); - self::assertEquals('documentation now', $response->result->documentation->value); - } - - public function testResolveCompletionItemWithNoPreviousCompletion(): void - { - $tester = $this->create([]); - $response = $tester->requestAndWait( - 'completionItem/resolve', - new CompletionItem('hello'), - ); - self::assertNotNull($response); - self::assertInstanceOf(CompletionItem::class, $response->result); - } - - public function testHandleAnIncompleteListOfSuggestions(): void - { - $tester = $this->create([ - Suggestion::create('hello'), - Suggestion::create('goodbye'), - ], true, true); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertInstanceOf(CompletionList::class, $response->result); - $this->assertCompletion([ - self::completionItem('hello', null), - self::completionItem('goodbye', null), - ], $response->result->items); - $this->assertTrue($response->result->isIncomplete); - } - - public function testHandleSuggestionsWithRange(): void - { - $tester = $this->create([ - Suggestion::createWithOptions('hello', [ 'range' => PhpactorRange::fromStartAndEnd(1, 2)]), - ]); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertCompletion([ - self::completionItem('hello', null, ['textEdit' => new TextEdit( - new Range(new Position(0, 1), new Position(0, 2)), - 'hello' - )]) - ], $response->result->items); - $this->assertFalse($response->result->isIncomplete); - } - - public function testSuggestionWithClassImport(): void - { - $tester = $this->create( - [ - Suggestion::createWithOptions( - 'hello', - [ - 'type' => 'class', - 'name_import' => '\Foo\Bar', - 'range' => PhpactorRange::fromStartAndEnd(0, 0), - ] - ), - ], - true, - false, - [ - [new TextEdit(new Range(new Position(0, 0), new Position(0, 4)), 'world')] - ] - ); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertCompletion( - [ - self::completionItem( - 'hello', - null, - [ - 'kind' => 7, - 'detail' => null, - 'insertText' => 'hello', - 'textEdit' => TextEdit::fromArray( - [ - 'newText' => 'hello', - 'range' => Range::fromArray( - [ - 'start' => Position::fromArray(['line' => 0, 'character' => 0]), - 'end' => Position::fromArray(['line' => 0, 'character' => 0]), - ] - ) - ] - ), - 'additionalTextEdits' => [ - TextEdit::fromArray([ - 'newText' => 'world', - 'range' => Range::fromArray([ - 'start' => Position::fromArray(['line' => 0, 'character' => 0]), - 'end' => Position::fromArray(['line' => 0, 'character' => 4]), - ]) - ]) - ] - ] - ) - ], - $response->result->items - ); - $this->assertFalse($response->result->isIncomplete); - } - - public function testSuggestionWithFunctionImport(): void - { - $tester = $this->create( - [ - Suggestion::createWithOptions( - 'async', - [ - 'type' => Suggestion::TYPE_FUNCTION, - 'name_import' => '\Amp\async', - 'range' => PhpactorRange::fromStartAndEnd(0, 0), - ] - ), - ], - true, - false, - [ - [new TextEdit(new Range(new Position(0, 0), new Position(0, 4)), 'async')] - ] - ); - $response = $tester->mustRequestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertCompletion( - [ - self::completionItem( - 'async', - null, - [ - 'kind' => 3, - 'detail' => null, - 'insertText' => 'async', - 'textEdit' => TextEdit::fromArray( - [ - 'newText' => 'async', - 'range' => Range::fromArray( - [ - 'start' => Position::fromArray(['line' => 0, 'character' => 0]), - 'end' => Position::fromArray(['line' => 0, 'character' => 0]), - ] - ) - ] - ), - 'additionalTextEdits' => [ - TextEdit::fromArray([ - 'newText' => 'async', - 'range' => Range::fromArray([ - 'start' => Position::fromArray(['line' => 0, 'character' => 0]), - 'end' => Position::fromArray(['line' => 0, 'character' => 4]), - ]) - ]) - ] - ] - ) - ], - /** @phpstan-ignore-next-line */ - $response->result->items - ); - /** @phpstan-ignore-next-line */ - $this->assertFalse($response->result->isIncomplete); - } - - public function testSuggestionWithImportAlias(): void - { - $importTextEdit = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), 'FooBar'); - - $tester = $this->create( - [ - Suggestion::createWithOptions( - 'hello', - [ - 'type' => 'class', - 'name_import' => '\Foo\Bar', - 'range' => PhpactorRange::fromStartAndEnd(0, 0), - ] - ), - ], - true, - false, - [ - [$importTextEdit] - ], - [ - 'FooBar' - ] - ); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertCompletion( - [ - self::completionItem( - 'hello', - null, - [ - 'kind' => 7, - 'detail' => null, - 'insertText' => 'FooBar', - 'textEdit' => TextEdit::fromArray( - [ - 'newText' => 'FooBar', - 'range' => Range::fromArray( - [ - 'start' => Position::fromArray(['line' => 0, 'character' => 0]), - 'end' => Position::fromArray(['line' => 0, 'character' => 0]), - ] - ) - ] - ), - 'additionalTextEdits' => [$importTextEdit] - ] - ) - ], - $response->result->items - ); - $this->assertFalse($response->result->isIncomplete); - } - - public function testCancelReturnsPartialResults(): void - { - $tester = $this->create( - array_map(function () { - return Suggestion::createWithOptions('hello', [ 'range' => PhpactorRange::fromStartAndEnd(1, 2)]); - }, range(0, 10000)) - ); - $response = $tester->request( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ], - 1 - ); - $responses =wait(all([ - $response, - call(function () use ($tester) { - yield new Delayed(10); - $tester->cancel(1); - }) - ])); - - $this->assertGreaterThan(1, count($responses[0]->result->items)); - $this->assertTrue($responses[0]->result->isIncomplete); - } - - public function testHandleSuggestionsWithSnippets(): void - { - $tester = $this->create([ - Suggestion::createWithOptions('hello', [ - 'type' => Suggestion::TYPE_METHOD, - 'label' => 'hello' - ]), - Suggestion::createWithOptions('goodbye', [ - 'type' => Suggestion::TYPE_METHOD, - 'snippet' => 'goodbye()', - ]), - Suggestion::createWithOptions('$var', [ - 'type' => Suggestion::TYPE_VARIABLE, - ]), - ]); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertCompletion([ - self::completionItem('hello', 2), - self::completionItem('goodbye', 2, ['insertText' => 'goodbye()', 'insertTextFormat' => 2]), - self::completionItem('var', 6, [ - 'label' => '$var', - ]), - ], $response->result->items); - $this->assertFalse($response->result->isIncomplete); - } - - public function testHandleSuggestionsWithSnippetsWhenClientDoesNotSupportIt(): void - { - $tester = $this->create([ - Suggestion::createWithOptions('hello', [ - 'type' => Suggestion::TYPE_METHOD, - 'label' => 'hello' - ]), - Suggestion::createWithOptions('goodbye', [ - 'type' => Suggestion::TYPE_METHOD, - 'snippet' => 'goodbye()', - ]), - Suggestion::createWithOptions('$var', [ - 'type' => Suggestion::TYPE_VARIABLE, - ]), - ], false); - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $this->assertCompletion([ - self::completionItem('hello', 2), - self::completionItem('goodbye', 2), - self::completionItem('var', 6, [ - 'label' => '$var', - ]), - ], $response->result->items); - $this->assertFalse($response->result->isIncomplete); - } - - public function testHandleSuggestionsWithPriority(): void - { - $tester = $this->create([ - Suggestion::createWithOptions('hello', [ - 'type' => Suggestion::TYPE_METHOD, - 'label' => 'hello', - 'priority' => Suggestion::PRIORITY_HIGH - ]), - Suggestion::createWithOptions('goodbye', [ - 'type' => Suggestion::TYPE_METHOD, - 'snippet' => 'goodbye()', - 'priority' => Suggestion::PRIORITY_LOW - ]), - Suggestion::createWithOptions('$var', [ - 'type' => Suggestion::TYPE_VARIABLE, - ]), - ], false); - - $response = $tester->requestAndWait( - 'textDocument/completion', - [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - - $this->assertCompletion([ - self::completionItem('hello', 2, [ - 'sortText' => '0064-hello', - ]), - self::completionItem('goodbye', 2, [ - 'sortText' => '0255-goodbye', - ]), - self::completionItem('var', 6, [ - 'label' => '$var', - ]), - ], $response->result->items); - $this->assertFalse($response->result->isIncomplete); - } - - private static function completionItem( - string $label, - ?int $type, - array $data = [] - ): CompletionItem { - return Invoke::new(CompletionItem::class, \array_merge([ - 'label' => $label, - 'kind' => $type, - 'detail' => '', - 'documentation' => new MarkupContent(MarkupKind::MARKDOWN, ''), - 'insertText' => $label, - 'insertTextFormat' => 1, - ], $data)); - } - - private function create( - array $suggestions, - bool $supportSnippets = true, - bool $isIncomplete = false, - array $importNameTextEdits = [], - array $aliases = [] - ): LanguageServerTester { - $completor = $this->createCompletor($suggestions, $isIncomplete); - $registry = new TypedCompletorRegistry([ - 'php' => $completor, - ]); - $builder = LanguageServerTesterBuilder::create(); - $tester = $builder->addHandler(new CompletionHandler( - $builder->workspace(), - $registry, - new SuggestionNameFormatter(true), - $this->createNameImporter($suggestions, $aliases, $importNameTextEdits), - $supportSnippets, - true - ))->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_TEXT); - - return $tester; - } - - /** - * @param array $suggestions - * @param array $aliases - */ - private function createNameImporter( - array $suggestions, - array $aliases, - array $importNameTextEdits - ): NameImporter { - $results = []; - - foreach ($suggestions as $i => $suggestion) { - /** @var Suggestion $suggestion */ - $textEdits = $importNameTextEdits[$i] ?? null; - $alias = $aliases[$i] ?? null; - - if ($suggestion->type() === 'function') { - $nameImport = NameImport::forFunction($suggestion->name(), $alias); - } else { - $nameImport = NameImport::forClass($suggestion->name(), $alias); - } - - $results[] = NameImporterResult::createResult($nameImport, $textEdits); - } - - $importNameMock = $this->getMockBuilder(NameImporter::class) - ->disableOriginalConstructor() - ->getMock(); - - $importNameMock->method('__invoke') - ->willReturnOnConsecutiveCalls(...$results); - - return $importNameMock; - } - - private function createCompletor(array $suggestions, bool $isIncomplete = false): Completor - { - return new class($suggestions, $isIncomplete) implements Completor { - public function __construct( - /** @var Suggestion[] */ - private array $suggestions, - private bool $isIncomplete - ) { - } - - public function complete(TextDocument $source, ByteOffset $offset): Generator - { - foreach ($this->suggestions as $suggestion) { - yield $suggestion; - - // simulate work - usleep(100); - } - - return !$this->isIncomplete; - } - }; - } - - /** - * @param CompletionItem[] $expectedItems - * @param CompletionItem[] $items - */ - private function assertCompletion(array $expectedItems, array $items): void - { - foreach ($expectedItems as $index => $expected) { - $actual = $items[$index]; - self::assertEquals($actual->detail, $expected->detail); - self::assertEquals($actual->kind, $expected->kind); - self::assertEquals($actual->insertText, $expected->insertText); - self::assertEquals($actual->additionalTextEdits, $expected->additionalTextEdits); - self::assertEquals($actual->label, $expected->label); - } - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/HoverHandlerTest.php b/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/HoverHandlerTest.php deleted file mode 100644 index 7516d0d0ed..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/HoverHandlerTest.php +++ /dev/null @@ -1,121 +0,0 @@ -createTester(); - $tester->textDocument()->open(self::PATH, $text); - - $response = $tester->requestAndWait('textDocument/hover', [ - 'textDocument' => new TextDocumentIdentifier(self::PATH), - 'position' => PositionConverter::byteOffsetToPosition(ByteOffset::fromInt((int)$offset), $text) - ]); - $tester->assertSuccess($response); - $result = $response->result; - $this->assertInstanceOf(Hover::class, $result); - } - - public static function provideHover(): Generator - { - yield 'var' => [ - 'oo;', - ]; - yield 'interface type' => [ - 'oo = "bar"', - ]; - - - yield 'poperty' => [ - 'b; }', - ]; - - yield 'method' => [ - 'oo():string {} }', - ]; - - yield 'method with documentation' => [ - <<<'EOT' - oo():string {} - } - EOT - , - ]; - - yield 'method with parent documentation' => [ - <<<'EOT' - oo():string {} - } - EOT - , - ]; - - yield 'method on a union' => [ - <<<'EOT' - fo<>o(); - } - - EOT - , - ]; - - yield 'class' => [ - 'ass A { } }', - 'A' - ]; - - yield 'unknown function' => [ - '()' - ]; - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/SignatureHelpHandlerTest.php b/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/SignatureHelpHandlerTest.php deleted file mode 100644 index aff192786c..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/SignatureHelpHandlerTest.php +++ /dev/null @@ -1,55 +0,0 @@ -create([]); - $tester->textDocument()->open(self::IDENTIFIER, 'hello'); - $response = $tester->requestAndWait( - 'textDocument/signatureHelp', - [ - 'textDocument' => new TextDocumentIdentifier(self::IDENTIFIER), - 'position' => ProtocolFactory::position(0, 0) - ] - ); - $list = $response->result; - $this->assertInstanceOf(LspSignatureHelp::class, $list); - } - - private function create(array $suggestions): LanguageServerTester - { - $builder = LanguageServerTesterBuilder::create(); - return $builder->addHandler(new SignatureHelpHandler( - $builder->workspace(), - $this->createHelper() - ))->build(); - } - - private function createHelper(): SignatureHelper - { - return new class() implements SignatureHelper { - public function signatureHelp(TextDocument $textDocument, ByteOffset $offset): SignatureHelp - { - $help = new SignatureHelp([], 0); - return $help; - } - }; - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Unit/LanguageServerCompletionExtensionTest.php b/lib/Extension/LanguageServerCompletion/Tests/Unit/LanguageServerCompletionExtensionTest.php deleted file mode 100644 index 6b7bf6ea58..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Unit/LanguageServerCompletionExtensionTest.php +++ /dev/null @@ -1,46 +0,0 @@ -createTester(); - - $position = new Position(0, 0); - $tester->textDocument()->open('/test', 'hello'); - - $response = $tester->requestAndWait('textDocument/completion', [ - 'textDocument' => new TextDocumentIdentifier('/test'), - 'position' => $position, - ]); - - $this->assertInstanceOf(ResponseMessage::class, $response); - $this->assertNull($response->error); - $this->assertInstanceOf(CompletionList::class, $response->result); - } - - public function testSignatureProvider(): void - { - $tester = $this->createTester(); - - $position = new Position(0, 0); - $tester->textDocument()->open('/test', 'hello'); - - $response = $tester->requestAndWait('textDocument/signatureHelp', [ - 'textDocument' => new TextDocumentIdentifier('/test'), - 'position' => $position, - ]); - - $this->assertInstanceOf(ResponseMessage::class, $response); - $this->assertNull($response->error); - $this->assertNull($response->result); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspCompletionTypeTest.php b/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspCompletionTypeTest.php deleted file mode 100644 index 114aa8e79d..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspCompletionTypeTest.php +++ /dev/null @@ -1,22 +0,0 @@ -getConstants() as $name => $constantValue) { - if (!str_starts_with($name, 'TYPE')) { - continue; - } - $this->assertNotNull(PhpactorToLspCompletionType::fromPhpactorType($constantValue), $constantValue); - } - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspSignatureTest.php b/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspSignatureTest.php deleted file mode 100644 index d927fcfadc..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspSignatureTest.php +++ /dev/null @@ -1,37 +0,0 @@ -assertInstanceOf(LspSignatureHelp::class, $help); - $this->assertCount(1, $help->signatures); - $this->assertCount(2, $help->signatures[0]->parameters); - $signature = $help->signatures[0]; - $this->assertInstanceOf(LspSignatureInformation::class, $signature); - $this->assertEquals('foo', $signature->label); - - $this->assertInstanceOf(PhpactorParameterInformation::class, $help->signatures[0]->parameters[0]); - $this->assertEquals('$one', $help->signatures[0]->parameters[0]->label); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/SuggestionNameFormatterTest.php b/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/SuggestionNameFormatterTest.php deleted file mode 100644 index 26bf06bc0c..0000000000 --- a/lib/Extension/LanguageServerCompletion/Tests/Unit/Util/SuggestionNameFormatterTest.php +++ /dev/null @@ -1,37 +0,0 @@ -formatter = new SuggestionNameFormatter(true); - } - - #[DataProvider('dataProvider')] - public function testFormat(string $type, string $name, string $expected): void - { - $suggestion = Suggestion::createWithOptions($name, ['type' => $type]); - - $this->assertSame($expected, $this->formatter->format($suggestion)); - } - - /** - * @return Generator - */ - public static function dataProvider(): Generator - { - yield [Suggestion::TYPE_VARIABLE, '$foo', 'foo']; - yield [Suggestion::TYPE_FUNCTION, 'foo', 'foo']; - yield [Suggestion::TYPE_FIELD, 'foo', 'foo']; - } -} diff --git a/lib/Extension/LanguageServerCompletion/Util/PhpactorToLspCompletionType.php b/lib/Extension/LanguageServerCompletion/Util/PhpactorToLspCompletionType.php deleted file mode 100644 index 5c21f90328..0000000000 --- a/lib/Extension/LanguageServerCompletion/Util/PhpactorToLspCompletionType.php +++ /dev/null @@ -1,35 +0,0 @@ - CompletionItemKind::METHOD, - Suggestion::TYPE_FUNCTION => CompletionItemKind::FUNCTION, - Suggestion::TYPE_CONSTRUCTOR => CompletionItemKind::CONSTRUCTOR, - Suggestion::TYPE_FIELD => CompletionItemKind::FIELD, - Suggestion::TYPE_VARIABLE => CompletionItemKind::VARIABLE, - Suggestion::TYPE_CLASS => CompletionItemKind::CLASS_, - Suggestion::TYPE_INTERFACE => CompletionItemKind::INTERFACE, - Suggestion::TYPE_MODULE => CompletionItemKind::MODULE, - Suggestion::TYPE_PROPERTY => CompletionItemKind::PROPERTY, - Suggestion::TYPE_UNIT => CompletionItemKind::UNIT, - Suggestion::TYPE_VALUE => CompletionItemKind::VALUE, - Suggestion::TYPE_ENUM => CompletionItemKind::ENUM, - Suggestion::TYPE_KEYWORD => CompletionItemKind::KEYWORD, - Suggestion::TYPE_SNIPPET => CompletionItemKind::KEYWORD, - Suggestion::TYPE_COLOR => CompletionItemKind::COLOR, - Suggestion::TYPE_FILE => CompletionItemKind::FILE, - Suggestion::TYPE_REFERENCE => CompletionItemKind::REFERENCE, - Suggestion::TYPE_CONSTANT => CompletionItemKind::CONSTANT, - Suggestion::TYPE_FIELD => CompletionItemKind::FIELD, - default => null, - }; - } -} diff --git a/lib/Extension/LanguageServerCompletion/Util/PhpactorToLspSignature.php b/lib/Extension/LanguageServerCompletion/Util/PhpactorToLspSignature.php deleted file mode 100644 index a314d60f17..0000000000 --- a/lib/Extension/LanguageServerCompletion/Util/PhpactorToLspSignature.php +++ /dev/null @@ -1,35 +0,0 @@ -signatures() as $phpactorSignature) { - $parameters = []; - foreach ($phpactorSignature->parameters() as $phpactorParameter) { - $parameters[] = new ParameterInformation( - '$' . $phpactorParameter->label(), - new MarkupContent(MarkupKind::MARKDOWN, $phpactorParameter->documentation()) - ); - } - - $signatures[] = new SignatureInformation( - $phpactorSignature->label(), - new MarkupContent(MarkupKind::MARKDOWN, $phpactorSignature->documentation() ?? ''), - $parameters - ); - } - - return new SignatureHelp($signatures, $phpactorHelp->activeSignature(), $phpactorHelp->activeParameter()); - } -} diff --git a/lib/Extension/LanguageServerCompletion/Util/SuggestionNameFormatter.php b/lib/Extension/LanguageServerCompletion/Util/SuggestionNameFormatter.php deleted file mode 100644 index e695aa01de..0000000000 --- a/lib/Extension/LanguageServerCompletion/Util/SuggestionNameFormatter.php +++ /dev/null @@ -1,22 +0,0 @@ -name(); - return match ($suggestion->type()) { - Suggestion::TYPE_VARIABLE => $this->trimLeadingDollar ? mb_substr($name, 1) : $name, - Suggestion::TYPE_FUNCTION, Suggestion::TYPE_METHOD => $name, - default => $name, - }; - } -} diff --git a/lib/Extension/LanguageServerConfiguration/LanguageServerConfigurationExtension.php b/lib/Extension/LanguageServerConfiguration/LanguageServerConfigurationExtension.php deleted file mode 100644 index a2e00f1f8a..0000000000 --- a/lib/Extension/LanguageServerConfiguration/LanguageServerConfigurationExtension.php +++ /dev/null @@ -1,49 +0,0 @@ -register(AutoConfigListener::class, function (Container $container) { - if (false === $container->parameter(self::AUTO_CONFIG)->bool()) { - return null; - } - - return new AutoConfigListener( - $container->get(Configurator::class), - $container->get(ClientApi::class), - $container->parameter(CoreExtension::PARAM_TRUSTED)->bool(), - ); - }, [ - LanguageServerExtension::TAG_LISTENER_PROVIDER => [], - ]); - } - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::AUTO_CONFIG => true, - ]); - $schema->setDescriptions([ - self::AUTO_CONFIG => 'Prompt to enable extensions which apply to your project on language server start' - ]); - $schema->setTypes([ - self::AUTO_CONFIG => 'boolean' - ]); - - } -} diff --git a/lib/Extension/LanguageServerConfiguration/Listener/AutoConfigListener.php b/lib/Extension/LanguageServerConfiguration/Listener/AutoConfigListener.php deleted file mode 100644 index 074e97fce9..0000000000 --- a/lib/Extension/LanguageServerConfiguration/Listener/AutoConfigListener.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ - public function getListenersForEvent(object $event): iterable - { - if ($event instanceof Initialized) { - yield function (): void { - $this->autoConfigure(); - }; - } - } - - private function autoConfigure(): void - { - asyncCall(function () { - $changes = 0; - foreach ($this->configurator->suggestChanges() as $change) { - $res = yield $this->clientApi->window()->showMessageRequest()->info( - $change->prompt(), - new MessageActionItem(self::YES), - new MessageActionItem(self::NO) - ); - $this->configurator->apply($change, $res->title === self::YES); - $changes++; - - // artificial delay to prevent neovim from producing concatenated prompts - yield delay(100); - } - - if ($changes) { - $api = $this->clientApi->window()->showMessage(); - if ($this->trusted) { - $api->info(sprintf('%d changes applied to .phpactor.json, restart the language server for them to take effect', $changes)); - return; - } - - $api->warning(sprintf('%d changes applied to .phpactor.json but you will need to trust this file for changes to take affect', $changes)); - } - }); - } -} diff --git a/lib/Extension/LanguageServerDiagnostics/LanguageServerDiagnosticsExtension.php b/lib/Extension/LanguageServerDiagnostics/LanguageServerDiagnosticsExtension.php deleted file mode 100644 index 870b8a0901..0000000000 --- a/lib/Extension/LanguageServerDiagnostics/LanguageServerDiagnosticsExtension.php +++ /dev/null @@ -1,33 +0,0 @@ -register(PhpLintDiagnosticProvider::class, function (Container $container) { - return new PhpLintDiagnosticProvider( - new PhpLinter(PHP_BINARY), - $container->get(TextDocumentLocator::class) - ); - }, [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('php') - ]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerDiagnostics/Model/PhpLinter.php b/lib/Extension/LanguageServerDiagnostics/Model/PhpLinter.php deleted file mode 100644 index a1b7f6e36f..0000000000 --- a/lib/Extension/LanguageServerDiagnostics/Model/PhpLinter.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ - public function lint(TextDocument $textDocument): Promise - { - return call(function () use ($textDocument) { - $process = new Process([ - $this->phpBin, - '-l', - '-d', - 'display_errors=stdout', - ]); - $pid = yield $process->start(); - yield $process->getStdin()->write($textDocument->__toString()); - yield $process->getStdin()->end(); - $exitCode = yield $process->join(); - - if ($exitCode == 0) { - return []; - } - - $err = yield buffer($process->getStdout()); - - if (!$err) { - return []; - } - - if (!preg_match('/line ([0-9]+)/i', $err, $line)) { - return []; - } - - $line = (int)$line[1] - 1; - $range = (new LineColRangeForLine())->rangeFromLine($textDocument->__toString(), $line + 1); - - return [ - new Diagnostic( - range: new Range( - new Position($line, $range->start()->col() - 1), - new Position($line, $range->end()->col() - 1) - ), - message: $err, - severity: DiagnosticSeverity::ERROR - ) - ]; - }); - } -} diff --git a/lib/Extension/LanguageServerDiagnostics/Provider/PhpLintDiagnosticProvider.php b/lib/Extension/LanguageServerDiagnostics/Provider/PhpLintDiagnosticProvider.php deleted file mode 100644 index aa2a1349eb..0000000000 --- a/lib/Extension/LanguageServerDiagnostics/Provider/PhpLintDiagnosticProvider.php +++ /dev/null @@ -1,36 +0,0 @@ -linter->lint( - $this->locator->get(TextDocumentUri::fromString($textDocument->uri)) - ); - }); - } - - public function name(): string - { - return 'php-lint'; - } -} diff --git a/lib/Extension/LanguageServerDiagnostics/Tests/Unit/PhpLinterTest.php b/lib/Extension/LanguageServerDiagnostics/Tests/Unit/PhpLinterTest.php deleted file mode 100644 index 6ead808b61..0000000000 --- a/lib/Extension/LanguageServerDiagnostics/Tests/Unit/PhpLinterTest.php +++ /dev/null @@ -1,27 +0,0 @@ -build(); - $linter = new PhpLinter(PHP_BINARY); - $diagnostics = wait($linter->lint($document)); - self::assertCount(0, $diagnostics); - } - - public function testLintInvalid(): void - { - $document = TextDocumentBuilder::create('build(); - $linter = new PhpLinter(PHP_BINARY); - $diagnostics = wait($linter->lint($document)); - self::assertCount(1, $diagnostics); - } -} diff --git a/lib/Extension/LanguageServerEvaluatableExpression/Handler/EvaluatableExpressionHandler.php b/lib/Extension/LanguageServerEvaluatableExpression/Handler/EvaluatableExpressionHandler.php deleted file mode 100644 index 3346536cb6..0000000000 --- a/lib/Extension/LanguageServerEvaluatableExpression/Handler/EvaluatableExpressionHandler.php +++ /dev/null @@ -1,110 +0,0 @@ - 'xevaluatableExpression', - ]; - } - - /** - * @return Promise - */ - public function xevaluatableExpression( - TextDocumentIdentifier $textDocument, - Position $position - ): Promise { - $document = $this->workspace->get($textDocument->uri); - $offset = PositionConverter::positionToByteOffset($position, $document->text); - $document = TextDocumentBuilder::create($document->text) - ->uri($document->uri) - ->language('php') - ->build(); - - $char = substr($document, $offset->toInt(), 1); - - // do not provide evaluatable for whitespace - if (trim($char) == '') { - return new Success(null); - } - - $rootNode = $this->parser->get($document); - $node = $rootNode->getDescendantNodeAtPosition($offset->toInt()); - return new Success($this->nodeToEvaluatable($node)); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->experimental ??= []; - // @phpstan-ignore offsetAccess.nonOffsetAccessible - $capabilities->experimental['xevaluatableExpressionProvider'] = true; - } - - private function nodeToEvaluatable(Node $node): ?EvaluatableExpression - { - if ($node instanceof Parameter) { - return $this->evaluatableExpressionForNode($node->variableName, $node); - } - if ( - $node instanceof Variable || - $node instanceof SubscriptExpression || - $node instanceof MemberAccessExpression - ) { - return $this->evaluatableExpressionForNode($node, $node); - } - if ($node2 = $node->getFirstAncestor(SubscriptExpression::class)) { - return $this->evaluatableExpressionForNode($node2, $node2); - } - return null; - } - - private function evaluatableExpressionForNode(Node|Token $token, Node $textNode): EvaluatableExpression - { - return - new EvaluatableExpression( - expression: (string)$token->getText($textNode->getFileContents()), - range: $this->byteOffsetRangeForNode($token, $textNode), - ); - } - - /** - * Converts Microsoft PhpParser Node to LSP Range. - */ - private function byteOffsetRangeForNode(Node|Token $token, Node $textNode): Range - { - return new Range( - PositionConverter::intByteOffsetToPosition($token->getStartPosition(), $textNode->getFileContents()), - PositionConverter::intByteOffsetToPosition($token->getEndPosition(), $textNode->getFileContents()), - ); - } -} diff --git a/lib/Extension/LanguageServerEvaluatableExpression/LanguageServerEvaluatableExpressionExtension.php b/lib/Extension/LanguageServerEvaluatableExpression/LanguageServerEvaluatableExpressionExtension.php deleted file mode 100644 index 307391bf27..0000000000 --- a/lib/Extension/LanguageServerEvaluatableExpression/LanguageServerEvaluatableExpressionExtension.php +++ /dev/null @@ -1,30 +0,0 @@ -register('language_server_evaluatable_expression.handler', function (Container $container) { - return new EvaluatableExpressionHandler( - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class), - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => []]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerEvaluatableExpression/Protocol/EvaluatableExpression.php b/lib/Extension/LanguageServerEvaluatableExpression/Protocol/EvaluatableExpression.php deleted file mode 100644 index 4ca770674d..0000000000 --- a/lib/Extension/LanguageServerEvaluatableExpression/Protocol/EvaluatableExpression.php +++ /dev/null @@ -1,34 +0,0 @@ -expression = $expression; - $this->range = $range; - } - - /** - * @param array $array - * @return self - */ - public static function fromArray(array $array, bool $allowUnknownKeys = false) - { - if (!is_array($array['range'])) { - throw new RuntimeException('Missing "range"'); - } - $range = Range::fromArray($array['range']); - $expression = is_string($array['expression']) ? $array['expression'] : null; - return new self($range, $expression); - } - -} diff --git a/lib/Extension/LanguageServerEvaluatableExpression/Tests/EvaluatableExpressionHandlerTest.php b/lib/Extension/LanguageServerEvaluatableExpression/Tests/EvaluatableExpressionHandlerTest.php deleted file mode 100644 index a9bcb2a654..0000000000 --- a/lib/Extension/LanguageServerEvaluatableExpression/Tests/EvaluatableExpressionHandlerTest.php +++ /dev/null @@ -1,71 +0,0 @@ -createTester(); - $tester->textDocument()->open(self::PATH, $text); - - $response = $tester->requestAndWait('textDocument/xevaluatableExpression', [ - 'textDocument' => new TextDocumentIdentifier(self::PATH), - 'position' => PositionConverter::byteOffsetToPosition(ByteOffset::fromInt((int)$offset), $text) - ]); - self::assertNotNull($response); - $tester->assertSuccess($response); - $result = $response->result; - $this->assertInstanceOf(EvaluatableExpression::class, $result); - $this->assertEquals($eval, $result->expression); - } - - public static function provideEvaluatableExpression(): Generator - { - yield 'var' => [ - '$f<>oo<>;', - ]; - yield 'array' => [ - '$f["aa<>a"]<>;', - ]; - yield 'object array' => [ - '$foo->abc["aa<>a"]<> == "test") {};', - ]; - yield 'inner var' => [ - 'abc[<>$aa<>a<>] == "test") {};', - ]; - yield 'just foo' => [ - '$fo<>o<>->abc[$aaa] == "test") {};', - ]; - yield 'arg' => [ - '$a<>rg<>) {};', - ]; - } - - protected function createTester(): LanguageServerTester - { - $tester = LanguageServerTesterBuilder::create(); - $tester->addHandler(new EvaluatableExpressionHandler($tester->workspace(), new TolerantAstProvider())); - return $tester->build(); - } -} diff --git a/lib/Extension/LanguageServerHighlight/LanguageServerHighlightExtension.php b/lib/Extension/LanguageServerHighlight/LanguageServerHighlightExtension.php deleted file mode 100644 index dfe4410f70..0000000000 --- a/lib/Extension/LanguageServerHighlight/LanguageServerHighlightExtension.php +++ /dev/null @@ -1,43 +0,0 @@ -register(HighlightHandler::class, function (Container $container) { - if ($container->parameter(self::PARAM_ENABLE)->bool() === false) { - return null; - } - - return new HighlightHandler( - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - new Highlighter($container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class)), - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => [] ]); - } - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_ENABLE => true, - ]); - $schema->setDescriptions([ - self::PARAM_ENABLE => 'Enable or disable the highlighter (can be expensive on large documents)', - ]); - } -} diff --git a/lib/Extension/LanguageServerHover/Handler/HoverHandler.php b/lib/Extension/LanguageServerHover/Handler/HoverHandler.php deleted file mode 100644 index 12367c33de..0000000000 --- a/lib/Extension/LanguageServerHover/Handler/HoverHandler.php +++ /dev/null @@ -1,211 +0,0 @@ - 'hover', - ]; - } - - /** - * @return Promise - */ - public function hover( - TextDocumentIdentifier $textDocument, - Position $position - ): Promise { - return call(function () use ($textDocument, $position) { - $document = $this->workspace->get($textDocument->uri); - $offset = PositionConverter::positionToByteOffset($position, $document->text); - $document = TextDocumentBuilder::create($document->text) - ->uri($document->uri) - ->language('php') - ->build(); - - $char = substr($document, $offset->toInt(), 1); - - // do not provide hover for whitespace - if (trim($char) == '') { - return null; - } - - $offsetReflection = $this->reflector->reflectOffset($document, $offset); - $info = $this->infoFromReflecionOffset($offsetReflection); - $string = new MarkupContent('markdown', $info); - $nodeContext = $offsetReflection->nodeContext(); - - return new Hover($string, new Range( - PositionConverter::byteOffsetToPosition( - ByteOffset::fromInt($nodeContext->symbol()->position()->start()->toInt()), - $document->__toString() - ), - PositionConverter::byteOffsetToPosition( - ByteOffset::fromInt($nodeContext->symbol()->position()->end()->toInt()), - $document->__toString() - ) - )); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->hoverProvider = true; - } - - private function infoFromReflecionOffset(ReflectionOffset $offset): string - { - $nodeContext = $offset->nodeContext(); - - if ($info = $this->infoFromSymbolContext($nodeContext)) { - return $info; - } - - return $this->renderer->render($offset); - } - - private function infoFromSymbolContext(NodeContext $nodeContext): ?string - { - try { - return $this->renderSymbolContext($nodeContext); - } catch (CouldNotFormat) { - } - - return null; - } - - private function renderSymbolContext(NodeContext $nodeContext): ?string - { - return match ($nodeContext->symbol()->symbolType()) { - Symbol::METHOD, Symbol::PROPERTY, Symbol::CONSTANT => $this->renderMember($nodeContext), - Symbol::CLASS_ => $this->renderClass($nodeContext->type()), - Symbol::FUNCTION => $this->renderFunction($nodeContext), - Symbol::DECLARED_CONSTANT => $this->renderDeclaredConstant($nodeContext), - default => null, - }; - } - - private function renderMember(NodeContext $nodeContext): string - { - $name = $nodeContext->symbol()->name(); - $container = $nodeContext->containerType(); - $infos = []; - - foreach ($container->expandTypes()->classLike() as $namedType) { - try { - $class = $this->reflector->reflectClassLike((string) $namedType); - $member = null; - $sep = '#'; - - // note that all class-likes (classes, traits and interfaces) have - // methods but not all have constants or properties, so we play safe - // with members() which is first-come-first-serve, rather than risk - // a fatal error because of a non-existing method. - $symbolType = $nodeContext->symbol()->symbolType(); - switch ($symbolType) { - case Symbol::METHOD: - $member = $class->methods()->get($name); - $sep = '#'; - break; - case Symbol::CONSTANT: - $sep = '::'; - $member = $class->members()->get($name); - break; - case Symbol::PROPERTY: - $sep = '$'; - $member = $class->members()->get($name); - break; - default: - return sprintf('Unknown symbol type "%s"', $symbolType); - } - - $infos[] = $this->renderer->render(new HoverInformation( - $namedType->short() .' '.$sep.' '.(string)$member->name(), - $this->renderer->render( - new MemberDocblock($member) - ), - $member - )); - } catch (NotFound) { - continue; - } - } - - return implode("\n", $infos); - } - - private function renderFunction(NodeContext $nodeContext): string - { - $name = $nodeContext->symbol()->name(); - try { - $function = $this->reflector->reflectFunction($name); - } catch (NotFound $notFound) { - return $notFound->getMessage(); - } - - return $this->renderer->render(new HoverInformation($name, $this->renderer->render($function->docblock()), $function)); - } - - private function renderClass(Type $type): string - { - try { - $class = $this->reflector->reflectClassLike((string) $type); - return $this->renderer->render(new HoverInformation( - $type->__toString(), - $class->docblock()->formatted(), - $class - )); - } catch (NotFound $e) { - return $e->getMessage(); - } - } - - private function renderDeclaredConstant(NodeContext $context): ?string - { - try { - $constant = $this->reflector->reflectConstant($context->symbol()->name()); - return $this->renderer->render(new HoverInformation( - $context->symbol()->name(), - $constant->docblock()->formatted(), - $constant - )); - } catch (NotFound $e) { - return $e->getMessage(); - } - } -} diff --git a/lib/Extension/LanguageServerHover/LanguageServerHoverExtension.php b/lib/Extension/LanguageServerHover/LanguageServerHoverExtension.php deleted file mode 100644 index bf223ce51e..0000000000 --- a/lib/Extension/LanguageServerHover/LanguageServerHoverExtension.php +++ /dev/null @@ -1,37 +0,0 @@ -register('language_server_completion.handler.hover', function (Container $container) { - return new HoverHandler( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(ObjectRendererExtension::SERVICE_MARKDOWN_RENDERER) - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => []]); - - $container->register(TwigFunctions::class, function (Container $container) { - return new TwigFunctions(); - }, [ - ObjectRendererExtension::TAG_TWIG_EXTENSION => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerHover/Renderer/HoverInformation.php b/lib/Extension/LanguageServerHover/Renderer/HoverInformation.php deleted file mode 100644 index aa5e3521a6..0000000000 --- a/lib/Extension/LanguageServerHover/Renderer/HoverInformation.php +++ /dev/null @@ -1,28 +0,0 @@ -docs)); - } - - public function name(): string - { - return $this->name; - } - - public function object(): object - { - return $this->object; - } -} diff --git a/lib/Extension/LanguageServerHover/Renderer/MemberDocblock.php b/lib/Extension/LanguageServerHover/Renderer/MemberDocblock.php deleted file mode 100644 index 7096ce36f4..0000000000 --- a/lib/Extension/LanguageServerHover/Renderer/MemberDocblock.php +++ /dev/null @@ -1,71 +0,0 @@ -buildAncestors($this->member->class())), function (ReflectionMember $member) { - return $member->docblock()->isDefined() && !empty(trim($member->docblock()->raw())); - }); - } - - public function member(): ReflectionMember - { - return $this->member; - } - - private function buildAncestors(?ReflectionClassLike $classLike, array $ancestors = []): array - { - if (null === $classLike) { - return $ancestors; - } - - $name = $classLike->name()->full(); - - if (isset($ancestors[$name])) { - return $ancestors; - } - - if ($classLike instanceof ReflectionClass) { - if ($classLike->methods()->belongingTo($classLike->name())->has($this->member->name())) { - $ancestors[$name] = $classLike->methods()->belongingTo($classLike->name())->get($this->member->name()); - } - - $ancestors = $this->buildAncestors($classLike->parent(), $ancestors); - - foreach ($classLike->interfaces() as $interface) { - $ancestors = $this->buildAncestors($interface, $ancestors); - } - - return $ancestors; - } - - if ($classLike instanceof ReflectionInterface) { - if ($classLike->methods()->belongingTo($classLike->name())->has($this->member->name())) { - $ancestors[$name] = $classLike->methods()->belongingTo($classLike->name())->get($this->member->name()); - } - - foreach ($classLike->parents() as $parent) { - $ancestors = $this->buildAncestors($parent, $ancestors); - } - - return $ancestors; - } - - return []; - } -} diff --git a/lib/Extension/LanguageServerHover/Twig/Functions/TypeShortName.php b/lib/Extension/LanguageServerHover/Twig/Functions/TypeShortName.php deleted file mode 100644 index 9918442a54..0000000000 --- a/lib/Extension/LanguageServerHover/Twig/Functions/TypeShortName.php +++ /dev/null @@ -1,14 +0,0 @@ -typeFromReflected($type); - } - - return null; - } - - private function typeFromReflected(ReflectedClassType $type): ?string - { - $reflection = $type->reflectionOrNull(); - - if (null === $reflection) { - return null; - } - - if ($reflection instanceof ReflectionInterface) { - return 'Ⓘ'; - } - - if ($reflection instanceof ReflectionClass) { - return 'Ⓒ'; - } - - if ($reflection instanceof ReflectionTrait) { - return 'Ⓣ'; - } - - if ($reflection instanceof ReflectionEnum) { - return 'Ⓔ'; - } - - return ''; - } -} diff --git a/lib/Extension/LanguageServerHover/Twig/TwigFunctions.php b/lib/Extension/LanguageServerHover/Twig/TwigFunctions.php deleted file mode 100644 index b710b048e3..0000000000 --- a/lib/Extension/LanguageServerHover/Twig/TwigFunctions.php +++ /dev/null @@ -1,26 +0,0 @@ -addFunction(new TwigFunction('typeShortName', new TypeShortName())); - - $env->addFunction(new TwigFunction('typeDefined', function (Type $type) { - return ($type->isDefined()); - })); - $env->addFunction(new TwigFunction('class', function ($type) { - return get_class($type); - })); - $env->addFunction(new TwigFunction('typeType', new TypeType())); - } -} diff --git a/lib/Extension/LanguageServerIndexer/Event/IndexReset.php b/lib/Extension/LanguageServerIndexer/Event/IndexReset.php deleted file mode 100644 index f50527726c..0000000000 --- a/lib/Extension/LanguageServerIndexer/Event/IndexReset.php +++ /dev/null @@ -1,7 +0,0 @@ - - */ - public function methods(): array - { - return [ - 'phpactor/indexer/reindex' => 'reindex', - 'phpactor/indexer/optimise' => 'optimise', - ]; - } - - /** - * @return array - */ - public function services(): array - { - return [ - self::SERVICE_INDEXER, - self::SERVICE_OPTIMISER, - ]; - } - - /** - * @return Promise - */ - public function indexer(CancellationToken $cancel): Promise - { - return call(function () use ($cancel) { - $job = $this->indexer->getJob(); - $size = $job->size(); - $token = WorkDoneToken::generate(); - - yield $this->progressNotifier->create($token); - $this->progressNotifier->begin($token, 'Indexing workspace', sprintf('%d PHP files', $size), 0); - - $start = microtime(true); - $index = 0; - foreach ($job->generator() as $file) { - $index++; - - if ($index % 500 === 0) { - $usage = MemoryUsage::create(); - $this->progressNotifier->report( - $token, - sprintf( - '%s/%s (%s%%, %s)', - $index, - $size, - number_format($index / $size * 100, 2), - $usage->memoryUsageFormatted() - ), - (int)round($index / $size * 100) - ); - } - - try { - $cancel->throwIfRequested(); - } catch (CancelledException) { - break; - } - - yield new Delayed(1); - } - - $process = yield $this->watcher->watch(); - $message = sprintf( - 'Done indexing (%ss, %s), watching with %s', - number_format(microtime(true) - $start, 2), - MemoryUsage::create()->memoryUsageFormatted(), - $this->watcher->describe() - ); - $this->progressNotifier->end($token, $message); - - (function (?int $timeout) use ($cancel): void { - if ($timeout === null) { - return; - } - asyncCall(function () use ($cancel, $timeout) { - while (true) { - if ($cancel->isRequested()) { - break; - } - yield delay($timeout); - yield $this->reindex(true); - } - }); - })($this->reindexTimeout); - - return yield from $this->watch($process, $cancel); - }); - } - - /** - * @return Promise - */ - public function reindex(bool $soft = false): Promise - { - return call(function () use ($soft): void { - if (false === $soft) { - $this->indexer->reset(); - } - - $this->eventDispatcher->dispatch(new IndexReset()); - }); - } - - /** - * @return Promise - */ - public function optimiser(CancellationToken $cancel): Promise - { - return call(function () use ($cancel) { - while (true) { - yield delay($this->optimiserTimeout); - - if ($cancel->isRequested()) { - break; - } - - yield $this->optimise($cancel); - } - }); - } - - /** - * @return Promise - */ - public function optimise(?CancellationToken $cancel = null): Promise - { - return call(function () use ($cancel) { - $token = WorkDoneToken::generate(); - $this->clientApi->workDoneProgress()->create($token); - $this->clientApi->workDoneProgress()->begin( - $token, - 'optimising index', - ); - $optimised = 0; - foreach ($this->indexer->optimise(false) as $tick) { - if ($tick !== null) { - $optimised++; - } - if ($cancel && $cancel->isRequested()) { - break; - } - yield new Delayed(0); - } - $this->clientApi->workDoneProgress()->end($token, sprintf( - '%d files optimised', - $optimised - )); - }); - } - - /** - * @return Generator - */ - private function watch(WatcherProcess $process, CancellationToken $cancel): Generator - { - asyncCall(function () use ($process, $cancel) { - while (true) { - try { - $cancel->throwIfRequested(); - } catch (CancelledException $cancelled) { - $previous = $cancelled->getPrevious(); - if ($previous) { - $this->logger->warning(sprintf('Watcher process cancelled: %s', $previous->getMessage())); - } - $this->logger->info('Watcher process cancelled'); - $process->stop(); - return; - } - yield new Delayed(100); - } - }); - try { - while (null !== $file = yield $process->wait()) { - try { - $cancel->throwIfRequested(); - } catch (CancelledException) { - $process->stop(); - break; - } - - try { - $this->logger->debug(sprintf('Indexing %s', $file->path())); - $this->indexer->index(TextDocumentBuilder::fromUri($file->path())->build()); - } catch (TextDocumentNotFound) { - $this->logger->warning(sprintf( - 'Trired to index non-existing file "%s"', - $file->path() - )); - continue; - } - $this->logger->debug(sprintf('Indexed file: %s', $file->path())); - yield new Delayed(0); - } - } catch (WatcherDied $watcherDied) { - $this->clientApi->window()->showMessage()->error(sprintf('File watcher died: %s', $watcherDied->getMessage())); - $this->logger->error($watcherDied->getMessage()); - } - } -} diff --git a/lib/Extension/LanguageServerIndexer/Handler/WorkspaceSymbolHandler.php b/lib/Extension/LanguageServerIndexer/Handler/WorkspaceSymbolHandler.php deleted file mode 100644 index 6daf08d714..0000000000 --- a/lib/Extension/LanguageServerIndexer/Handler/WorkspaceSymbolHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - 'symbol', - ]; - } - - /** - * @return Promise - */ - public function symbol( - WorkspaceSymbolParams $params - ): Promise { - return call(function () use ($params) { - return $this->provider->provideFor($params->query); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->workspaceSymbolProvider = true; - } -} diff --git a/lib/Extension/LanguageServerIndexer/LanguageServerIndexerExtension.php b/lib/Extension/LanguageServerIndexer/LanguageServerIndexerExtension.php deleted file mode 100644 index cdda0a25ab..0000000000 --- a/lib/Extension/LanguageServerIndexer/LanguageServerIndexerExtension.php +++ /dev/null @@ -1,117 +0,0 @@ -registerSessionHandler($container); - - $container->register(WorkspaceSymbolHandler::class, function (Container $container) { - return new WorkspaceSymbolHandler( - new WorkspaceSymbolProvider( - $container->get(SearchClient::class), - $container->get(TextDocumentLocator::class), - $container->getParameter(self::WORKSPACE_SYMBOL_SEARCH_LIMIT) - ) - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => [] ]); - - $container->register(LanguageServerWatcher::class, function (Container $container) { - return new LanguageServerWatcher( - $container->has(ClientCapabilities::class) ? $container->get(ClientCapabilities::class) : null - ); - }, [ - IndexerExtension::TAG_WATCHER => [ - 'name' => 'lsp', - ], - LanguageServerExtension::TAG_LISTENER_PROVIDER => [] - ]); - - $container->register(IndexerStatusProvider::class, function (Container $container) { - return new IndexerStatusProvider($container->get(Watcher::class)); - }, [ - LanguageServerExtension::TAG_STATUS_PROVIDER => [], - ]); - } - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::WORKSPACE_SYMBOL_SEARCH_LIMIT => 250, - self::PARAM_REINDEX_TIMEOUT => 300, - self::PARAM_OPTIMISER_TIMEOUT => 3600, - ]); - $schema->setTypes([ - self::PARAM_OPTIMISER_TIMEOUT => 'integer', - ]); - $schema->setDescriptions([ - self::PARAM_REINDEX_TIMEOUT => 'Unconditionally reindex modified files every N seconds', - self::PARAM_OPTIMISER_TIMEOUT => 'Optimise the index every N seconds', - ]); - } - - private function registerSessionHandler(ContainerBuilder $container): void - { - $container->register(IndexerHandler::class, function (Container $container) { - return new IndexerHandler( - $container->get(Indexer::class), - $container->get(Watcher::class), - $container->get(ClientApi::class), - LoggingExtension::channelLogger($container, 'lspindexer'), - $container->get(EventDispatcherInterface::class), - $container->get(ProgressNotifier::class), - (fn (mixed $timeout) => is_int($timeout) ? $timeout * 1000 : null)( - $container->parameter(self::PARAM_REINDEX_TIMEOUT)->value() - ), - $container->parameter(self::PARAM_OPTIMISER_TIMEOUT)->int() * 1000 - ); - }, [ - LanguageServerExtension::TAG_METHOD_HANDLER => [], - LanguageServerExtension::TAG_SERVICE_PROVIDER => [] - ]); - - $container->register(IndexerListener::class, function (Container $container) { - return new IndexerListener($container->get(ServiceManager::class)); - }, [ - LanguageServerExtension::TAG_LISTENER_PROVIDER => [], - ]); - $container->register(IndexOnSaveListener::class, function (Container $container) { - return new IndexOnSaveListener( - $container->get(Indexer::class), - $container->get(TextDocumentLocator::class) - ); - }, [ - LanguageServerExtension::TAG_LISTENER_PROVIDER => [], - ]); - } -} diff --git a/lib/Extension/LanguageServerIndexer/Listener/IndexOnSaveListener.php b/lib/Extension/LanguageServerIndexer/Listener/IndexOnSaveListener.php deleted file mode 100644 index 17aa1c0ec7..0000000000 --- a/lib/Extension/LanguageServerIndexer/Listener/IndexOnSaveListener.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ - public function getListenersForEvent($event): iterable - { - if ($event instanceof TextDocumentSaved) { - yield function () use ($event): void { - try { - $textDocument = $this->locator->get( - TextDocumentUri::fromString($event->identifier()->uri) - ); - } catch (TextDocumentNotFound) { - return; - } - $this->indexer->index($textDocument); - - // flush the index to make changes available to external - // processes (i.e. the outsourced diagnostics). - $this->indexer->flush(); - }; - } - } -} diff --git a/lib/Extension/LanguageServerIndexer/Listener/IndexerListener.php b/lib/Extension/LanguageServerIndexer/Listener/IndexerListener.php deleted file mode 100644 index 1ab3e08052..0000000000 --- a/lib/Extension/LanguageServerIndexer/Listener/IndexerListener.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ - public function getListenersForEvent(object $event): iterable - { - if ($event instanceof IndexReset) { - yield function (): void { - if ($this->manager->isRunning(IndexerHandler::SERVICE_INDEXER)) { - $this->manager->stop(IndexerHandler::SERVICE_INDEXER); - } - $this->manager->start(IndexerHandler::SERVICE_INDEXER); - }; - } - - if ($event instanceof WillShutdown) { - yield function (): void { - if ($this->manager->isRunning(IndexerHandler::SERVICE_INDEXER)) { - $this->manager->stop(IndexerHandler::SERVICE_INDEXER); - } - }; - } - } -} diff --git a/lib/Extension/LanguageServerIndexer/Model/WorkspaceSymbolProvider.php b/lib/Extension/LanguageServerIndexer/Model/WorkspaceSymbolProvider.php deleted file mode 100644 index d2ab971de3..0000000000 --- a/lib/Extension/LanguageServerIndexer/Model/WorkspaceSymbolProvider.php +++ /dev/null @@ -1,91 +0,0 @@ - - */ - public function provideFor(string $query): Promise - { - return call(function () use ($query) { - $infos = []; - foreach ($this->client->search(Criteria::shortNameContains($query)) as $count => $record) { - if ($count >= $this->limit) { - break; - } - - assert($record instanceof Record); - $infos[] = $this->informationFromRecord($record); - } - - return array_filter($infos, function (?SymbolInformation $info) { - return $info !== null; - }); - }); - } - - private function informationFromRecord(Record $record): ?SymbolInformation - { - $kind = match (true) { - $record instanceof ClassRecord => SymbolKind::CLASS_, - $record instanceof FunctionRecord => SymbolKind::FUNCTION, - $record instanceof ConstantRecord => SymbolKind::CONSTANT, - default => null - }; - - if ($kind === null) { - return null; - } - - /** @var ClassRecord|FunctionRecord|ConstantRecord $record */ - - $uri = TextDocumentUri::fromString($record->filePath()); - - return new SymbolInformation( - name: $record->fqn()->__toString(), - kind: $kind, - location: new Location( - $uri, - new Range( - $this->toLspPosition($record->start(), $uri), - $this->toLspPosition($record->start()->add(mb_strlen($record->shortName())), $uri) - ) - ) - ); - } - - private function toLspPosition(ByteOffset $offset, TextDocumentUri $uri): Position - { - return PositionConverter::byteOffsetToPosition( - $offset, - $this->locator->get($uri)->__toString() - ); - } -} diff --git a/lib/Extension/LanguageServerIndexer/Status/IndexerStatusProvider.php b/lib/Extension/LanguageServerIndexer/Status/IndexerStatusProvider.php deleted file mode 100644 index b3c24e5e9b..0000000000 --- a/lib/Extension/LanguageServerIndexer/Status/IndexerStatusProvider.php +++ /dev/null @@ -1,25 +0,0 @@ - $this->watcher->describe(), - ]; - } -} diff --git a/lib/Extension/LanguageServerIndexer/Tests/Extension/TestExtension.php b/lib/Extension/LanguageServerIndexer/Tests/Extension/TestExtension.php deleted file mode 100644 index 917edf338a..0000000000 --- a/lib/Extension/LanguageServerIndexer/Tests/Extension/TestExtension.php +++ /dev/null @@ -1,31 +0,0 @@ -register('test.watcher.will_die', function (Container $container) { - return new TestWatcher(new ModifiedFileQueue(), 0, new WatcherDied('No')); - }, [ - IndexerExtension::TAG_WATCHER => [ - 'name' => 'will_die', - ], - ]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerIndexer/Tests/IntegrationTestCase.php b/lib/Extension/LanguageServerIndexer/Tests/IntegrationTestCase.php deleted file mode 100644 index 3de689953b..0000000000 --- a/lib/Extension/LanguageServerIndexer/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,59 +0,0 @@ - __DIR__ . '/../', - FilePathResolverExtension::PARAM_PROJECT_ROOT => $this->workspace()->path(), - IndexerExtension::PARAM_INDEX_PATH => $this->workspace()->path('/cache'), - LoggingExtension::PARAM_ENABLED=> false, - LoggingExtension::PARAM_PATH=> 'php://stderr', - WorseReflectionExtension::PARAM_ENABLE_CACHE=> false, - IndexerExtension::PARAM_ENABLED_WATCHERS => [], - LanguageServerExtension::PARAM_ENABLE_TRUST_CHECK => false, - ], $config)); - - return $container; - } -} diff --git a/lib/Extension/LanguageServerIndexer/Tests/Unit/IndexerHandlerTest.php b/lib/Extension/LanguageServerIndexer/Tests/Unit/IndexerHandlerTest.php deleted file mode 100644 index 9870fb4ccf..0000000000 --- a/lib/Extension/LanguageServerIndexer/Tests/Unit/IndexerHandlerTest.php +++ /dev/null @@ -1,121 +0,0 @@ -container([ - LanguageServerExtension::PARAM_FILE_EVENTS => false, - ]); - $this->tester = $container->get(LanguageServerBuilder::class)->tester( - new InitializeParams( - rootUri: $this->workspace()->path(), - capabilities: new ClientCapabilities(window: new WindowClientCapabilities(workDoneProgress: true)) - ) - ); - } - - public function testIndexer(): void - { - $this->workspace()->put( - 'Foobar.php', - <<<'EOT' - tester->initialize(); - $response = $this->tester->transmitter()->shiftRequest(); - $this->tester->respond($response->id, null); - wait(delay(50)); - - self::assertGreaterThanOrEqual(2, $this->tester->transmitter()->count()); - $this->tester->transmitter()->shift(); - $done = $this->tester->transmitter()->shift(); - self::assertStringContainsString('Done indexing', $done->params['value']['message']); - } - - public function testReindexNonStarted(): void - { - $this->tester->initialize(); - - wait(delay(10)); - - self::assertContains('indexer', $this->tester->services()->listRunning()); - $this->tester->services()->stop('indexer'); - self::assertNotContains('indexer', $this->tester->services()->listRunning()); - - $this->tester->notifyAndWait('phpactor/indexer/reindex', []); - - self::assertContains('indexer', $this->tester->services()->listRunning()); - } - - public function testReindexHard(): void - { - $this->tester->notifyAndWait('phpactor/indexer/reindex', [ - 'soft' => false, - ]); - - self::assertContains('indexer', $this->tester->services()->listRunning()); - } - - public function testOptimiseCommand(): void - { - $this->tester->notifyAndWait('phpactor/indexer/optimise', [ - ]); - - // shift off progress create - $this->tester->transmitter()->shift(); - - // test we started optimising the index - $message = $this->tester->transmitter()->shift(); - self::assertInstanceOf(NotificationMessage::class, $message); - self::assertIsArray($message->params); - self::assertIsArray($message->params['value']); - self::assertEquals('optimising index', $message->params['value']['title'] ?? null); - } - - public function testShowsMessageOnWatcherDied(): void - { - $this->workspace()->put( - 'Foobar.php', - <<<'EOT' - container([ - 'indexer.enabled_watchers' => ['will_die'], - ])->get(LanguageServerBuilder::class)->tester( - new InitializeParams( - rootUri: $this->workspace()->path(), - capabilities: new ClientCapabilities(window: new WindowClientCapabilities(workDoneProgress: true)) - ) - ); - - $tester->initialize(); - $response = $tester->transmitter()->shiftRequest(); - $tester->respondAndWait($response->id, null); - wait(delay(10)); - - $tester->transmitter()->shift(); - $tester->transmitter()->shift(); - $message = $tester->transmitter()->shift(); - self::assertStringContainsString('File watcher died:', $message->params['message']); - } -} diff --git a/lib/Extension/LanguageServerIndexer/Tests/Unit/Model/WorkspaceSymbolProviderTest.php b/lib/Extension/LanguageServerIndexer/Tests/Unit/Model/WorkspaceSymbolProviderTest.php deleted file mode 100644 index 1476838b46..0000000000 --- a/lib/Extension/LanguageServerIndexer/Tests/Unit/Model/WorkspaceSymbolProviderTest.php +++ /dev/null @@ -1,126 +0,0 @@ -workspace()->reset(); - } - - #[DataProvider('provideProvide')] - public function testProvide(array $workspace, Closure $assertion, string $query, int $limit = 250): void - { - $container = $this->container([ - LanguageServerIndexerExtension::WORKSPACE_SYMBOL_SEARCH_LIMIT => $limit, - ]); - foreach ($workspace as $path => $contents) { - $this->workspace()->put($path, $contents); - } - - $indexer = $container->get(Indexer::class); - assert($indexer instanceof Indexer); - $indexer->getJob()->run(); - $client = $container->get(SearchClient::class); - $locator = $container->get(TextDocumentLocator::class); - - $provider = new WorkspaceSymbolProvider($client, $locator, $limit); - $informations = wait($provider->provideFor($query)); - $assertion($informations); - } - - /** - * @return Generator - */ - public static function provideProvide(): Generator - { - yield 'No matches' => [ - [ - 'Foo.php' => ' [ - [ - 'Foo1.php' => 'name); - self::assertEquals(SymbolKind::CLASS_, $info->kind); - }, - 'F' - ]; - - yield 'Methods not currently supported' => [ - [ - 'Foo.php' => ' [ - [ - 'Foo.php' => 'name); - self::assertEquals(SymbolKind::FUNCTION, $info->kind); - }, - 'bar' - ]; - - yield 'Constants' => [ - [ - 'Foo4.php' => 'name); - self::assertEquals(SymbolKind::CONSTANT, $info->kind); - }, - 'Foo' - ]; - - yield 'Applies a limit' => [ - [ - 'Foo5.php' => ' ' 'isSupported())); - } - - public function testNotSupported(): void - { - $capabiltiies = ClientCapabilities::fromArray([ - 'workspace' => [ - 'didChangeWatchedFiles' => null - ] - ]); - $watcher = new LanguageServerWatcher($capabiltiies); - - self::assertFalse(wait($watcher->isSupported())); - } - - public function testWatch(): void - { - $watcher = new LanguageServerWatcher(new ClientCapabilities()); - $server = LanguageServerTesterBuilder::create() - ->addListenerProvider($watcher) - ->enableFileEvents() - ->build(); - - $server->notify('workspace/didChangeWatchedFiles', new DidChangeWatchedFilesParams([ - new FileEvent('file:///foobar', FileChangeType::CREATED) - ])); - - $event = wait($watcher->wait()); - self::assertInstanceOf(ModifiedFile::class, $event); - } - - public function testWatchMultipleFilesChanged(): void - { - $watcher = new LanguageServerWatcher(new ClientCapabilities()); - $server = LanguageServerTesterBuilder::create() - ->addListenerProvider($watcher) - ->enableFileEvents() - ->build(); - - $server->notify('workspace/didChangeWatchedFiles', new DidChangeWatchedFilesParams([ - new FileEvent('file:///foobar1', FileChangeType::CREATED), - new FileEvent('file:///foobar2', FileChangeType::CREATED), - new FileEvent('file:///foobar3', FileChangeType::CREATED), - ])); - - $event = wait($watcher->wait()); - self::assertInstanceOf(ModifiedFile::class, $event); - $event = wait($watcher->wait()); - self::assertInstanceOf(ModifiedFile::class, $event); - $event = wait($watcher->wait()); - self::assertInstanceOf(ModifiedFile::class, $event); - } -} diff --git a/lib/Extension/LanguageServerIndexer/Watcher/LanguageServerWatcher.php b/lib/Extension/LanguageServerIndexer/Watcher/LanguageServerWatcher.php deleted file mode 100644 index 6bb791cfb8..0000000000 --- a/lib/Extension/LanguageServerIndexer/Watcher/LanguageServerWatcher.php +++ /dev/null @@ -1,113 +0,0 @@ - - */ - private Deferred $deferred; - - /** - * @var FileEvent[] - */ - private array $queue = []; - - private bool $running = false; - - public function __construct(private ?ClientCapabilities $clientCapabilities) - { - $this->deferred = new Deferred(); - } - - - public function watch(): Promise - { - return new Success($this); - } - - - public function isSupported(): Promise - { - if (!$this->clientCapabilities) { - return new Success(false); - } - - return new Success( - (bool)($this->clientCapabilities?->workspace?->didChangeWatchedFiles ?? false) - ); - } - - - public function describe(): string - { - return 'LSP file events'; - } - - - public function getListenersForEvent(object $event): iterable - { - if ($event instanceof FilesChanged) { - return [$this->enqueue(...)]; - } - - return []; - } - - public function enqueue(FilesChanged $filesChanged): void - { - foreach ($filesChanged->events() as $changedFile) { - $this->queue[] = $changedFile; - } - - if (!$this->running) { - $this->running = true; - $this->deferred->resolve(); - } - } - - public function stop(): void - { - } - - - public function wait(): Promise - { - return call(function () { - while (true) { - yield $this->deferred->promise(); - $this->running = false; - $this->deferred = new Deferred(); - $event = array_shift($this->queue); - if ($event === null) { - continue; - } - break; - } - - assert($event instanceof FileEvent); - - if ($this->queue) { - $this->deferred->resolve(); - } - - return ModifiedFileBuilder::fromPath( - TextDocumentUri::fromString($event->uri)->path(), - )->asFile()->build(); - }); - } -} diff --git a/lib/Extension/LanguageServerInlineValue/Handler/InlineValueHandler.php b/lib/Extension/LanguageServerInlineValue/Handler/InlineValueHandler.php deleted file mode 100644 index 47e73d405b..0000000000 --- a/lib/Extension/LanguageServerInlineValue/Handler/InlineValueHandler.php +++ /dev/null @@ -1,128 +0,0 @@ - 'inlineValue', - ]; - } - - /** - * @return Promise - */ - public function inlineValue( - TextDocumentIdentifier $textDocument, - Range $range - ): Promise { - $document = $this->workspace->get($textDocument->uri); - $document = TextDocumentBuilder::create($document->text) - ->uri($document->uri) - ->language('php') - ->build(); - - $start = PositionConverter::positionToByteOffset($range->start, $document)->toInt(); - $end = PositionConverter::positionToByteOffset($range->end, $document)->toInt(); - - $root = $this->parser->get($document); - - $i = $root->getDescendantNodes(fn ($child) => $child->getStartPosition() <= $end && $child->getEndPosition() >= $start); - $i = new CallbackFilterIterator($i, fn ($node) => - ($node instanceof Variable || - $node instanceof Parameter)); - $ret = array_map( - function ($node) { - /** @var Node $node */ - $ev = $this->nodeToEvaluatable($node); - if ($ev === null) { - return null; - } - return new InlineValueVariableLookup( - range: $ev['range'], - caseSensitiveLookup: true, - variableName: $ev['expression'] - ); - }, - \iterator_to_array($i, false) - ); - $ret = array_filter($ret); - return new Success($ret); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->inlineValueProvider = true; - } - - /** - * @return array{expression:string,range:Range}|null - */ - private function nodeToEvaluatable(Node $node): ?array - { - if ($node instanceof Parameter) { - return $this->nodeToExpressionRange($node->variableName, $node); - } - if ( - $node instanceof Variable || - $node instanceof SubscriptExpression || - $node instanceof MemberAccessExpression - ) { - return $this->nodeToExpressionRange($node, $node); - } - if ($node2 = $node->getFirstAncestor(SubscriptExpression::class)) { - return $this->nodeToExpressionRange($node2, $node2); - } - return null; - } - - /** - * @return array{expression:string,range:Range} - */ - private function nodeToExpressionRange(Node|Token $token, Node $textNode): array - { - return [ - 'expression' => (string)$token->getText($textNode->getFileContents()), - 'range' => $this->byteOffsetRangeForNode($token, $textNode), - ]; - } - - /** - * Converts Microsoft PhpParser Node to LSP Range. - */ - private function byteOffsetRangeForNode(Node|Token $token, Node $textNode): Range - { - return new Range( - PositionConverter::intByteOffsetToPosition($token->getStartPosition(), $textNode->getFileContents()), - PositionConverter::intByteOffsetToPosition($token->getEndPosition(), $textNode->getFileContents()), - ); - } -} diff --git a/lib/Extension/LanguageServerInlineValue/LanguageServerInlineValueExtension.php b/lib/Extension/LanguageServerInlineValue/LanguageServerInlineValueExtension.php deleted file mode 100644 index d2f9a01043..0000000000 --- a/lib/Extension/LanguageServerInlineValue/LanguageServerInlineValueExtension.php +++ /dev/null @@ -1,30 +0,0 @@ -register('language_server_inline_value.handler', function (Container $container) { - return new InlineValueHandler( - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->expect(WorseReflectionExtension::SERVICE_AST_PROVIDER, AstProvider::class), - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => []]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerInlineValue/Tests/InlineValueHandlerTest.php b/lib/Extension/LanguageServerInlineValue/Tests/InlineValueHandlerTest.php deleted file mode 100644 index 0fc158b9f0..0000000000 --- a/lib/Extension/LanguageServerInlineValue/Tests/InlineValueHandlerTest.php +++ /dev/null @@ -1,83 +0,0 @@ -createTester(); - $tester->textDocument()->open(self::PATH, $text); - - $response = $tester->requestAndWait('textDocument/inlineValue', [ - 'textDocument' => new TextDocumentIdentifier(self::PATH), - 'range' => RangeConverter::toLspRange(ByteOffsetRange::fromInts(0, mb_strlen($text)), $text), - ]); - self::assertNotNull($response); - $tester->assertSuccess($response); - $result = $response->result; - $this->assertIsArray($response->result); - $this->assertCount(count($ranges), $response->result); - foreach ($response->result as $result) { - $this->assertNotEmpty($ranges); - [$test_range, $test_text] = array_shift($ranges); - $this->assertInstanceOf(InlineValueVariableLookup::class, $result); - $this->assertEquals(true, $result->caseSensitiveLookup); - $this->assertEquals($test_range, $result->range); - $this->assertEquals($test_text, $result->variableName); - } - } - - public static function provideInlineValue(): Generator - { - yield 'var' => [ - <<<'EOF' - $param1<>, <>$param2<>) { - <>$param1<>; - } - EOF, - ]; - yield 'foreach' => [ - <<<'EOF' - $array<> as <>$key<> => <>$val<>) { - <>$param1<>; - } - EOF, - ]; - } - - protected function createTester(): LanguageServerTester - { - $tester = LanguageServerTesterBuilder::create(); - $tester->addHandler(new InlineValueHandler($tester->workspace(), new TolerantAstProvider())); - return $tester->build(); - } -} diff --git a/lib/Extension/LanguageServerMago/LanguageServerMagoExtension.php b/lib/Extension/LanguageServerMago/LanguageServerMagoExtension.php deleted file mode 100644 index a4e610b603..0000000000 --- a/lib/Extension/LanguageServerMago/LanguageServerMagoExtension.php +++ /dev/null @@ -1,114 +0,0 @@ -register(MagoProcess::class, function (Container $container) { - $pathResolver = $container->expect( - FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, - PathResolver::class - ); - - $bin = $pathResolver->resolve($container->parameter(self::PARAM_BIN)->string()); - $root = $pathResolver->resolve('%project_root%'); - - $config = null; - if ($container->parameter(self::PARAM_CONFIG)->value()) { - $config = $pathResolver->resolve($container->parameter(self::PARAM_CONFIG)->string()); - } - - return new MagoProcess( - $root, - new MagoConfig($bin, $container->parameter(self::PARAM_TIMEOUT)->int(), $config), - LoggingExtension::channelLogger($container, 'mago'), - ); - }); - - $this->registerProvider( - $container, - self::SERVICE_ANALYZE_PROVIDER, - 'analyze', - 'mago', - self::PARAM_ANALYZE_ENABLED, - ); - $this->registerProvider( - $container, - self::SERVICE_LINT_PROVIDER, - 'lint', - 'mago-lint', - self::PARAM_LINT_ENABLED, - ); - } - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_BIN => '%project_root%/vendor/bin/mago', - self::PARAM_CONFIG => null, - self::PARAM_TIMEOUT => 10000, - self::PARAM_ANALYZE_ENABLED => true, - self::PARAM_LINT_ENABLED => true, - ]); - $schema->setDescriptions([ - self::PARAM_BIN => 'Path to the Mago executable', - self::PARAM_CONFIG => 'Override the Mago configuration file (mago.toml)', - self::PARAM_TIMEOUT => 'Maximum time in milliseconds to wait for a Mago run', - self::PARAM_ANALYZE_ENABLED => 'Show diagnostics from `mago analyze` (static analysis)', - self::PARAM_LINT_ENABLED => 'Show diagnostics from `mago lint` (style and code smells)', - ]); - } - - public function name(): string - { - return 'language_server_mago'; - } - - private function registerProvider( - ContainerBuilder $container, - string $serviceId, - string $subcommand, - string $source, - string $enabledParam, - ): void { - $container->register($serviceId, function (Container $container) use ($subcommand, $source, $enabledParam) { - $root = $container->expect( - FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, - PathResolver::class - )->resolve('%project_root%'); - - return new MagoDiagnosticProvider( - new MagoLinter($container->get(MagoProcess::class), $root, $subcommand, $source), - $source, - $container->parameter($enabledParam)->bool(), - ); - }, [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create($source), - ]); - } -} diff --git a/lib/Extension/LanguageServerMago/LanguageServerMagoSuggestExtension.php b/lib/Extension/LanguageServerMago/LanguageServerMagoSuggestExtension.php deleted file mode 100644 index 301986b41a..0000000000 --- a/lib/Extension/LanguageServerMago/LanguageServerMagoSuggestExtension.php +++ /dev/null @@ -1,50 +0,0 @@ -register('language_server_mago.suggest', function (Container $container) { - return new PhpactorComposerSuggestor( - $container->expect(ConfigurationExtension::SERVICE_PHPACTOR_CONFIG_LOCAL, JsonConfig::class), - $container->get(ComposerInspector::class), - function (JsonConfig $config, ComposerInspector $inspector) { - if ($config->has(LanguageServerMagoExtension::PARAM_ENABLED)) { - return Changes::none(); - } - - if (!$inspector->package('carthage-software/mago')) { - return Changes::none(); - } - - return Changes::from([ - new PhpactorConfigChange('Mago detected, enable Mago extension?', function (bool $enable) { - return [ - LanguageServerMagoExtension::PARAM_ENABLED => $enable, - ]; - }) - ]); - } - ); - }, [ - ConfigurationExtension::TAG_SUGGESTOR => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerMago/Model/DiagnosticsParser.php b/lib/Extension/LanguageServerMago/Model/DiagnosticsParser.php deleted file mode 100644 index 206b0e14a9..0000000000 --- a/lib/Extension/LanguageServerMago/Model/DiagnosticsParser.php +++ /dev/null @@ -1,221 +0,0 @@ - - */ - public function parse( - string $jsonString, - string $documentText, - string $source, - string $relativePath, - string $documentUri - ): array { - $decoded = $this->decodeJson($jsonString); - $issues = is_array($decoded['issues'] ?? null) ? $decoded['issues'] : []; - - $diagnostics = []; - foreach ($issues as $issue) { - if (!is_array($issue)) { - continue; - } - $diagnostic = $this->parseIssue($issue, $documentText, $source, $relativePath, $documentUri); - if (null !== $diagnostic) { - $diagnostics[] = $diagnostic; - } - } - - return $diagnostics; - } - - /** - * @param array $issue - */ - private function parseIssue( - array $issue, - string $documentText, - string $source, - string $relativePath, - string $documentUri - ): ?Diagnostic { - $annotations = is_array($issue['annotations'] ?? null) ? $issue['annotations'] : []; - - $primary = $this->primaryAnnotation($annotations); - if (null === $primary) { - return null; - } - - // Drop issues whose primary location is in another file: the provider - // publishes diagnostics for the current document only. - if ($this->annotationName($primary) !== $relativePath) { - return null; - } - - return new Diagnostic( - range: $this->annotationRange($primary, $documentText), - message: $this->composeMessage($issue), - severity: $this->severity(is_string($issue['level'] ?? null) ? $issue['level'] : ''), - code: is_string($issue['code'] ?? null) ? $issue['code'] : null, - source: $source, - relatedInformation: $this->relatedInformation($annotations, $documentText, $relativePath, $documentUri), - ); - } - - /** - * @param array $annotations - * @return array|null - */ - private function primaryAnnotation(array $annotations): ?array - { - foreach ($annotations as $annotation) { - if (is_array($annotation) && ($annotation['kind'] ?? null) === 'Primary') { - return $annotation; - } - } - - $first = $annotations[0] ?? null; - - return is_array($first) ? $first : null; - } - - /** - * Secondary annotations that point inside the current document become - * related information. Cross-file secondaries are skipped: their byte - * offsets index a file whose text is not available here. - * - * @param array $annotations - * @return array|null - */ - private function relatedInformation( - array $annotations, - string $documentText, - string $relativePath, - string $documentUri - ): ?array { - $related = []; - foreach ($annotations as $annotation) { - if (!is_array($annotation) || ($annotation['kind'] ?? null) !== 'Secondary') { - continue; - } - if ($this->annotationName($annotation) !== $relativePath) { - continue; - } - $message = is_string($annotation['message'] ?? null) ? $annotation['message'] : 'related'; - $related[] = new DiagnosticRelatedInformation( - new Location($documentUri, $this->annotationRange($annotation, $documentText)), - $message, - ); - } - - return $related === [] ? null : $related; - } - - /** - * @param array $annotation - */ - private function annotationRange(array $annotation, string $documentText): Range - { - $span = is_array($annotation['span'] ?? null) ? $annotation['span'] : []; - $start = $this->offset($span['start'] ?? null, 0); - $end = $this->offset($span['end'] ?? null, $start); - - return new Range( - PositionConverter::intByteOffsetToPosition($start, $documentText), - PositionConverter::intByteOffsetToPosition($end, $documentText), - ); - } - - private function offset(mixed $position, int $default): int - { - if (is_array($position) && is_int($position['offset'] ?? null)) { - return $position['offset']; - } - - return $default; - } - - /** - * @param array $annotation - */ - private function annotationName(array $annotation): ?string - { - $span = is_array($annotation['span'] ?? null) ? $annotation['span'] : []; - $fileId = is_array($span['file_id'] ?? null) ? $span['file_id'] : []; - $name = $fileId['name'] ?? null; - - return is_string($name) ? $name : null; - } - - /** - * Mago levels have no LSP columns, so notes and help (which carry no - * location) are folded into the message rather than dropped. - * - * @param array $issue - */ - private function composeMessage(array $issue): string - { - $message = is_string($issue['message'] ?? null) ? $issue['message'] : ''; - - foreach (is_array($issue['notes'] ?? null) ? $issue['notes'] : [] as $note) { - if (is_string($note) && $note !== '') { - $message .= "\n" . $note; - } - } - - if (is_string($issue['help'] ?? null) && $issue['help'] !== '') { - $message .= "\n\n" . $issue['help']; - } - - return $message; - } - - /** - * @return DiagnosticSeverity::* - */ - private function severity(string $level): int - { - return match (strtolower($level)) { - 'warning' => DiagnosticSeverity::WARNING, - 'note' => DiagnosticSeverity::INFORMATION, - 'help' => DiagnosticSeverity::HINT, - default => DiagnosticSeverity::ERROR, - }; - } - - /** - * @return array - */ - private function decodeJson(string $jsonString): array - { - $decoded = json_decode($jsonString, true); - - if (!is_array($decoded)) { - throw new RuntimeException(sprintf( - 'Could not decode expected Mago JSON string "%s"', - $jsonString - )); - } - - return $decoded; - } -} diff --git a/lib/Extension/LanguageServerMago/Model/Linter.php b/lib/Extension/LanguageServerMago/Model/Linter.php deleted file mode 100644 index f57f81073b..0000000000 --- a/lib/Extension/LanguageServerMago/Model/Linter.php +++ /dev/null @@ -1,15 +0,0 @@ -> - */ - public function lint(string $url, string $text, CancellationToken $cancel): Promise; -} diff --git a/lib/Extension/LanguageServerMago/Model/Linter/MagoLinter.php b/lib/Extension/LanguageServerMago/Model/Linter/MagoLinter.php deleted file mode 100644 index cd57801e5f..0000000000 --- a/lib/Extension/LanguageServerMago/Model/Linter/MagoLinter.php +++ /dev/null @@ -1,61 +0,0 @@ -path(); - - // Skip anything not contained in the project root (the root itself - // yields an empty relative path). - if (!Path::isBasePath($this->projectRoot, $path)) { - return []; - } - - $relativePath = Path::makeRelative($path, $this->projectRoot); - - if ($relativePath === '') { - return []; - } - - return yield $this->process->analyse( - $this->subcommand, - $this->source, - $relativePath, - $url, - $text, - $cancel, - ); - }); - } -} diff --git a/lib/Extension/LanguageServerMago/Model/Linter/TestLinter.php b/lib/Extension/LanguageServerMago/Model/Linter/TestLinter.php deleted file mode 100644 index 027915625f..0000000000 --- a/lib/Extension/LanguageServerMago/Model/Linter/TestLinter.php +++ /dev/null @@ -1,24 +0,0 @@ - $diagnostics - */ - public function __construct(private array $diagnostics = []) - { - } - - public function lint(string $url, string $text, CancellationToken $cancel): Promise - { - return new Success($this->diagnostics); - } -} diff --git a/lib/Extension/LanguageServerMago/Model/MagoConfig.php b/lib/Extension/LanguageServerMago/Model/MagoConfig.php deleted file mode 100644 index 40ee44deee..0000000000 --- a/lib/Extension/LanguageServerMago/Model/MagoConfig.php +++ /dev/null @@ -1,31 +0,0 @@ -bin; - } - - /** - * Maximum time in milliseconds to wait for a Mago run before giving up. - */ - public function timeout(): int - { - return $this->timeout; - } - - public function config(): ?string - { - return $this->config; - } -} diff --git a/lib/Extension/LanguageServerMago/Model/MagoProcess.php b/lib/Extension/LanguageServerMago/Model/MagoProcess.php deleted file mode 100644 index ad49799625..0000000000 --- a/lib/Extension/LanguageServerMago/Model/MagoProcess.php +++ /dev/null @@ -1,154 +0,0 @@ - --reporting-format=json --stdin-input ` - * with the document text piped on stdin, and parses the result. - * - * Mago is a native (Rust) binary, so the command is run directly without a PHP - * interpreter prefix. Global options such as --config must precede the - * subcommand. - */ -class MagoProcess -{ - public function __construct( - private string $cwd, - private MagoConfig $config, - private LoggerInterface $logger, - private DiagnosticsParser $parser = new DiagnosticsParser(), - ) { - } - - /** - * @return Promise> - */ - public function analyse( - string $subcommand, - string $source, - string $relativePath, - string $documentUri, - string $documentText, - CancellationToken $cancel, - ): Promise { - return call(function () use ($subcommand, $source, $relativePath, $documentUri, $documentText, $cancel) { - $process = ProcessBuilder::create($this->buildArgs($subcommand, $relativePath)) - ->cwd($this->cwd) - ->mergeParentEnv() - ->build(); - - $start = microtime(true); - yield $process->start(); - - // Honour client cancellation by killing the process. - $cancelId = $cancel->subscribe(function () use ($process): void { - if ($process->isRunning()) { - $process->kill(); - } - }); - - try { - // Buffer both streams before writing stdin and joining, so the - // child can drain its stdout while we feed it (avoiding a pipe - // deadlock) and large output cannot stall the process. - $stdoutPromise = buffer($process->getStdout()); - $stderrPromise = buffer($process->getStderr()); - - try { - $stdin = $process->getStdin(); - yield $stdin->write($documentText); - yield $stdin->end(); - - /** @var int $exitCode */ - $exitCode = yield timeout($process->join(), $this->config->timeout()); - } catch (TimeoutException) { - if ($process->isRunning()) { - $process->kill(); - } - $this->logger->error(sprintf( - 'Mago timed out after %dms: %s', - $this->config->timeout(), - $process->getCommand(), - )); - - return []; - } catch (ProcessException | StreamException) { - // join() rejects, or stdin closes, when the process is - // killed, which happens when the client cancels the request. - $this->logger->debug(sprintf( - 'Mago run cancelled: %s', - $process->getCommand(), - )); - - return []; - } - - $stdout = yield $stdoutPromise; - - // A clean file produces empty output (issues are reported via - // JSON only when present), so empty stdout is not an error. - if (!is_string($stdout) || trim($stdout) === '') { - $this->logger->debug(sprintf( - 'Mago produced no diagnostics in %ss (exit %s): %s', - number_format(microtime(true) - $start, 4), - $exitCode, - $process->getCommand(), - )); - - return []; - } - - try { - return $this->parser->parse($stdout, $documentText, $source, $relativePath, $documentUri); - } catch (Throwable $error) { - $stderr = yield $stderrPromise; - $this->logger->error(sprintf( - 'Mago output could not be parsed (exit %s): %s; error: %s; stderr: %s', - $exitCode, - $process->getCommand(), - $error->getMessage(), - is_string($stderr) ? trim($stderr) : '', - )); - - return []; - } - } finally { - $cancel->unsubscribe($cancelId); - } - }); - } - - /** - * @return list - */ - private function buildArgs(string $subcommand, string $relativePath): array - { - $args = [$this->config->bin()]; - - // Global options precede the subcommand. - if (null !== $this->config->config()) { - $args[] = '--config'; - $args[] = $this->config->config(); - } - - $args[] = $subcommand; - $args[] = '--reporting-format=json'; - $args[] = '--stdin-input'; - $args[] = $relativePath; - - return $args; - } -} diff --git a/lib/Extension/LanguageServerMago/Provider/MagoDiagnosticProvider.php b/lib/Extension/LanguageServerMago/Provider/MagoDiagnosticProvider.php deleted file mode 100644 index f42c659f95..0000000000 --- a/lib/Extension/LanguageServerMago/Provider/MagoDiagnosticProvider.php +++ /dev/null @@ -1,39 +0,0 @@ - "mago", lint -> "mago-lint"); each can be toggled - * independently. - */ -class MagoDiagnosticProvider implements DiagnosticsProvider -{ - public function __construct( - private Linter $linter, - private string $name, - private bool $enabled = true, - ) { - } - - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - if (!$this->enabled) { - return new Success([]); - } - - return $this->linter->lint($textDocument->uri, $textDocument->text, $cancel); - } - - public function name(): string - { - return $this->name; - } -} diff --git a/lib/Extension/LanguageServerMago/Tests/Fixtures/analyze.json b/lib/Extension/LanguageServerMago/Tests/Fixtures/analyze.json deleted file mode 100644 index f6692e09f9..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/Fixtures/analyze.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "issues": [ - { - "level": "Error", - "code": "invalid-argument", - "message": "Invalid argument type for argument #1 of `add`: expected `int`, but found `string('x')`.", - "notes": [ - "The provided type `string('x')` is not compatible with the expected type `int`." - ], - "help": "Change the argument value to match `int`, or update the parameter's type declaration.", - "annotations": [ - { - "message": "This has type `string('x')`", - "kind": "Primary", - "span": { - "file_id": { - "name": "src/Example.php", - "path": "/private/var/folders/p6/c8nqdy2s1vg1ps052xyq7zdw0000gn/T/tmp.egyt9o1cqA/src/Example.php", - "size": 81, - "file_type": "Host" - }, - "start": { - "offset": 70, - "line": 7 - }, - "end": { - "offset": 73, - "line": 7 - } - } - }, - { - "message": "Arguments to this function are incorrect", - "kind": "Secondary", - "span": { - "file_id": { - "name": "src/Example.php", - "path": "/private/var/folders/p6/c8nqdy2s1vg1ps052xyq7zdw0000gn/T/tmp.egyt9o1cqA/src/Example.php", - "size": 81, - "file_type": "Host" - }, - "start": { - "offset": 66, - "line": 7 - }, - "end": { - "offset": 69, - "line": 7 - } - } - } - ] - }, - { - "level": "Error", - "code": "invalid-argument", - "message": "Invalid argument type for argument #2 of `add`: expected `int`, but found `string('y')`.", - "notes": [ - "The provided type `string('y')` is not compatible with the expected type `int`." - ], - "help": "Change the argument value to match `int`, or update the parameter's type declaration.", - "annotations": [ - { - "message": "This has type `string('y')`", - "kind": "Primary", - "span": { - "file_id": { - "name": "src/Example.php", - "path": "/private/var/folders/p6/c8nqdy2s1vg1ps052xyq7zdw0000gn/T/tmp.egyt9o1cqA/src/Example.php", - "size": 81, - "file_type": "Host" - }, - "start": { - "offset": 75, - "line": 7 - }, - "end": { - "offset": 78, - "line": 7 - } - } - }, - { - "message": "Arguments to this function are incorrect", - "kind": "Secondary", - "span": { - "file_id": { - "name": "src/Example.php", - "path": "/private/var/folders/p6/c8nqdy2s1vg1ps052xyq7zdw0000gn/T/tmp.egyt9o1cqA/src/Example.php", - "size": 81, - "file_type": "Host" - }, - "start": { - "offset": 66, - "line": 7 - }, - "end": { - "offset": 69, - "line": 7 - } - } - } - ] - } - ] -} \ No newline at end of file diff --git a/lib/Extension/LanguageServerMago/Tests/Fixtures/lint.json b/lib/Extension/LanguageServerMago/Tests/Fixtures/lint.json deleted file mode 100644 index 0c7711209b..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/Fixtures/lint.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "issues": [ - { - "level": "Warning", - "code": "strict-types", - "message": "Missing `declare(strict_types=1);` statement at the beginning of the file.", - "notes": [ - "The `strict_types` directive enforces strict type checking, which can prevent subtle bugs." - ], - "help": "Add `declare(strict_types=1);` at the top of your file.", - "annotations": [ - { - "kind": "Primary", - "span": { - "file_id": { - "name": "src/Example.php", - "path": "/private/var/folders/p6/c8nqdy2s1vg1ps052xyq7zdw0000gn/T/tmp.egyt9o1cqA/src/Example.php", - "size": 81, - "file_type": "Host" - }, - "start": { - "offset": 0, - "line": 0 - }, - "end": { - "offset": 5, - "line": 0 - } - } - } - ], - "edits": [ - [ - { - "name": "src/Example.php", - "path": "/private/var/folders/p6/c8nqdy2s1vg1ps052xyq7zdw0000gn/T/tmp.egyt9o1cqA/src/Example.php", - "size": 81, - "file_type": "Host" - }, - [ - { - "range": { - "start": 5, - "end": 5 - }, - "new_text": [ - 10, - 10, - 100, - 101, - 99, - 108, - 97, - 114, - 101, - 40, - 115, - 116, - 114, - 105, - 99, - 116, - 95, - 116, - 121, - 112, - 101, - 115, - 61, - 49, - 41, - 59, - 10 - ], - "safety": "potentiallyunsafe" - } - ] - ] - ] - }, - { - "level": "Warning", - "code": "literal-named-argument", - "message": "Literal argument `'y'` should be passed as a named argument for clarity.", - "notes": [ - "Passing literals positionally can make code less clear, especially with booleans, numbers, or `null`." - ], - "help": "Consider using a named argument instead: `function_name(param: 'y')`.", - "annotations": [ - { - "message": "This literal is being passed positionally.", - "kind": "Primary", - "span": { - "file_id": { - "name": "src/Example.php", - "path": "/private/var/folders/p6/c8nqdy2s1vg1ps052xyq7zdw0000gn/T/tmp.egyt9o1cqA/src/Example.php", - "size": 81, - "file_type": "Host" - }, - "start": { - "offset": 75, - "line": 7 - }, - "end": { - "offset": 78, - "line": 7 - } - } - } - ] - } - ] -} \ No newline at end of file diff --git a/lib/Extension/LanguageServerMago/Tests/IntegrationTestCase.php b/lib/Extension/LanguageServerMago/Tests/IntegrationTestCase.php deleted file mode 100644 index 28cea53356..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,14 +0,0 @@ -getContainer(); - - $names = []; - foreach (array_keys($container->getServiceIdsForTag(LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER)) as $id) { - $provider = $container->get($id); - self::assertInstanceOf(MagoDiagnosticProvider::class, $provider); - $names[] = $provider->name(); - } - - self::assertContains('mago', $names); - self::assertContains('mago-lint', $names); - } - - /** - * @param array $params - */ - private function getContainer(array $params = []): Container - { - return PhpactorContainer::fromExtensions( - [ - FilePathResolverExtension::class, - LoggingExtension::class, - LanguageServerMagoExtension::class, - ], - $params, - ); - } -} diff --git a/lib/Extension/LanguageServerMago/Tests/LanguageServerMagoSuggestExtensionTest.php b/lib/Extension/LanguageServerMago/Tests/LanguageServerMagoSuggestExtensionTest.php deleted file mode 100644 index 9f6dd455e5..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/LanguageServerMagoSuggestExtensionTest.php +++ /dev/null @@ -1,21 +0,0 @@ -getServiceIdsForTag(ConfigurationExtension::TAG_SUGGESTOR), - ); - } -} diff --git a/lib/Extension/LanguageServerMago/Tests/Model/DiagnosticsParserTest.php b/lib/Extension/LanguageServerMago/Tests/Model/DiagnosticsParserTest.php deleted file mode 100644 index 718e262b8e..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/Model/DiagnosticsParserTest.php +++ /dev/null @@ -1,180 +0,0 @@ - [ - [ - 'level' => 'Error', - 'code' => 'undefined-function', - 'message' => 'Function foo not found', - 'notes' => ['It is not defined anywhere'], - 'help' => 'Did you mean bar?', - 'annotations' => [ - [ - 'message' => 'called here', - 'kind' => 'Primary', - 'span' => $this->span(self::REL, 11, 16), - ], - [ - 'message' => 'assigned here', - 'kind' => 'Secondary', - 'span' => $this->span(self::REL, 6, 8), - ], - ], - ], - ], - ], JSON_THROW_ON_ERROR); - - $diagnostics = (new DiagnosticsParser())->parse($json, $text, 'mago', self::REL, self::URI); - - self::assertCount(1, $diagnostics); - $diagnostic = $diagnostics[0]; - self::assertEquals(new Range(new Position(1, 5), new Position(1, 10)), $diagnostic->range); - self::assertSame(DiagnosticSeverity::ERROR, $diagnostic->severity); - self::assertSame('undefined-function', $diagnostic->code); - self::assertSame('mago', $diagnostic->source); - self::assertStringContainsString('Function foo not found', $diagnostic->message); - self::assertStringContainsString('It is not defined anywhere', $diagnostic->message); - self::assertStringContainsString('Did you mean bar?', $diagnostic->message); - - self::assertNotNull($diagnostic->relatedInformation); - self::assertCount(1, $diagnostic->relatedInformation); - $related = $diagnostic->relatedInformation[0]; - self::assertSame('assigned here', $related->message); - self::assertSame(self::URI, $related->location->uri); - self::assertEquals(new Range(new Position(1, 0), new Position(1, 2)), $related->location->range); - } - - public function testDropsIssueWhosePrimaryAnnotationIsInAnotherFile(): void - { - $json = json_encode([ - 'issues' => [ - [ - 'level' => 'Error', - 'code' => 'x', - 'message' => 'in another file', - 'annotations' => [ - ['kind' => 'Primary', 'span' => $this->span('other/B.php', 0, 1)], - ], - ], - ], - ], JSON_THROW_ON_ERROR); - - $diagnostics = (new DiagnosticsParser())->parse($json, " [ - [ - 'level' => 'Warning', - 'code' => 'x', - 'message' => 'm', - 'annotations' => [ - ['kind' => 'Primary', 'span' => $this->span(self::REL, 17, 18)], - ], - ], - ], - ], JSON_THROW_ON_ERROR); - - $diagnostics = (new DiagnosticsParser())->parse($json, $text, 'mago-lint', self::REL, self::URI); - - self::assertCount(1, $diagnostics); - self::assertSame(DiagnosticSeverity::WARNING, $diagnostics[0]->severity); - self::assertSame(1, $diagnostics[0]->range->start->line); - self::assertSame(10, $diagnostics[0]->range->start->character); - } - - public function testThrowsOnInvalidJson(): void - { - $this->expectException(RuntimeException::class); - (new DiagnosticsParser())->parse('not json', '', 'mago', self::REL, self::URI); - } - - public function testParsesRealAnalyzeFixture(): void - { - $diagnostics = (new DiagnosticsParser())->parse( - $this->fixture('analyze.json'), - self::EXAMPLE_SOURCE, - 'mago', - 'src/Example.php', - 'file:///src/Example.php', - ); - - self::assertCount(2, $diagnostics); - foreach ($diagnostics as $diagnostic) { - self::assertSame(DiagnosticSeverity::ERROR, $diagnostic->severity); - self::assertSame('invalid-argument', $diagnostic->code); - self::assertSame('mago', $diagnostic->source); - } - } - - public function testParsesRealLintFixture(): void - { - $diagnostics = (new DiagnosticsParser())->parse( - $this->fixture('lint.json'), - self::EXAMPLE_SOURCE, - 'mago-lint', - 'src/Example.php', - 'file:///src/Example.php', - ); - - self::assertCount(2, $diagnostics); - foreach ($diagnostics as $diagnostic) { - self::assertSame(DiagnosticSeverity::WARNING, $diagnostic->severity); - self::assertSame('mago-lint', $diagnostic->source); - } - } - - /** - * @return array{file_id: array{name: string}, start: array{offset: int, line: int}, end: array{offset: int, line: int}} - */ - private function span(string $name, int $start, int $end): array - { - return [ - 'file_id' => ['name' => $name], - 'start' => ['offset' => $start, 'line' => 0], - 'end' => ['offset' => $end, 'line' => 0], - ]; - } - - private function fixture(string $name): string - { - return (string)file_get_contents(__DIR__ . '/../Fixtures/' . $name); - } -} diff --git a/lib/Extension/LanguageServerMago/Tests/Model/Linter/MagoLinterTest.php b/lib/Extension/LanguageServerMago/Tests/Model/Linter/MagoLinterTest.php deleted file mode 100644 index 52f516d031..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/Model/Linter/MagoLinterTest.php +++ /dev/null @@ -1,44 +0,0 @@ -lint($url, ' - */ - public static function provideUncontainedDocuments(): iterable - { - yield 'non-file scheme' => ['untitled:Untitled-1']; - yield 'outside the root' => ['file:///home/other/file.php']; - yield 'sibling whose name prefixes the root' => ['file:///home/project2/file.php']; - yield 'the root itself' => ['file://' . self::ROOT]; - yield 'parent escape' => ['file:///home/project/../escape.php']; - } -} diff --git a/lib/Extension/LanguageServerMago/Tests/Model/MagoProcessTest.php b/lib/Extension/LanguageServerMago/Tests/Model/MagoProcessTest.php deleted file mode 100644 index b23e4d2bb6..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/Model/MagoProcessTest.php +++ /dev/null @@ -1,198 +0,0 @@ -workspace()->reset(); - $this->workspace()->mkdir('capture'); - } - - public function testRunsBinaryDirectlyAndParsesOutput(): void - { - $this->fakeMago($this->issuesJson()); - - $diagnostics = wait($this->process()->analyse( - 'analyze', - 'mago', - self::RELATIVE_PATH, - 'file:///workspace/src/A.php', - self::DOCUMENT_TEXT, - new NullCancellationToken(), - )); - - self::assertCount(1, $diagnostics); - self::assertSame('invalid-argument', $diagnostics[0]->code); - self::assertSame('mago', $diagnostics[0]->source); - - // The subcommand precedes its flags, the reporting format is JSON, and - // the workspace-relative path is passed to --stdin-input. - self::assertSame( - 'analyze --reporting-format=json --stdin-input src/A.php', - trim($this->workspace()->getContents('capture/args')), - ); - // The document buffer is piped on stdin. - self::assertSame(self::DOCUMENT_TEXT, $this->workspace()->getContents('capture/stdin')); - // The process runs in the project root. - self::assertSame( - realpath($this->workspace()->path()), - realpath(trim($this->workspace()->getContents('capture/cwd'))), - ); - } - - public function testPassesGlobalConfigBeforeSubcommand(): void - { - $this->fakeMago(''); - - wait($this->process('/etc/mago.toml')->analyse( - 'lint', - 'mago-lint', - self::RELATIVE_PATH, - 'file:///workspace/src/A.php', - self::DOCUMENT_TEXT, - new NullCancellationToken(), - )); - - self::assertSame( - '--config /etc/mago.toml lint --reporting-format=json --stdin-input src/A.php', - trim($this->workspace()->getContents('capture/args')), - ); - } - - public function testEmptyOutputYieldsNoDiagnostics(): void - { - $this->fakeMago(''); - - $diagnostics = wait($this->process()->analyse( - 'analyze', - 'mago', - self::RELATIVE_PATH, - 'file:///workspace/src/A.php', - self::DOCUMENT_TEXT, - new NullCancellationToken(), - )); - - self::assertSame([], $diagnostics); - } - - public function testInvalidOutputIsLoggedAndYieldsNoDiagnostics(): void - { - $this->fakeMago('this is not json'); - $logger = new TestLogger(); - - $diagnostics = wait($this->process(null, $logger)->analyse( - 'analyze', - 'mago', - self::RELATIVE_PATH, - 'file:///workspace/src/A.php', - self::DOCUMENT_TEXT, - new NullCancellationToken(), - )); - - self::assertSame([], $diagnostics); - self::assertTrue($logger->hasErrorRecords()); - } - - public function testTimeoutKillsProcessAndYieldsNoDiagnostics(): void - { - $this->fakeMago($this->issuesJson(), sleepSeconds: 5); - $logger = new TestLogger(); - - $diagnostics = wait($this->process(null, $logger, timeout: 200)->analyse( - 'analyze', - 'mago', - self::RELATIVE_PATH, - 'file:///workspace/src/A.php', - self::DOCUMENT_TEXT, - new NullCancellationToken(), - )); - - self::assertSame([], $diagnostics); - self::assertTrue($logger->hasErrorRecords()); - } - - public function testCancellationKillsProcessAndYieldsNoDiagnostics(): void - { - $this->fakeMago($this->issuesJson(), sleepSeconds: 5); - $source = new CancellationTokenSource(); - - $promise = $this->process(null, null, timeout: 10000)->analyse( - 'analyze', - 'mago', - self::RELATIVE_PATH, - 'file:///workspace/src/A.php', - self::DOCUMENT_TEXT, - $source->getToken(), - ); - Loop::delay(100, function () use ($source): void { - $source->cancel(); - }); - - self::assertSame([], wait($promise)); - } - - private function process(?string $config = null, ?LoggerInterface $logger = null, int $timeout = 5000): MagoProcess - { - return new MagoProcess( - $this->workspace()->path(), - new MagoConfig($this->workspace()->path('mago'), $timeout, $config), - $logger ?? new NullLogger(), - ); - } - - private function fakeMago(string $output, int $sleepSeconds = 0): void - { - $capture = $this->workspace()->path('capture'); - $this->workspace()->put('capture/output', $output); - $sleep = $sleepSeconds > 0 ? "sleep $sleepSeconds\n" : ''; - $script = << "$capture/args" - pwd > "$capture/cwd" - cat > "$capture/stdin" - $sleep - cat "$capture/output" - BASH; - $this->workspace()->put('mago', $script); - chmod($this->workspace()->path('mago'), 0755); - } - - private function issuesJson(): string - { - return (string)json_encode([ - 'issues' => [ - [ - 'level' => 'Error', - 'code' => 'invalid-argument', - 'message' => 'bad', - 'annotations' => [ - [ - 'kind' => 'Primary', - 'span' => [ - 'file_id' => ['name' => self::RELATIVE_PATH], - 'start' => ['offset' => 6, 'line' => 1], - 'end' => ['offset' => 8, 'line' => 1], - ], - ], - ], - ], - ], - ]); - } -} diff --git a/lib/Extension/LanguageServerMago/Tests/Model/MagoRealBinaryTest.php b/lib/Extension/LanguageServerMago/Tests/Model/MagoRealBinaryTest.php deleted file mode 100644 index b1a96eb69b..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/Model/MagoRealBinaryTest.php +++ /dev/null @@ -1,67 +0,0 @@ -markTestSkipped('mago binary is not available on PATH'); - } - $this->mago = $mago; - $this->workspace()->reset(); - $this->workspace()->put('mago.toml', "[source]\npaths = [\"src\"]\n"); - } - - public function testAnalyzeReportsTypeErrorsFromTheRealBinary(): void - { - $text = <<<'PHP' - workspace()->put('src/A.php', $text); - - $process = new MagoProcess( - $this->workspace()->path(), - new MagoConfig($this->mago, 20000, null), - new NullLogger(), - ); - - $diagnostics = wait($process->analyse( - 'analyze', - 'mago', - 'src/A.php', - 'file://' . $this->workspace()->path('src/A.php'), - $text, - new NullCancellationToken(), - )); - - self::assertNotEmpty($diagnostics); - self::assertSame('mago', $diagnostics[0]->source); - } -} diff --git a/lib/Extension/LanguageServerMago/Tests/Provider/MagoDiagnosticProviderTest.php b/lib/Extension/LanguageServerMago/Tests/Provider/MagoDiagnosticProviderTest.php deleted file mode 100644 index f3e1eaabb0..0000000000 --- a/lib/Extension/LanguageServerMago/Tests/Provider/MagoDiagnosticProviderTest.php +++ /dev/null @@ -1,51 +0,0 @@ -diagnostic(); - $provider = new MagoDiagnosticProvider(new TestLinter([$diagnostic]), 'mago', true); - - $result = wait($provider->provideDiagnostics($this->document(), new NullCancellationToken())); - - self::assertSame([$diagnostic], $result); - self::assertSame('mago', $provider->name()); - } - - public function testReturnsNothingWhenDisabled(): void - { - $provider = new MagoDiagnosticProvider(new TestLinter([$this->diagnostic()]), 'mago-lint', false); - - $result = wait($provider->provideDiagnostics($this->document(), new NullCancellationToken())); - - self::assertSame([], $result); - self::assertSame('mago-lint', $provider->name()); - } - - private function diagnostic(): Diagnostic - { - return new Diagnostic( - range: new Range(new Position(0, 0), new Position(0, 1)), - message: 'something', - source: 'mago', - ); - } - - private function document(): TextDocumentItem - { - return new TextDocumentItem('file:///src/A.php', 'php', 1, 'phpCsFixer->fix($textDocument->text, ['--diff', '--dry-run']); - - $diffToTextEdits = new DiffToTextEditsConverter(); - $textEdits = $diffToTextEdits->toTextEdits($diff); - - return $textEdits; - }); - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/LanguageServerPhpCsFixerExtension.php b/lib/Extension/LanguageServerPhpCsFixer/LanguageServerPhpCsFixerExtension.php deleted file mode 100644 index 79ef9f46d6..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/LanguageServerPhpCsFixerExtension.php +++ /dev/null @@ -1,132 +0,0 @@ -register(self::SERVICE_VERSION_RESOLVER, function (Container $container) { - $pathResolver = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - - $path = $pathResolver->resolve($container->parameter(self::PARAM_PHP_CS_FIXER_BIN)->string()); - - return new CachedSemVerResolver( - new AggregateSemVerResolver( - new ArbitrarySemVerResolver($container->parameter(self::PARAM_PHP_CS_FIXER_VERSION)->stringOrNull()), - new PhpCsFixerVersionResolver($path, LoggingExtension::channelLogger($container, 'php-cs-fixer')), - ), - LoggingExtension::channelLogger($container, 'php-cs-fixer'), - ); - }); - - $container->register( - PhpCsFixerProcess::class, - function (Container $container) { - $pathResolver = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - - $path = $pathResolver->resolve($container->parameter(self::PARAM_PHP_CS_FIXER_BIN)->string()); - - $configPath = null; - if ($container->parameter(self::PARAM_CONFIG)->value()) { - $configPath = $pathResolver->resolve($container->parameter(self::PARAM_CONFIG)->string()); - } - - return new PhpCsFixerProcess( - $path, - LoggingExtension::channelLogger($container, 'php-cs-fixer'), - $container->expect(self::SERVICE_VERSION_RESOLVER, SemVersionResolver::class), - /** @phpstan-ignore-next-line */ - $container->parameter(self::PARAM_ENV)->value(), - $configPath, - ); - }, - ); - - $container->register(PhpCsFixerFormatter::class, function (Container $container) { - return new PhpCsFixerFormatter($container->get(PhpCsFixerProcess::class)); - }, [ - LanguageServerExtension::TAG_FORMATTER => [], - ]); - - $container->register(PhpCsFixerDiagnosticsProvider::class, function (Container $container) { - return new PhpCsFixerDiagnosticsProvider( - $container->get(PhpCsFixerProcess::class), - new RangesForDiff(), - $container->parameter(self::PARAM_SHOW_DIAGNOSTICS)->bool(), - LoggingExtension::channelLogger($container, 'php-cs-fixer'), - ); - }, [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('php-cs-fixer'), - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [], - ]); - - $container->register(FormatCommand::class, function (Container $container) { - return new FormatCommand( - $container->get(PhpCsFixerProcess::class), - $container->get(ClientApi::class), - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - LoggingExtension::channelLogger($container, 'php-cs-fixer'), - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => 'php_cs_fixer.fix', - ], - ]); - } - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_PHP_CS_FIXER_BIN => '%project_root%/vendor/bin/php-cs-fixer', - self::PARAM_PHP_CS_FIXER_VERSION => null, - self::PARAM_ENV => [ - 'XDEBUG_MODE' => 'off', - ], - self::PARAM_SHOW_DIAGNOSTICS => true, - self::PARAM_CONFIG => null, - ]); - - $schema->setDescriptions([ - self::PARAM_PHP_CS_FIXER_BIN => 'Path to the php-cs-fixer executable', - self::PARAM_PHP_CS_FIXER_VERSION => 'Arbitrary version (if not provided, phpactor tries to detect it - only to run it on unsupported PHP versions)', - self::PARAM_ENV => 'Environment for PHP CS Fixer', - self::PARAM_SHOW_DIAGNOSTICS => 'Whether PHP CS Fixer diagnostics are shown', - self::PARAM_CONFIG => 'Set custom PHP CS config path. Ex., %project_root%/.php-cs-fixer.php', - ]); - } - - public function name(): string - { - return 'language_server_php_cs_fixer'; - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/LanguageServerPhpCsFixerSuggestExtension.php b/lib/Extension/LanguageServerPhpCsFixer/LanguageServerPhpCsFixerSuggestExtension.php deleted file mode 100644 index 4bd8987c17..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/LanguageServerPhpCsFixerSuggestExtension.php +++ /dev/null @@ -1,50 +0,0 @@ -register('language_server_php_cs_fixer.suggest', function (Container $container) { - return new PhpactorComposerSuggestor( - $container->expect(ConfigurationExtension::SERVICE_PHPACTOR_CONFIG_LOCAL, JsonConfig::class), - $container->get(ComposerInspector::class), - function (JsonConfig $config, ComposerInspector $inspector) { - if ($config->has(LanguageServerPhpCsFixerExtension::PARAM_ENABLED)) { - return Changes::none(); - } - - if (!$inspector->package('friendsofphp/php-cs-fixer')) { - return Changes::none(); - } - - return Changes::from([ - new PhpactorConfigChange('PHP-CS-Fixer detected, enable the PHP-CS-Fixer extension?', function (bool $enable) { - return [ - LanguageServerPhpCsFixerExtension::PARAM_ENABLED => $enable, - ]; - }) - ]); - } - ); - }, [ - ConfigurationExtension::TAG_SUGGESTOR => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/LspCommand/FormatCommand.php b/lib/Extension/LanguageServerPhpCsFixer/LspCommand/FormatCommand.php deleted file mode 100644 index 429bedee1b..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/LspCommand/FormatCommand.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ - public function __invoke(string $uri, ?array $rules = null): Promise - { - return call(function () use ($uri, $rules) { - $path = TextDocumentUri::fromString($uri)->path(); - $textDocument = $this->workspace->get($uri); - - $rulesOpt = $rules ? ['--rules', ...$rules] : []; - $diff = yield $this->phpCsFixer->fix($textDocument->text, ['--diff', '--dry-run', ...$rulesOpt]); - - $diffToTextEdits = new DiffToTextEditsConverter(); - $textEdits = $diffToTextEdits->toTextEdits($diff); - - $this->logger->debug(sprintf('PHP CS Fixer produced %s text edits', count($textEdits))); - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => $textEdits - ]), 'Fix with PHP CS Fixer'); - }); - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php b/lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php deleted file mode 100644 index 4514f8046c..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php +++ /dev/null @@ -1,166 +0,0 @@ - $env - */ - public function __construct( - private string $binPath, - private LoggerInterface $logger, - private ?SemVersionResolver $versionResolver = null, - private array $env = [], - private ?string $configPath = null, - ) { - } - - /** - * @param string[] $options - * - * @return Promise - */ - public function fix(string $content, array $options = []): Promise - { - return call(function () use ($content, $options) { - $version = yield $this->versionResolver?->resolve(); - - if (false === array_search('--rules', $options, true) && null !== $this->configPath) { - $options = array_merge($options, ['--config', $this->configPath]); - } - - /** @var Process */ - $process = yield $this->run($this->resolveEnv($version), 'fix', ...$this->resolveExtraArgs($version, ...[ - ...$options, - '-', - ])); - - $stdin = $process->getStdin(); - $stdin->write($content); - $stdin->end(); - - $stdout = yield buffer($process->getStdout()); - $exitCode = yield $process->join(); - - if ( - $exitCode !== 0 - && $exitCode !== self::EXIT_SOME_FILES_INVALID - && $exitCode !== self::EXIT_FILES_NEEDS_FIXING - && $exitCode !== (self::EXIT_SOME_FILES_INVALID | self::EXIT_FILES_NEEDS_FIXING) - ) { - throw new PhpCsFixerError( - $exitCode, - $process->getCommand(), - yield buffer($process->getStderr()), - $stdout, - ); - } - - return $stdout; - }); - } - - /** - * @param string[] $options - * - * @return Promise - */ - public function describe(string $rule, array $options = []): Promise - { - return call(function () use ($rule, $options) { - /** @var Process */ - $process = yield $this->run($this->env, 'describe', ...[...$options, $rule]); - - $stdout = yield buffer($process->getStdout()); - $exitCode = yield $process->join(); - - if ($exitCode !== 0) { - throw new PhpCsFixerError( - $exitCode, - $process->getCommand(), - yield buffer($process->getStderr()), - $stdout, - ); - } - - return $stdout; - }); - } - - /** - * @param array $env - * @return Promise - */ - public function run(array $env, string ...$args): Promise - { - return call(function () use ($env, $args) { - $process = ProcessBuilder::create([PHP_BINARY, $this->binPath, ...$args])->mergeParentEnv()->env($env)->build(); - yield $process->start(); - - $process->join() - ->onResolve(function (?Throwable $error, $data) use ($process): void { - $this->logger->log( - $error ? 'warning' : 'debug', - sprintf( - 'Executed %s, which exited with %s', - $process->getCommand(), - $data, - ), - ); - }); - - return $process; - }); - } - - /** - * @return string[] - */ - private function resolveExtraArgs(?SemVersion $version, string ...$args): array - { - if ($version?->greaterThanOrEqualTo(SemVersion::fromString('3.89.2'))) { - return [...['--allow-unsupported-php-version=yes'], ...$args]; - } - - return $args; - } - - /** - * @return array - */ - private function resolveEnv(?SemVersion $version): array - { - $env = $this->env; - - if (null === $version) { - return $env; - } - - if ($version->greaterThanOrEqualTo(SemVersion::fromString('3.89.2'))) { - unset($env['PHP_CS_FIXER_IGNORE_ENV']); - - return $env; - } - - $env['PHP_CS_FIXER_IGNORE_ENV'] = '1'; - - return $env; - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php b/lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php deleted file mode 100644 index 975174c3dc..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php +++ /dev/null @@ -1,189 +0,0 @@ - */ - private array $ruleDescriptions = []; - - public function __construct( - private PhpCsFixerProcess $phpCsFixer, - private RangesForDiff $rangeForDiff, - private bool $showDiagnostics, - private LoggerInterface $logger, - ) { - } - - /** - * @return Promise - */ - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - if (!$this->showDiagnostics) { - return new Success([]); - } - - return call(function () use ($textDocument, $cancel) { - $diagnostics = yield $this->findDiagnostics($textDocument, $cancel); - - return $diagnostics ?: []; - }); - } - - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument, $cancel) { - $diagnostics = yield $this->findDiagnostics($textDocument, $cancel); - - if (false === $diagnostics) { - return []; - } - - $title = 'Format with PHP CS Fixer'; - - return [ - CodeAction::fromArray([ - 'title' => $title, - 'kind' => 'source.fixAll.phpactor.phpCsFixer', - 'diagnostics' => $diagnostics, - 'command' => new Command( - $title, - 'php_cs_fixer.fix', - [ - $textDocument->uri, - ] - ), - ]), - ]; - }); - } - - public function kinds(): array - { - return ['source.fixAll.phpactor.phpCsFixer']; - } - - public function name(): string - { - return 'php-cs-fixer'; - } - - public function describe(): string - { - return 'php-cs-fixer'; - } - - /** - * @return Promise False when there are no diagnostics available for file, array othwerwise - * Array containing diagnostics to show - */ - private function findDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument) { - $outputJson = yield $this->phpCsFixer->fix($textDocument->text, [ - '--dry-run', - '--verbose', - '--format', - 'json', - ]); - - $output = json_decode($outputJson, flags: JSON_THROW_ON_ERROR); - - if (empty($output->files)) { - return false; - } - - $rules = $output->files[0]->appliedFixers; - - $diagnostics = []; - - $diffParser = new Parser(); - - foreach ($rules as $rule) { - $fileDiffText = yield $this->phpCsFixer->fix($textDocument->text, ['--dry-run', '--diff', '--using-cache', 'no', '--rules', $rule]); - $fileDiff = $diffParser->parse($fileDiffText); - - // one file input is passed and one file expected - if (1 !== count($fileDiff)) { - $this->logger->warning( - sprintf("Expected php-cs-fixer to provide 1 diff, got %s. Skipping diagnostics for file '%s'", count($fileDiff), $textDocument->uri) - ); - - continue; - } - - $ranges = $this->rangeForDiff->createRangesForDiff($fileDiff[0]); - - foreach ($ranges as $range) { - $diagnostics[] = yield $this->createRuleDiagnostics($rule, $range); - } - } - - return $diagnostics; - }); - } - - /** - * @return Promise - */ - private function createRuleDiagnostics(string $rule, Range $range): Promise - { - return call(function () use ($rule, $range) { - return Diagnostic::fromArray([ - 'message' => yield $this->explainRule($rule), - 'range' => $range, - 'severity' => DiagnosticSeverity::WARNING, - 'source' => $this->name(), - 'code' => $rule, - ]); - }); - } - - /** - * @return Promise - */ - private function explainRule(string $rule): Promise - { - if (isset($this->ruleDescriptions[$rule])) { - return new Success($this->ruleDescriptions[$rule]); - } - - return call(function () use ($rule) { - $description = yield $this->phpCsFixer->describe($rule); - - // @see https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/src/Console/Command/DescribeCommand.php - // for a class producing descriptions output in php-cs-fixer - - // preg_replace calls below are matching content generated from above class, not content of individual rules. - - // "clean up" the description - $description = (string)preg_replace('/Description of ([\\w\\-\\_ ]+) rule.*/', '', $description); - $description = (string)preg_replace('/Fixer is configurable using following option.*/s', '', $description); - $description = (string)preg_replace('/Fixing examples:.*/s', '', $description); - $description = (string)preg_replace('/Description of the `[^`]+` rule./s', '', $description); - $description = trim($description); - - $this->ruleDescriptions[$rule] = $description; - - return $this->ruleDescriptions[$rule]; - }); - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php b/lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php deleted file mode 100644 index 735da9cbba..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php +++ /dev/null @@ -1,69 +0,0 @@ -getPhpCsFixer(); - - $process = call(function () use ($phpCsFixer) { - $process = yield $phpCsFixer->run([], '--version'); - $stdout = yield buffer($process->getStdout()); - - self::assertStringContainsString('PHP CS Fixer ', $stdout, sprintf("Expected php-cs-fixer --version to return it's name followed with version, got: %s", $stdout)); - }); - - wait($process); - } - - public function testFix(): void - { - $phpCsFixer = $this->getPhpCsFixer(); - - $correctFix = wait($phpCsFixer->fix( - <<fix( - <<expectException(PhpCsFixerError::class); - $incorrectFix = wait($phpCsFixer->fix('', ['--invalid-option'])); - } - - public function testDescribe(): void - { - $phpCsFixer = $this->getPhpCsFixer(); - - $description = wait($phpCsFixer->describe('braces')); - - self::assertIsString($description); - self::assertStringStartsWith('Description of', $description); - - $this->expectException(PhpCsFixerError::class); - wait($phpCsFixer->describe('UNKNOWN_RULE')); - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/Tests/PhpCsFixerTestCase.php b/lib/Extension/LanguageServerPhpCsFixer/Tests/PhpCsFixerTestCase.php deleted file mode 100644 index 4931427f83..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/Tests/PhpCsFixerTestCase.php +++ /dev/null @@ -1,28 +0,0 @@ - 'off' ]) extends PhpCsFixerProcess { - public function fix(string $content, array $options = []): Promise - { - return parent::fix($content, array_merge($options, ['--no-ansi'])); - } - - public function describe(string $rule, array $options = []): Promise - { - return parent::describe($rule, array_merge($options, ['--no-ansi'])); - } - }; - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/Tests/Provider/PhpCsFixerDiagnosticsProviderTest.php b/lib/Extension/LanguageServerPhpCsFixer/Tests/Provider/PhpCsFixerDiagnosticsProviderTest.php deleted file mode 100644 index 2820165c98..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/Tests/Provider/PhpCsFixerDiagnosticsProviderTest.php +++ /dev/null @@ -1,147 +0,0 @@ -getPhpCsFixerDiagnosticsProvider(true); - - $cancel = new NullCancellationToken(); - $document = ProtocolFactory::textDocumentItem('/tmp/test.php', $fileContent); - - $diagnostics = wait($provider->provideDiagnostics($document, $cancel)); - self::assertIsArray($diagnostics); - foreach ($diagnostics as $diagnostic) { - self::assertInstanceOf(Diagnostic::class, $diagnostic); - } - self::assertCount($expectedDiagnostics, $diagnostics); - } - - #[DataProvider('fileProvider')] - public function testProvideDiagnosticsHidden(string $fileContent): void - { - $provider = $this->getPhpCsFixerDiagnosticsProvider(false); - - $cancel = new NullCancellationToken(); - $document = ProtocolFactory::textDocumentItem('/tmp/test.php', $fileContent); - - $diagnostics = wait($provider->provideDiagnostics($document, $cancel)); - self::assertIsArray($diagnostics); - self::assertCount(0, $diagnostics); - } - - #[DataProvider('fileProvider')] - public function testProvideActionsForVisibleDiagnostics(string $fileContent, int $expectedDiagnostics): void - { - $provider = $this->getPhpCsFixerDiagnosticsProvider(true); - - $cancel = new NullCancellationToken(); - $document = ProtocolFactory::textDocumentItem('/tmp/test.php', $fileContent); - - $actions = wait( - $provider->provideActionsFor( - $document, - new Range( - new Position(0, 0), - new Position(PHP_INT_MAX, PHP_INT_MAX) - ), - $cancel - ) - ); - - self::assertIsArray($actions); - if ($expectedDiagnostics > 0) { - self::assertTrue(count($actions) > 0, 'Expected at least one action if file has diagnostics'); - } - foreach ($actions as $action) { - self::assertInstanceOf(CodeAction::class, $action); - } - } - - #[DataProvider('fileProvider')] - public function testProvideActionsForHiddenDiagnostics(string $fileContent, int $expectedDiagnostics): void - { - $provider = $this->getPhpCsFixerDiagnosticsProvider(false); - - $cancel = new NullCancellationToken(); - $document = ProtocolFactory::textDocumentItem('/tmp/test.php', $fileContent); - - $actions = wait( - $provider->provideActionsFor( - $document, - new Range( - new Position(0, 0), - new Position(PHP_INT_MAX, PHP_INT_MAX) - ), - $cancel - ) - ); - - self::assertIsArray($actions); - if ($expectedDiagnostics > 0) { - self::assertTrue(count($actions) > 0, 'Expected at least one action if file has diagnostics'); - } - foreach ($actions as $action) { - self::assertInstanceOf(CodeAction::class, $action); - } - } - - public function getPhpCsFixerDiagnosticsProvider(bool $showDiagnostics): PhpCsFixerDiagnosticsProvider - { - $phpCsFixer = $this->getPhpCsFixer(); - - return new PhpCsFixerDiagnosticsProvider( - $phpCsFixer, - new RangesForDiff(), - $showDiagnostics, - new NullLogger() - ); - } - - /** - * @return Generator - */ - public static function fileProvider(): Generator - { - yield [ - <<resolve(); - - self::assertMatchesRegularExpression('/^\d+\.\d+\.\d+.*$/', $version->__toString()); - }); - - wait($process); - } -} diff --git a/lib/Extension/LanguageServerPhpCsFixer/VersionResolver/PhpCsFixerVersionResolver.php b/lib/Extension/LanguageServerPhpCsFixer/VersionResolver/PhpCsFixerVersionResolver.php deleted file mode 100644 index a52bfb5a31..0000000000 --- a/lib/Extension/LanguageServerPhpCsFixer/VersionResolver/PhpCsFixerVersionResolver.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ - public function resolve(): Promise - { - return call(function () { - $versionQuery = yield (new PhpCsFixerProcess($this->binPath, $this->logger))->run([], '--version'); - $stdout = yield buffer($versionQuery->getStdout()); - $exitCode = yield $versionQuery->join(); - - if ($exitCode !== 0) { - return null; - } - - preg_match('/^PHP CS Fixer (\d+\.\d+\.\d+) /', $stdout, $version); - - return (count($version) > 0) ? SemVersion::fromString($version[1]) : null; - }); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Adapter/VersionResolver/PhpstanVersionResolver.php b/lib/Extension/LanguageServerPhpstan/Adapter/VersionResolver/PhpstanVersionResolver.php deleted file mode 100644 index d14943f917..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Adapter/VersionResolver/PhpstanVersionResolver.php +++ /dev/null @@ -1,27 +0,0 @@ -process->version(); - if (!is_string($versionString)) { - return null; - } - return SemVersion::fromString($versionString); - }); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/LanguageServerPhpstanExtension.php b/lib/Extension/LanguageServerPhpstan/LanguageServerPhpstanExtension.php deleted file mode 100644 index 50a555317c..0000000000 --- a/lib/Extension/LanguageServerPhpstan/LanguageServerPhpstanExtension.php +++ /dev/null @@ -1,133 +0,0 @@ -register(PhpstanVersionResolver::class, function (Container $container) { - return new CachedSemVerResolver( - new PhpstanVersionResolver($container->get(PhpstanProcess::class)), - LoggingExtension::channelLogger($container, 'phpstan'), - ); - }); - - $container->register( - PhpstanDiagnosticProvider::class, - function (Container $container) { - return new PhpstanDiagnosticProvider( - $container->get(Linter::class) - ); - }, - [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('phpstan'), - ] - ); - - $container->register( - Linter::class, - function (Container $container) { - return new PhpstanLinter( - $container->get(PhpstanProcess::class), - $container->get(PhpstanVersionResolver::class), - $container->parameter(self::PARAM_TMP_FILE_DISABLED)->value() ? $container->parameter(self::PARAM_TMP_FILE_DISABLED)->bool() : false, - ); - } - ); - - $container->register( - PhpstanProcess::class, - function (Container $container) { - $pathResolver = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - - $binPath = $pathResolver->resolve($container->parameter(self::PARAM_PHPSTAN_BIN)->string()); - - $root = $pathResolver->resolve('%project_root%'); - - $configPath = null; - if ($container->parameter(self::PARAM_CONFIG)->value()) { - $configPath = $pathResolver->resolve($container->parameter(self::PARAM_CONFIG)->string()); - } - - /** @var DiagnosticSeverity::* $severity */ - $severity = $container->parameter(self::PARAM_SEVERITY)->value() ? $container->parameter(self::PARAM_SEVERITY)->int() : DiagnosticSeverity::ERROR; - - $phpstanConfig = new PhpstanConfig( - $binPath, - $severity, - $container->parameter(self::PARAM_LEVEL)->value() ? $container->parameter(self::PARAM_LEVEL)->string() : null, - $configPath, - $container->parameter(self::PARAM_MEM_LIMIT)->value() ? $container->parameter(self::PARAM_MEM_LIMIT)->string() : null, - ); - - return new PhpstanProcess( - $root, - $phpstanConfig, - LoggingExtension::channelLogger($container, 'phpstan'), - ); - } - ); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults( - [ - self::PARAM_PHPSTAN_BIN => '%project_root%/vendor/bin/phpstan', - self::PARAM_SEVERITY => DiagnosticSeverity::ERROR, - self::PARAM_LEVEL => null, - self::PARAM_CONFIG => null, - self::PARAM_MEM_LIMIT => null, - self::PARAM_TMP_FILE_DISABLED => false, - self::PARAM_EDITOR_MODE => false, - ] - ); - $schema->setDescriptions( - [ - self::PARAM_PHPSTAN_BIN => 'Path to the PHPStan executable', - self::PARAM_SEVERITY => 'Severity at which PHPStan diagnostics should be reported. Ranges from 1 (error) to 4 (hint).', - self::PARAM_LEVEL => 'Override the PHPStan level', - self::PARAM_CONFIG => 'Override the PHPStan configuration file', - self::PARAM_MEM_LIMIT => 'Override the PHPStan memory limit', - self::PARAM_TMP_FILE_DISABLED => 'Disable the use of temporary files when.' - . ' This prevents as-you-type diagnostics, but ensures paths in phpstan config are respected.' - . ' See https://github.com/phpactor/phpactor/issues/2763', - self::PARAM_EDITOR_MODE => 'DEPRECATED. Editor mode of Phpstan is used automatically when it\'s supported.' - ] - ); - } - - public function name(): string - { - return 'language_server_phpstan'; - } -} diff --git a/lib/Extension/LanguageServerPhpstan/LanguageServerPhpstanSuggestExtension.php b/lib/Extension/LanguageServerPhpstan/LanguageServerPhpstanSuggestExtension.php deleted file mode 100644 index dc8eee6e0f..0000000000 --- a/lib/Extension/LanguageServerPhpstan/LanguageServerPhpstanSuggestExtension.php +++ /dev/null @@ -1,50 +0,0 @@ -register('language_server_phpstan.suggest', function (Container $container) { - return new PhpactorComposerSuggestor( - $container->expect(ConfigurationExtension::SERVICE_PHPACTOR_CONFIG_LOCAL, JsonConfig::class), - $container->get(ComposerInspector::class), - function (JsonConfig $config, ComposerInspector $inspector) { - if ($config->has(LanguageServerPhpstanExtension::PARAM_ENABLED)) { - return Changes::none(); - } - - if (!$inspector->package('phpstan/phpstan')) { - return Changes::none(); - } - - return Changes::from([ - new PhpactorConfigChange('Phpstan detected, enable PHPStan extension?', function (bool $enable) { - return [ - LanguageServerPhpstanExtension::PARAM_ENABLED => $enable, - ]; - }) - ]); - } - ); - }, [ - ConfigurationExtension::TAG_SUGGESTOR => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php b/lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php deleted file mode 100644 index 0b3905e21b..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php +++ /dev/null @@ -1,83 +0,0 @@ - - */ - public function parse(string $jsonString, int $severity): array - { - $decoded = $this->decodeJson($jsonString); - $diagnostics = []; - - foreach ($decoded['files'] ?? [] as $fileDiagnostics) { - foreach ($fileDiagnostics['messages'] as $message) { - $lineNo = (int)$message['line'] - 1; - $lineNo = (int)$lineNo > 0 ? $lineNo : 0; - $text = $message['message']; - $diagnostics[] = new Diagnostic( - message: $text, - range: new Range(new Position($lineNo, 1), new Position($lineNo, 100)), - severity: $severity, - source: 'phpstan', - code: $message['identifier'] ?? null, - ); - if (($message['tip'] ?? null) !== null) { - $diagnostics[] = new Diagnostic( - message: $message['tip'], - range: new Range(new Position($lineNo, 1), new Position($lineNo, 100)), - severity: DiagnosticSeverity::HINT, - source: 'phpstan', - codeDescription: $this->resolveCodeDescription($message), - code: $message['identifier'] ?? null, - ); - } - } - } - - return $diagnostics; - } - - /** - * @return array - */ - private function decodeJson(string $jsonString): array - { - $decoded = json_decode($jsonString, true); - - if (null === $decoded) { - throw new RuntimeException(sprintf( - 'Could not decode expected PHPStan JSON string "%s"', - $jsonString - )); - } - - return $decoded; - } - - /** - * @param array{tip?: string} $message - */ - private function resolveCodeDescription(array $message): ?CodeDescription - { - $tip = $message['tip'] ?? null; - if (null === $tip) { - return null; - } - if (!preg_match('{(https?\://[^ ]+)$}', $tip, $matches)) { - return null; - } - - return new CodeDescription($matches[1]); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Model/Linter.php b/lib/Extension/LanguageServerPhpstan/Model/Linter.php deleted file mode 100644 index cf3e33caf5..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Model/Linter.php +++ /dev/null @@ -1,14 +0,0 @@ -> - */ - public function lint(string $url, ?string $text): Promise; -} diff --git a/lib/Extension/LanguageServerPhpstan/Model/Linter/PhpstanLinter.php b/lib/Extension/LanguageServerPhpstan/Model/Linter/PhpstanLinter.php deleted file mode 100644 index 3201620825..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Model/Linter/PhpstanLinter.php +++ /dev/null @@ -1,78 +0,0 @@ -disableTmpFile; - } - - public function lint(string $url, ?string $text): Promise - { - return call(function () use ($url, $text) { - $version = yield $this->versionResolver->resolve(); - - if (!$version instanceof SemVersion) { - throw new RuntimeException(sprintf( - 'Could not determine PHPStan version' - )); - } - - $diagnostics = yield from $this->doLint($url, $text, $version); - - return $diagnostics; - }); - } - - /** - * @return Generator>> - */ - private function doLint(string $url, ?string $text, SemVersion $version): Generator - { - $path = TextDocumentUri::fromString($url)->path(); - - if (null === $text || $this->disableTmpFile) { - return yield $this->phpstanProcess->analyseInPlace($path); - } - - $tempFile = tempnam(sys_get_temp_dir(), 'phpstanls'); - file_put_contents($tempFile, $text); - - try { - if ( - $version->greaterThanOrEqualTo(SemVersion::fromString('2.1.17')) - || - ( - $version->greaterThanOrEqualTo(SemVersion::fromString('1.12.27')) && - $version->lessThan(SemVersion::fromString('2.0.0')) - ) - ) { - return yield $this->phpstanProcess->editorModeAnalyse($path, $tempFile); - } - - return yield $this->phpstanProcess->analyseInPlace($tempFile); - } finally { - @unlink($tempFile); - } - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Model/Linter/TestLinter.php b/lib/Extension/LanguageServerPhpstan/Model/Linter/TestLinter.php deleted file mode 100644 index 09b24754ad..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Model/Linter/TestLinter.php +++ /dev/null @@ -1,29 +0,0 @@ - $diagnostics - */ - public function __construct( - private array $diagnostics, - private int $delay - ) { - } - - public function lint(string $url, ?string $text): Promise - { - return call(function () { - yield new Delayed($this->delay); - return $this->diagnostics; - }); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Model/PhpstanConfig.php b/lib/Extension/LanguageServerPhpstan/Model/PhpstanConfig.php deleted file mode 100644 index 705cbbf029..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Model/PhpstanConfig.php +++ /dev/null @@ -1,48 +0,0 @@ -level; - } - - public function phpstanBin(): string - { - return $this->phpstanBin; - } - - public function config(): ?string - { - return $this->config; - } - - public function memLimit(): ?string - { - return $this->memLimit; - } - - /** - * @return DiagnosticSeverity::* - */ - public function severity(): int - { - return $this->severity; - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Model/PhpstanProcess.php b/lib/Extension/LanguageServerPhpstan/Model/PhpstanProcess.php deleted file mode 100644 index a8b21df09f..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Model/PhpstanProcess.php +++ /dev/null @@ -1,145 +0,0 @@ -> - */ - public function analyseInPlace(string $filename): Promise - { - $args = [ - PHP_BINARY, - $this->config->phpstanBin(), - 'analyse', - '--no-progress', - '--error-format=json', - $filename, - ]; - - return $this->runPhpstan($args); - } - - /** - * @return Promise> - */ - public function editorModeAnalyse(string $filename, string $tempFile): Promise - { - $args = [ - PHP_BINARY, - $this->config->phpstanBin(), - 'analyse', - '--no-progress', - '--error-format=json', - '--tmp-file='.$tempFile, - '--instead-of='.$filename, - $filename - ]; - - return $this->runPhpstan($args); - } - - /** - * @return Promise - */ - public function version(): Promise - { - return call(function () { - $args = [ - PHP_BINARY, - $this->config->phpstanBin(), - '--version', - ]; - $process = new Process($args, $this->cwd); - yield $process->start(); - $exitCode = yield $process->join(); - - if ($exitCode !== 0) { - return null; - } - - $stdout = yield buffer($process->getStdout()); - - if (!is_string($stdout)) { - return null; - } - - preg_match('{[0-9]+\.[0-9]+\.[0-9]+}', $stdout, $matches); - - return $matches[0] ?? null; - }); - } - - /** - * @param array $args - * - * @return Promise> - */ - private function runPhpstan(array $args): Promise - { - return call(function () use ($args) { - if (null !== $this->config->level()) { - $args[] = '--level=' . (string)$this->config->level(); - } - if (null !== $this->config->config()) { - $args[] = '--configuration=' . (string)$this->config->config(); - } - if (null !== $this->config->memLimit()) { - $args[] = '--memory-limit=' . (string)$this->config->memLimit(); - } - $process = new Process($args, $this->cwd); - - $start = microtime(true); - $pid = yield $process->start(); - - $stdout = buffer($process->getStdout()); - $stderr = buffer($process->getStderr()); - - $exitCode = yield $process->join(); - - if ($exitCode > 1) { - $this->logger->error(sprintf( - 'Phpstan exited with code "%s": %s', - $exitCode, - yield $stderr - )); - - return []; - } - - $this->logger->debug(sprintf( - 'Phpstan completed in %s: %s in %s', - number_format(microtime(true) - $start, 4), - $process->getCommand(), - $process->getWorkingDirectory(), - )); - - $stdout = yield $stdout; - if ($stdout === '') { - $this->logger->error(sprintf( - 'Phpstan exited with code "%s": But the standard output was empty', - $exitCode, - )); - return []; - } - - return $this->parser->parse($stdout, $this->config->severity()); - }); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Provider/PhpstanDiagnosticProvider.php b/lib/Extension/LanguageServerPhpstan/Provider/PhpstanDiagnosticProvider.php deleted file mode 100644 index 520d3a7075..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Provider/PhpstanDiagnosticProvider.php +++ /dev/null @@ -1,26 +0,0 @@ -linter->lint($textDocument->uri, $textDocument->text); - } - - public function name(): string - { - return 'phpstan'; - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Tests/IntegrationTestCase.php b/lib/Extension/LanguageServerPhpstan/Tests/IntegrationTestCase.php deleted file mode 100644 index 5b02e01680..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,14 +0,0 @@ -getLinter([]); - $this->assertFalse($linter->isTmpFileDisabled()); - - // Case: Enable via param - $linter = $this->getLinter([LanguageServerPhpstanExtension::PARAM_TMP_FILE_DISABLED => false]); - $this->assertFalse($linter->isTmpFileDisabled()); - - // Case: Disable via param - $linter = $this->getLinter([LanguageServerPhpstanExtension::PARAM_TMP_FILE_DISABLED => true]); - $this->assertTrue($linter->isTmpFileDisabled()); - } - - /** - * @param array $params - */ - private function getLinter(array $params = []): PhpstanLinter - { - $container = $this->getContainer($params); - - $linter = $container->get(Linter::class); - - $this->assertInstanceOf(PhpstanLinter::class, $linter); - - return $linter; - } - - /** - * @param array $params - */ - private function getContainer(array $params = []): Container - { - return PhpactorContainer::fromExtensions( - [ - FilePathResolverExtension::class, - LoggingExtension::class, - LanguageServerPhpstanExtension::class - ], - $params - ); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Tests/Model/DiagnosticsParserTest.php b/lib/Extension/LanguageServerPhpstan/Tests/Model/DiagnosticsParserTest.php deleted file mode 100644 index c330e1e602..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Tests/Model/DiagnosticsParserTest.php +++ /dev/null @@ -1,128 +0,0 @@ -parse($phpstanJson, DiagnosticSeverity::ERROR)); - } - - /** - * @return Generator - */ - public static function provideParse(): Generator - { - yield [ - '{"totals":{"errors":0,"file_errors":1},"files":{"/home/daniel/www/phpactor/language-server-phpstan/test.php":{"errors":1,"messages":[{"message":"Undefined variable: $bar","line":3,"ignorable":true}]}},"errors":[]}', - 1 - ]; - yield [ - <<<'EOT' - { - "totals": { - "errors": 0, - "file_errors": 6 - }, - "files": { - "/home/daniel/www/php-tui/cli-parser/src/Type/TypeFactory.php": { - "errors": 1, - "messages": [ - { - "message": "Method PhpTui\\CliParser\\Type\\TypeFactory::fromReflectionType() should return PhpTui\\CliParser\\Type\\Type but returns PhpTui\\CliParser\\Type\\BooleanType|PhpTui\\CliParser\\Type\\FloatType|PhpTui\\CliParser\\Type\\IntegerType|PhpTui\\CliParser\\Type\\StringType.", - "line": 37, - "ignorable": true, - "tip": "• Template type TParseType on class PhpTui\\CliParser\\Type\\Type is not covariant. Learn more: https://phpstan.org/blog/whats-up-with-template-covariant\n• Template type TParseType on class PhpTui\\CliParser\\Type\\Type is not covariant. Learn more: https://phpstan.org/blog/whats-up-with-template-covariant\n• Template type TParseType on class PhpTui\\CliParser\\Type\\Type is not covariant. Learn more: https://phpstan.org/blog/whats-up-with-template-covariant\n• Template type TParseType on class PhpTui\\CliParser\\Type\\Type is not covariant. Learn more: https://phpstan.org/blog/whats-up-with-template-covariant" - } - ] - }, - "/home/daniel/www/php-tui/cli-parser/tests/Unit/ParserTest.php": { - "errors": 3, - "messages": [ - { - "message": "Syntax error, unexpected ';', expecting ',' or ']' or ')' on line 64", - "line": 64, - "ignorable": false - }, - { - "message": "Syntax error, unexpected ';' on line 70", - "line": 70, - "ignorable": false - }, - { - "message": "Syntax error, unexpected '}', expecting EOF on line 129", - "line": 129, - "ignorable": false - } - ] - }, - "/home/daniel/www/php-tui/cli-parser/tests/Unit/Type/TypeFactoryTest.php": { - "errors": 2, - "messages": [ - { - "message": "Parameter #1 ...$types of class PhpTui\\CliParser\\Type\\UnionType constructor expects PhpTui\\CliParser\\Type\\Type, PhpTui\\CliParser\\Type\\StringType given.", - "line": 73, - "ignorable": true, - "tip": "Template type TParseType on class PhpTui\\CliParser\\Type\\Type is not covariant. Learn more: https://phpstan.org/blog/whats-up-with-template-covariant" - }, - { - "message": "Parameter #2 ...$types of class PhpTui\\CliParser\\Type\\UnionType constructor expects PhpTui\\CliParser\\Type\\Type, PhpTui\\CliParser\\Type\\IntegerType given.", - "line": 73, - "ignorable": true, - "tip": "Template type TParseType on class PhpTui\\CliParser\\Type\\Type is not covariant. Learn more: https://phpstan.org/blog/whats-up-with-template-covariant" - } - ] - } - }, - "errors": [] - } - EOT, - 9 - ]; - } - - - public function testTipUrl(): void - { - $diagnostics = (new DiagnosticsParser())->parse( - json_encode([ - 'files' => [ - 'file1.php' => [ - 'messages' => [ - [ - 'line' => 2, - 'message' => 'foobar', - 'tip' => 'Template is not covariant. Learn more: https://phpstan.org/blog/whats-up-with-template-covariant' - - ] - ] - ] - ], - ], JSON_THROW_ON_ERROR), - DiagnosticSeverity::ERROR - ); - self::assertCount(2, $diagnostics); - $diagnostic = $diagnostics[1]; - self::assertEquals( - new CodeDescription('https://phpstan.org/blog/whats-up-with-template-covariant'), - $diagnostic->codeDescription - ); - } - - public function testExceptionOnNonJsonString(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('stdout was not JSON'); - (new DiagnosticsParser())->parse('stdout was not JSON', DiagnosticSeverity::ERROR); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Tests/Model/PhpstanLinterTest.php b/lib/Extension/LanguageServerPhpstan/Tests/Model/PhpstanLinterTest.php deleted file mode 100644 index b6fc6a48fa..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Tests/Model/PhpstanLinterTest.php +++ /dev/null @@ -1,83 +0,0 @@ -'; - - $phpstanProcess = $this->createMock(PhpstanProcess::class); - $phpstanProcess->expects($this->once()) - ->method('editorModeAnalyse') - ->with($filePathInProject, $this->callback(function (string $tempFile) use ($fileContent) { - // Assert the temp file has the same content as the project file - $this->assertStringEqualsFile($tempFile, $fileContent); - - // Explicitly confirm a temporary file is used. - // - // NOTE: We use implementation detail knowledge here on how the tempnam is created. - // This might not be necessary but provides an extra layer of safety and makes - // the test more declarative. - // - // NOTE: It is not guaranteed that the sys_get_temp_dir() will be the *actual* directory - // that is used. MacOs for example will create a directory prefixed with /private: - // /private//... - // - $expectedPathSegment = sys_get_temp_dir() . '/phpstanls'; - $this->assertStringContainsString($expectedPathSegment, $tempFile); - - return true; - })); - - $linter = new PhpstanLinter( - $phpstanProcess, - new ArbitrarySemVerResolver(SemVersion::fromString('2.30.0')) - ); - - $linter->lint($filePathInProject, $fileContent); - } - - public function testLinterUsesOriginalFilePathWhenTmpFileDisabled(): void - { - $originalFilePath = '/foo'; - - $phpstanProcess = $this->createMock(PhpstanProcess::class); - $phpstanProcess->expects($this->once()) - ->method('analyseInPlace') - ->with($originalFilePath); - - $linter = new PhpstanLinter( - phpstanProcess: $phpstanProcess, - versionResolver: new ArbitrarySemVerResolver(SemVersion::fromString('1.0.0')), - disableTmpFile: true, - ); - - $linter->lint($originalFilePath, ''); - } - - public function testLinterUsesEditoMode(): void - { - $originalFilePath = '/foo'; - - $phpstanProcess = $this->createMock(PhpstanProcess::class); - $phpstanProcess->expects($this->once()) - ->method('editorModeAnalyse') - ->with($originalFilePath); - - $linter = new PhpstanLinter( - phpstanProcess: $phpstanProcess, - versionResolver: new ArbitrarySemVerResolver(SemVersion::fromString('2.4.0')), - ); - - $linter->lint($originalFilePath, 'example'); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Tests/Model/PhpstanProcessTest.php b/lib/Extension/LanguageServerPhpstan/Tests/Model/PhpstanProcessTest.php deleted file mode 100644 index a75d502ef4..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Tests/Model/PhpstanProcessTest.php +++ /dev/null @@ -1,99 +0,0 @@ - $expectedDiagnostics - */ - #[DataProvider('provideLint')] - public function testLint(string $source, int $configuredSeverity, array $expectedDiagnostics): void - { - $this->workspace()->reset(); - $this->workspace()->put('test.php', $source); - $linter = $this->createProcess($configuredSeverity); - $diagnostics = wait($linter->analyseInPlace($this->workspace()->path('test.php'))); - self::assertEquals($expectedDiagnostics, $diagnostics); - } - - /** - * @return Generator - */ - public static function provideLint(): Generator - { - yield [ - 'workspace()->reset(); - $process = $this->createProcess(); - $version = wait($process->version()); - self::assertIsString($version); - self::assertMatchesRegularExpression('{^[0-9]+\.[0-9]+\.[0-9]+}', $version); - } - - /** - * @param DiagnosticSeverity::* $configuredSeverity - */ - private function createProcess(int $configuredSeverity = DiagnosticSeverity::ERROR): PhpstanProcess - { - return new PhpstanProcess( - $this->workspace()->path(), - new PhpstanConfig(__DIR__ . '/../../../../../vendor/bin/phpstan', $configuredSeverity, '7', __DIR__ . '/../../../../../phpstan-baseline.neon', '200M'), - new NullLogger() - ); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Tests/Provider/PhpstanDiagnosticProviderTest.php b/lib/Extension/LanguageServerPhpstan/Tests/Provider/PhpstanDiagnosticProviderTest.php deleted file mode 100644 index 25ff212a3f..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Tests/Provider/PhpstanDiagnosticProviderTest.php +++ /dev/null @@ -1,54 +0,0 @@ -addDiagnosticsProvider(new PhpstanDiagnosticProvider( - $this->createTestLinter() - )); - $tester->enableDiagnostics(); - $tester->enableTextDocuments(); - $this->tester = $tester->build(); - $this->tester->initialize(); - } - - /** - * @return Generator - */ - public function testHandleSingle(): void - { - $updated = new TextDocumentUpdated(ProtocolFactory::versionedTextDocumentIdentifier('file:///path', 12), 'asd'); - $this->tester->textDocument()->open('file:///path', 'asd'); - - wait(delay(10)); - - self::assertEquals(2, $this->tester->transmitter()->count()); - } - - private function createTestLinter(): TestLinter - { - return new TestLinter([ - DiagnosticBuilder::create()->build(), - ], 10); - } -} diff --git a/lib/Extension/LanguageServerPhpstan/Tests/Util/DiagnosticBuilder.php b/lib/Extension/LanguageServerPhpstan/Tests/Util/DiagnosticBuilder.php deleted file mode 100644 index adea9f8bc0..0000000000 --- a/lib/Extension/LanguageServerPhpstan/Tests/Util/DiagnosticBuilder.php +++ /dev/null @@ -1,29 +0,0 @@ - 'Undefined variable: $barfoo', - 'range' => new Range( - new Position(1, 1), - new Position(1, 1) - ), - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'phpstan' - ]); - } -} diff --git a/lib/Extension/LanguageServerPsalm/DiagnosticProvider/PsalmDiagnosticProvider.php b/lib/Extension/LanguageServerPsalm/DiagnosticProvider/PsalmDiagnosticProvider.php deleted file mode 100644 index 3350f9388d..0000000000 --- a/lib/Extension/LanguageServerPsalm/DiagnosticProvider/PsalmDiagnosticProvider.php +++ /dev/null @@ -1,27 +0,0 @@ -linter->lint($textDocument->uri, $textDocument->text); - } - - public function name(): string - { - return 'psalm'; - } -} diff --git a/lib/Extension/LanguageServerPsalm/LanguageServerPsalmExtension.php b/lib/Extension/LanguageServerPsalm/LanguageServerPsalmExtension.php deleted file mode 100644 index 04f44ca9c9..0000000000 --- a/lib/Extension/LanguageServerPsalm/LanguageServerPsalmExtension.php +++ /dev/null @@ -1,112 +0,0 @@ -register(PsalmDiagnosticProvider::class, function (Container $container) { - return new PsalmDiagnosticProvider( - $container->get(Linter::class) - ); - }, [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('psalm'), - ]); - - $container->register(Linter::class, function (Container $container) { - return new PsalmLinter($container->get(PsalmProcess::class)); - }); - - $container->register(PsalmProcess::class, function (Container $container) { - $resolver = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - $binPath = $resolver->resolve($container->parameter(self::PARAM_PSALM_BIN)->string()); - $configPath = $resolver->resolve($container->parameter(self::PARAM_PSALM_CONFIG)->string()); - $root = $resolver->resolve('%project_root%'); - $shouldShowInfo = $container->parameter(self::PARAM_PSALM_SHOW_INFO)->bool(); - $useCache = $container->parameter(self::PARAM_PSALM_USE_CACHE)->bool(); - $errorLevel = $container->parameter(self::PARAM_PSALM_ERROR_LEVEL)->value(); - $threads = $container->parameter(self::PARAM_PSALM_THREADS)->value(); - if (!is_null($errorLevel) && !is_int($errorLevel)) { - $errorLevel = null; - } - if (!is_null($threads) && !is_int($threads)) { - $threads = null; - } - - return new PsalmProcess( - cwd: $root, - config: new PsalmConfig( - $binPath, - $shouldShowInfo, - $useCache, - $errorLevel ? (int)$errorLevel : null, - $threads ? (int)$threads : null, - $configPath === '' ? null : $configPath, - ), - logger: LoggingExtension::channelLogger($container, 'PSALM'), - timeoutSeconds: $container->parameter(self::PARAM_TIMEOUT)->int(), - ); - }); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_PSALM_BIN => '%project_root%/vendor/bin/psalm', - self::PARAM_PSALM_CONFIG => '', - self::PARAM_PSALM_SHOW_INFO => true, - self::PARAM_PSALM_USE_CACHE => true, - self::PARAM_PSALM_ERROR_LEVEL => null, - self::PARAM_PSALM_THREADS => 1, - self::PARAM_TIMEOUT => 15, - ]); - $schema->setTypes([ - self::PARAM_PSALM_BIN => 'string', - self::PARAM_PSALM_CONFIG => 'string', - self::PARAM_PSALM_SHOW_INFO => 'boolean', - self::PARAM_PSALM_USE_CACHE => 'boolean', - self::PARAM_TIMEOUT => 'integer', - self::PARAM_PSALM_THREADS => 'integer', - ]); - $schema->setDescriptions([ - self::PARAM_PSALM_BIN => 'Path to psalm if different from vendor/bin/psalm', - self::PARAM_PSALM_CONFIG => 'Path to psalm config. Like %project_root%/psalm.xml', - self::PARAM_PSALM_SHOW_INFO => 'If infos from psalm should be displayed', - self::PARAM_PSALM_USE_CACHE => 'If the Psalm cache should be used (see the `--no-cache` option)', - self::PARAM_PSALM_ERROR_LEVEL => 'Override level at which Psalm should report errors (lower => more errors)', - self::PARAM_PSALM_THREADS => 'Set the number of threads Psalm should use. Warning: NULL will use as many as possible and may crash your computer', - self::PARAM_TIMEOUT => 'Kill the psalm process after this number of seconds', - ]); - } - - public function name(): string - { - return 'language_server_psalm'; - } -} diff --git a/lib/Extension/LanguageServerPsalm/LanguageServerPsalmSuggestExtension.php b/lib/Extension/LanguageServerPsalm/LanguageServerPsalmSuggestExtension.php deleted file mode 100644 index 11b9bbda2e..0000000000 --- a/lib/Extension/LanguageServerPsalm/LanguageServerPsalmSuggestExtension.php +++ /dev/null @@ -1,50 +0,0 @@ -register('language_server_psalm.suggest', function (Container $container) { - return new PhpactorComposerSuggestor( - $container->expect(ConfigurationExtension::SERVICE_PHPACTOR_CONFIG_LOCAL, JsonConfig::class), - $container->get(ComposerInspector::class), - function (JsonConfig $config, ComposerInspector $inspector) { - if ($config->has(LanguageServerPsalmExtension::PARAM_ENABLED)) { - return Changes::none(); - } - - if (!$inspector->package('vimeo/psalm')) { - return Changes::none(); - } - - return Changes::from([ - new PhpactorConfigChange('Psalm detected, enable the Psalm extension?', function (bool $enable) { - return [ - LanguageServerPsalmExtension::PARAM_ENABLED => $enable, - ]; - }) - ]); - } - ); - }, [ - ConfigurationExtension::TAG_SUGGESTOR => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerPsalm/Model/DiagnosticsParser.php b/lib/Extension/LanguageServerPsalm/Model/DiagnosticsParser.php deleted file mode 100644 index e874b04a61..0000000000 --- a/lib/Extension/LanguageServerPsalm/Model/DiagnosticsParser.php +++ /dev/null @@ -1,98 +0,0 @@ - - */ - public function parse(string $jsonString, string $filename): array - { - $decoded = $this->decodeJson($jsonString); - $diagnostics = []; - - foreach ($decoded as $psalmDiagnostic) { - if ($psalmDiagnostic['file_path'] !== $filename) { - continue; - } - - $diagnostics[] = Diagnostic::fromArray([ - 'message' => $psalmDiagnostic['message'], - 'range' => new Range( - new Position($psalmDiagnostic['line_from'] - 1, $psalmDiagnostic['column_from'] - 1), - new Position($psalmDiagnostic['line_to'] - 1, $psalmDiagnostic['column_to'] - 1) - ), - 'severity' => $this->errorLevel($psalmDiagnostic), - 'source' => 'psalm' - ]); - } - - return $diagnostics; - } - - /** - * @return array - */ - private function decodeJson(string $jsonString): array - { - - try { - /** @var array $decoded */ - $decoded = json_decode($jsonString, true, flags: JSON_THROW_ON_ERROR); - return $decoded; - } catch (JsonException $e) { - throw new RuntimeException(sprintf( - 'Could not decode Psalm JSON output "%s": %s', - $jsonString, - $e->getMessage() - )); - } - } - - /** - * @param PsalmDiagnostic $psalmDiagnostic - */ - private function errorLevel(array $psalmDiagnostic): int - { - switch ($psalmDiagnostic['severity']) { - case 'error': - return DiagnosticSeverity::ERROR; - case 'info': - return DiagnosticSeverity::WARNING; - } - - return DiagnosticSeverity::INFORMATION; - } -} diff --git a/lib/Extension/LanguageServerPsalm/Model/Linter.php b/lib/Extension/LanguageServerPsalm/Model/Linter.php deleted file mode 100644 index 92dc92aa15..0000000000 --- a/lib/Extension/LanguageServerPsalm/Model/Linter.php +++ /dev/null @@ -1,14 +0,0 @@ -> - */ - public function lint(string $url, ?string $text): Promise; -} diff --git a/lib/Extension/LanguageServerPsalm/Model/Linter/PsalmLinter.php b/lib/Extension/LanguageServerPsalm/Model/Linter/PsalmLinter.php deleted file mode 100644 index 8712bb3f2a..0000000000 --- a/lib/Extension/LanguageServerPsalm/Model/Linter/PsalmLinter.php +++ /dev/null @@ -1,35 +0,0 @@ -doLint($url, $text); - - return $diagnostics; - }); - } - - /** - * @return Generator>> - */ - private function doLint(string $url, ?string $text): Generator - { - return yield $this->process->analyse(TextDocumentUri::fromString($url)->path()); - } -} diff --git a/lib/Extension/LanguageServerPsalm/Model/Linter/TestLinter.php b/lib/Extension/LanguageServerPsalm/Model/Linter/TestLinter.php deleted file mode 100644 index d69b500cc3..0000000000 --- a/lib/Extension/LanguageServerPsalm/Model/Linter/TestLinter.php +++ /dev/null @@ -1,29 +0,0 @@ - $diagnostics - */ - public function __construct( - private array $diagnostics, - private int $delay - ) { - } - - public function lint(string $url, ?string $text): Promise - { - return call(function () { - yield new Delayed($this->delay); - return $this->diagnostics; - }); - } -} diff --git a/lib/Extension/LanguageServerPsalm/Model/PsalmConfig.php b/lib/Extension/LanguageServerPsalm/Model/PsalmConfig.php deleted file mode 100644 index 5dec7f3420..0000000000 --- a/lib/Extension/LanguageServerPsalm/Model/PsalmConfig.php +++ /dev/null @@ -1,46 +0,0 @@ -threads; - } - - public function psalmBin(): string - { - return $this->phpstanBin; - } - - public function shouldShowInfo(): bool - { - return $this->shouldShowInfo; - } - - public function useCache(): bool - { - return $this->useCache; - } - - public function errorLevel(): ?int - { - return $this->errorLevel; - } - - public function config(): ?string - { - return $this->config; - } -} diff --git a/lib/Extension/LanguageServerPsalm/Model/PsalmProcess.php b/lib/Extension/LanguageServerPsalm/Model/PsalmProcess.php deleted file mode 100644 index f5aeb37999..0000000000 --- a/lib/Extension/LanguageServerPsalm/Model/PsalmProcess.php +++ /dev/null @@ -1,105 +0,0 @@ -> - */ - public function analyse(string $filename): Promise - { - return call(function () use ($filename) { - $command = [ - PHP_BINARY, - $this->config->psalmBin(), - sprintf( - '--show-info=%s', - $this->config->shouldShowInfo() ? 'true' : 'false', - ), - '--output-format=json', - ]; - - $command = (function (array $command, ?int $errorLevel) { - if (null === $errorLevel) { - return $command; - } - $command[] = sprintf('--error-level=%d', $errorLevel); - return $command; - })($command, $this->config->errorLevel()); - - $command = (function (array $command, ?int $threads) { - if (null === $threads) { - return $command; - } - $command[] = sprintf('--threads=%d', $threads); - return $command; - })($command, $this->config->threads()); - - $command = (function (array $command, ?string $config) { - if (null === $config) { - return $command; - } - $command[] = "--config=$config"; - return $command; - })($command, $this->config->config()); - - if (!$this->config->useCache()) { - $command[] = '--no-cache'; - } - $command[] = $filename; - - $process = new Process($command, $this->cwd); - - $start = microtime(true); - $pid = yield $process->start(); - - ProcessUtil::killAfter($this->logger, $process, $this->timeoutSeconds); - - try { - $exitCode = yield $process->join(); - } catch (ProcessException $e) { - return []; - } - - if ($exitCode !== 0 && $exitCode !== 2) { - throw new RuntimeException(sprintf( - 'Psalm exited with code "%s": %s', - $exitCode, - yield buffer($process->getStderr()) - )); - } - - $stdout = yield buffer($process->getStdout()); - - $this->logger->debug(sprintf( - 'Psalm completed in %s: %s in %s ... checking for %s', - number_format(microtime(true) - $start, 4), - $process->getCommand(), - $process->getWorkingDirectory(), - $filename - )); - - return $this->parser->parse($stdout, $filename); - }); - } -} diff --git a/lib/Extension/LanguageServerPsalm/Tests/DiagnosticProvider/PsalmDiagnosticProviderTest.php b/lib/Extension/LanguageServerPsalm/Tests/DiagnosticProvider/PsalmDiagnosticProviderTest.php deleted file mode 100644 index 0fee76b7f7..0000000000 --- a/lib/Extension/LanguageServerPsalm/Tests/DiagnosticProvider/PsalmDiagnosticProviderTest.php +++ /dev/null @@ -1,50 +0,0 @@ -addDiagnosticsProvider(new PsalmDiagnosticProvider( - $this->createTestLinter() - )); - $tester->enableDiagnostics(); - $tester->enableTextDocuments(); - $this->tester = $tester->build(); - $this->tester->initialize(); - } - - public function testHandleSingle(): void - { - $updated = new TextDocumentUpdated(ProtocolFactory::versionedTextDocumentIdentifier('file:///path', 12), 'asd'); - $this->tester->textDocument()->open('file:///path', 'asd'); - - wait(delay(10)); - - self::assertEquals(2, $this->tester->transmitter()->count()); - } - - private function createTestLinter(): TestLinter - { - return new TestLinter([ - DiagnosticBuilder::create()->build(), - ], 10); - } -} diff --git a/lib/Extension/LanguageServerPsalm/Tests/IntegrationTestCase.php b/lib/Extension/LanguageServerPsalm/Tests/IntegrationTestCase.php deleted file mode 100644 index 11af505c17..0000000000 --- a/lib/Extension/LanguageServerPsalm/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,19 +0,0 @@ -workspace()->reset(); - } - - protected static function workspace(): Workspace - { - return Workspace::create(__DIR__ . '/Workspace'); - } -} diff --git a/lib/Extension/LanguageServerPsalm/Tests/Model/DiagnosticsParserTest.php b/lib/Extension/LanguageServerPsalm/Tests/Model/DiagnosticsParserTest.php deleted file mode 100644 index 94f8a716aa..0000000000 --- a/lib/Extension/LanguageServerPsalm/Tests/Model/DiagnosticsParserTest.php +++ /dev/null @@ -1,38 +0,0 @@ -parse($psalmJson, '/path/to.php')); - } - - /** - * @return Generator - */ - public static function provideParse(): Generator - { - yield [ - <<<'EOT' - [{"severity":"info","line_from":49,"line_to":49,"type":"TooManyArguments","message":"Too many arguments for Phpactor\\Extension\\LanguageServerPsalm\\Model\\PsalmConfig::__construct - expecting 1 but saw 2","file_name":"lib\/LanguageServerPhpstanExtension.php","file_path":"\/path\/to.php","snippet":" new PsalmConfig($binPath, $container->getParameter(self::PARAM_LEVEL)),","selected_text":"new PsalmConfig($binPath, $container->getParameter(self::PARAM_LEVEL))","from":2040,"to":2110,"snippet_from":2024,"snippet_to":2111,"column_from":17,"column_to":87,"error_level":4,"shortcode":26,"link":"https:\/\/psalm.dev\/026","taint_trace":null}] - EOT - , 1 - ]; - } - - public function testExceptionOnNonJsonString(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('stdout was not JSON'); - (new DiagnosticsParser())->parse('stdout was not JSON', '/path/to.php'); - } -} diff --git a/lib/Extension/LanguageServerPsalm/Tests/Model/PsalmProcessTest.php b/lib/Extension/LanguageServerPsalm/Tests/Model/PsalmProcessTest.php deleted file mode 100644 index 87b610e7d6..0000000000 --- a/lib/Extension/LanguageServerPsalm/Tests/Model/PsalmProcessTest.php +++ /dev/null @@ -1,223 +0,0 @@ -workspace()->reset(); - } - - /** - * @param Closure(list):void $assertion - */ - #[DataProvider('provideLint')] - public function testLint(string $source, Closure $assertion, int $initLevel = 1, bool $shouldShowInfo = true, ?int $errorLevel = null): void - { - $psalmBin = __DIR__ . '/../../../../../vendor/bin/psalm.phar'; - - // without a src dir, psalm crashes - $this->workspace()->mkdir('src'); - - $this->workspace()->put( - 'composer.json', - <<<'EOT' - { - "name": "test/project", - "autoload": { - "psr-4": { - "Phpactor\\Extension\\LanguageServerPsalm\\": "/" - } - } - } - EOT - ); - (Process::fromShellCommandline('composer dump', $this->workspace()->path()))->mustRun(); - - (new Process([PHP_BINARY, $psalmBin, '--init', 'src', $initLevel], $this->workspace()->path()))->mustRun(); - $this->workspace()->put('src/test.php', $source); - $linter = new PsalmProcess( - $this->workspace()->path(), - new PsalmConfig( - phpstanBin: $psalmBin, - shouldShowInfo: $shouldShowInfo, - useCache: false, - errorLevel: $errorLevel, - ), - new NullLogger(), - timeoutSeconds: 15, - ); - - $diagnostics = wait($linter->analyse($this->workspace()->path('src/test.php'))); - $assertion($diagnostics); - } - - /** - * @return Generator - */ - public static function provideLint(): Generator - { - yield [ - ' new Range( - new Position(0, 5), - new Position(0, 12) - ), - 'message' => 'Unable to determine the type that $foobar is being assigned to', - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'psalm', - ]), - Diagnostic::fromArray([ - 'range' => new Range( - new Position(0, 5), - new Position(0, 12) - ), - 'message' => '$foobar is never referenced or the value is not used', - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'psalm', - ]), - Diagnostic::fromArray([ - 'range' => new Range( - new Position(0, 15), - new Position(0, 22) - ), - 'message' => 'Cannot find referenced variable $barfoo in global scope', - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'psalm', - ]), - ], - $diagnostics - ); - }, - ]; - - yield [ - ' new Range( - new Position(0, 5), - new Position(0, 12) - ), - 'message' => '$foobar is never referenced or the value is not used', - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'psalm', - ]), - Diagnostic::fromArray([ - 'range' => new Range( - new Position(0, 15), - new Position(0, 22) - ), - 'message' => 'Cannot find referenced variable $barfoo in global scope', - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'psalm', - ]), - Diagnostic::fromArray([ - 'range' => new Range( - new Position(0, 5), - new Position(0, 12) - ), - 'message' => 'Unable to determine the type that $foobar is being assigned to', - 'severity' => DiagnosticSeverity::WARNING, - 'source' => 'psalm', - ]), - ], - $diagnostics - ); - }, - 2, - true, - ]; - - yield 'should not show info' => [ - ' new Range( - new Position(0, 5), - new Position(0, 12) - ), - 'message' => '$foobar is never referenced or the value is not used', - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'psalm', - ]), - Diagnostic::fromArray([ - 'range' => new Range( - new Position(0, 15), - new Position(0, 22) - ), - 'message' => 'Cannot find referenced variable $barfoo in global scope', - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'psalm', - ]), - ], - $diagnostics - ); - }, - 2, - false, - ]; - - yield 'do not override error level' => [ - 'foo(); } ', - function (array $diagnostics): void { - self::assertCount(0, $diagnostics); - }, - 7, - false, - ]; - - yield 'override error level' => [ - 'foo(); } ', - function (array $diagnostics): void { - self::assertCount(3, $diagnostics); - }, - 7, - false, - 1, - ]; - } - /** - * @param list $expectedDiagnostics - * @param list $diagnostics - */ - private static function assertDiagnostics(array $expectedDiagnostics, $diagnostics): void - { - usort($diagnostics, fn (Diagnostic $a, Diagnostic $b) => strcasecmp($a->message, $b->message)); - usort($expectedDiagnostics, fn (Diagnostic $a, Diagnostic $b) => strcasecmp($a->message, $b->message)); - self::assertEquals($expectedDiagnostics, $diagnostics); - } -} diff --git a/lib/Extension/LanguageServerPsalm/Tests/Util/DiagnosticBuilder.php b/lib/Extension/LanguageServerPsalm/Tests/Util/DiagnosticBuilder.php deleted file mode 100644 index a7552bdafb..0000000000 --- a/lib/Extension/LanguageServerPsalm/Tests/Util/DiagnosticBuilder.php +++ /dev/null @@ -1,29 +0,0 @@ - 'Undefined variable: $barfoo', - 'range' => new Range( - new Position(1, 1), - new Position(1, 1) - ), - 'severity' => DiagnosticSeverity::ERROR, - 'source' => 'phpstan' - ]); - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Adapter/Indexer/WorkspaceUpdateReferenceFinder.php b/lib/Extension/LanguageServerReferenceFinder/Adapter/Indexer/WorkspaceUpdateReferenceFinder.php deleted file mode 100644 index b48308343c..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Adapter/Indexer/WorkspaceUpdateReferenceFinder.php +++ /dev/null @@ -1,63 +0,0 @@ - - */ - private array $documentVersions = []; - - private int $counter = 0; - - public function __construct( - private Workspace $workspace, - private Indexer $indexer, - private ReferenceFinder $innerReferenceFinder - ) { - } - - public function findReferences(TextDocument $document, ByteOffset $byteOffset): Generator - { - $this->indexWorkspace(); - - $generator = $this->innerReferenceFinder->findReferences($document, $byteOffset); - yield from $generator; - return $generator->getReturn(); - } - - private function indexWorkspace(): void - { - // put an upper limit on the size of the cache - if ($this->counter++ === 1_000) { - $this->documentVersions = []; - } - - // ensure that the index is current with the workspace - foreach ($this->workspace as $document) { - - // avoid reindexing documents that have not changed - if (($this->documentVersions[$document->uri] ?? null) === $document->version) { - continue; - } - $this->documentVersions[$document->uri] = $document->version; - - try { - $this->indexer->indexDirty( - TextDocumentBuilder::fromUri($document->uri)->text($document->text)->build() - ); - } catch (TextDocumentNotFound) { - } - } - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Handler/GotoDefinitionHandler.php b/lib/Extension/LanguageServerReferenceFinder/Handler/GotoDefinitionHandler.php deleted file mode 100644 index 128718a837..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Handler/GotoDefinitionHandler.php +++ /dev/null @@ -1,89 +0,0 @@ - 'definition', - ]; - } - - /** - * @return Promise - */ - public function definition(DefinitionParams $params): Promise - { - return call(function () use ($params) { - $textDocument = $this->workspace->get($params->textDocument->uri); - - $offset = PositionConverter::positionToByteOffset($params->position, $textDocument->text); - - try { - $typeLocations = $this->definitionLocator->locateDefinition( - TextDocumentBuilder::create( - $textDocument->text - )->uri($textDocument->uri)->language( - $textDocument->languageId, - )->build(), - $offset - ); - } catch (CouldNotLocateDefinition) { - return null; - } - - if ($typeLocations->count() === 1) { - return $this->locationConverter->toLspLocation($typeLocations->first()->location()); - } - - $actions = []; - foreach ($typeLocations as $typeLocation) { - $actions[] = new MessageActionItem(sprintf('%s', $typeLocation->type()->__toString())); - } - - $item = yield $this->clientApi->window()->showMessageRequest()->info('Goto type', ...$actions); - - if (!$item instanceof MessageActionItem) { - throw new CouldNotLocateType( - 'Client did not return an action item' - ); - } - - return $this->locationConverter->toLspLocation( - $typeLocations->byTypeName($item->title)->location() - ); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->definitionProvider = true; - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Handler/GotoImplementationHandler.php b/lib/Extension/LanguageServerReferenceFinder/Handler/GotoImplementationHandler.php deleted file mode 100644 index cc80e7ca58..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Handler/GotoImplementationHandler.php +++ /dev/null @@ -1,60 +0,0 @@ - 'gotoImplementation', - ]; - } - - public function gotoImplementation(ImplementationParams $params): Promise - { - return call(function () use ($params) { - $textDocument = $this->workspace->get($params->textDocument->uri); - $phpactorDocument = TextDocumentBuilder::create( - $textDocument->text - )->uri( - $textDocument->uri - )->language( - $textDocument->languageId ?? 'php' - )->build(); - - $offset = PositionConverter::positionToByteOffset($params->position, $textDocument->text); - $locations = $this->finder->findImplementations( - $phpactorDocument, - $offset - ); - - return $this->locationConverter->toLspLocations($locations); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->implementationProvider = true; - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Handler/HighlightHandler.php b/lib/Extension/LanguageServerReferenceFinder/Handler/HighlightHandler.php deleted file mode 100644 index dd521195fb..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Handler/HighlightHandler.php +++ /dev/null @@ -1,52 +0,0 @@ - 'highlight', - ]; - } - - /** - * @return Promise|null> - */ - public function highlight(DocumentHighlightParams $params): Promise - { - $textDocument = $this->workspace->get($params->textDocument->uri); - $offset = PositionConverter::positionToByteOffset($params->position, $textDocument->text); - - return call(function () use ($textDocument, $offset) { - $document = TextDocumentConverter::fromLspTextItem($textDocument); - return (yield $this->highlighter->highlightsFor($document, $offset))->toArray(); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->documentHighlightProvider = true; - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Handler/ReferencesHandler.php b/lib/Extension/LanguageServerReferenceFinder/Handler/ReferencesHandler.php deleted file mode 100644 index cb451e779f..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Handler/ReferencesHandler.php +++ /dev/null @@ -1,175 +0,0 @@ - 'references', - ]; - } - - /** - * @return Promise> - */ - public function references( - TextDocumentIdentifier $textDocument, - Position $position, - ReferenceContext $context - ): Promise { - return call(function () use ($textDocument, $position, $context) { - $textDocument = $this->workspace->get($textDocument->uri); - $phpactorDocument = TextDocumentBuilder::create( - $textDocument->text - )->uri( - $textDocument->uri - )->language( - $textDocument->languageId - )->build(); - - $offset = PositionConverter::positionToByteOffset($position, $textDocument->text); - - $locations = []; - if ($context->includeDeclaration) { - try { - $potentialLocation = $this->definitionLocator->locateDefinition($phpactorDocument, $offset)->first()->location(); - $locations[] = new Location($potentialLocation->uri(), $potentialLocation->range()); - } catch (CouldNotLocateDefinition) { - } - } - - $token = WorkDoneToken::generate(); - $this->clientApi->workDoneProgress()->begin($token, 'Finding references'); - $start = microtime(true); - $count = 0; - $risky = 0; - $dontAsk = false; - foreach ($this->finder->findReferences($phpactorDocument, $offset) as $potentialLocation) { - if ($potentialLocation->isSurely()) { - $locations[] = $potentialLocation->location(); - } - - if ($potentialLocation->isMaybe()) { - $risky++; - } - - $count++; - $this->clientApi->workDoneProgress()->report($token, sprintf( - '... analysed %s references confirmed %s ...', - $count - 1, - count($locations) - )); - - if (false === $dontAsk && microtime(true) - $start > $this->softTimeoutSeconds) { - $no = new MessageActionItem('No, show me what you got'); - $another = new MessageActionItem(sprintf('Another %.2f seconds', $this->softTimeoutSeconds)); - $until = new MessageActionItem(sprintf('Keep going until the %.2f second hard timeout', $this->timeoutSeconds)); - $selection = yield $this->clientApi->window()->showMessageRequest()->info( - sprintf( - 'Finding references is taking a while, scanned %d and confirmed %d - do you want to continue?', - $count - 1, - count($locations), - ), - $no, - $another, - $until, - ); - if ($selection == $no) { - break; - } - if ($selection == $another) { - $this->clientApi->workDoneProgress()->report( - $token, - sprintf('searching for another %.2f seconds', $this->softTimeoutSeconds) - ); - $start = microtime(true); - continue; - } - if ($selection == $until) { - $dontAsk = true; - } - } - - if (microtime(true) - $start > $this->timeoutSeconds) { - $this->clientApi->workDoneProgress()->end( - $token, - sprintf( - 'Reference finding stopped, %s/%s references confirmed but took too long (%s/%s seconds). Adjust `%s`', - count($locations), - $count, - number_format(microtime(true) - $start, 2), - $this->timeoutSeconds, - LanguageServerReferenceFinderExtension::PARAM_REFERENCE_TIMEOUT - ) - ); - return $this->toLocations($locations); - } - - if ($count % 10) { - // give other co-routines a chance - yield new Delayed(0); - } - } - - $this->clientApi->workDoneProgress()->end($token, sprintf( - 'Found %s reference(s)%s', - count($locations), - $risky ? sprintf(' %s unresolvable references excluded', $risky) : '' - )); - - return $this->toLocations($locations); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->referencesProvider = true; - } - - /** - * @param array $ranges - * @return LspLocation[] - */ - private function toLocations(array $ranges): array - { - return $this->locationConverter->toLspLocations((new Locations($ranges))->sorted()); - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Handler/TypeDefinitionHandler.php b/lib/Extension/LanguageServerReferenceFinder/Handler/TypeDefinitionHandler.php deleted file mode 100644 index e2033b8957..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Handler/TypeDefinitionHandler.php +++ /dev/null @@ -1,88 +0,0 @@ - - */ - public function methods(): array - { - return [ - 'textDocument/typeDefinition' => 'type', - ]; - } - - /** - * @return Promise - */ - public function type( - TextDocumentIdentifier $textDocument, - Position $position - ): Promise { - return call(function () use ($textDocument, $position) { - $textDocument = $this->workspace->get($textDocument->uri); - - $offset = PositionConverter::positionToByteOffset($position, $textDocument->text); - - try { - $typeLocations = $this->typeLocator->locateTypes( - TextDocumentBuilder::create($textDocument->text)->uri($textDocument->uri)->language('php')->build(), - $offset - ); - } catch (CouldNotLocateType) { - return null; - } - - if ($typeLocations->count() === 1) { - return $this->locationConverter->toLspLocation($typeLocations->first()->location()); - } - - $actions = []; - foreach ($typeLocations as $typeLocation) { - $actions[] = new MessageActionItem(sprintf('%s', $typeLocation->type()->__toString())); - } - - $item = yield $this->client->window()->showMessageRequest()->info('Goto type', ...$actions); - - if (!$item instanceof MessageActionItem) { - return null; - } - - return $this->locationConverter->toLspLocation( - $typeLocations->byTypeName($item->title)->location() - ); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->typeDefinitionProvider = true; - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php b/lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php deleted file mode 100644 index 7199c603e0..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php +++ /dev/null @@ -1,88 +0,0 @@ -register(GotoDefinitionHandler::class, function (Container $container) { - return new GotoDefinitionHandler( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(ReferenceFinderExtension::SERVICE_DEFINITION_LOCATOR), - $container->get(LocationConverter::class), - $container->get(ClientApi::class), - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => [] ]); - - $container->register(TypeDefinitionHandler::class, function (Container $container) { - return new TypeDefinitionHandler( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(ReferenceFinderExtension::SERVICE_TYPE_LOCATOR), - $container->get(LocationConverter::class), - $container->get(ClientApi::class), - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => [] ]); - - $container->register(WorkspaceUpdateReferenceFinder::class, function (Container $container) { - return new WorkspaceUpdateReferenceFinder( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(Indexer::class), - $container->get(ReferenceFinder::class), - ); - }); - - $container->register(ReferencesHandler::class, function (Container $container) { - return new ReferencesHandler( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(WorkspaceUpdateReferenceFinder::class), - $container->get(ReferenceFinderExtension::SERVICE_DEFINITION_LOCATOR), - $container->get(LocationConverter::class), - $container->get(ClientApi::class), - $container->parameter(self::PARAM_REFERENCE_TIMEOUT)->int(), - $container->parameter(self::PARAM_REFERENCE_SOFT_TIMEOUT)->int(), - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => [] ]); - - $container->register(GotoImplementationHandler::class, function (Container $container) { - return new GotoImplementationHandler( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(ReferenceFinderExtension::SERVICE_IMPLEMENTATION_FINDER), - $container->get(LocationConverter::class) - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => [] ]); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_REFERENCE_TIMEOUT => 60, - self::PARAM_REFERENCE_SOFT_TIMEOUT => 10, - ]); - $schema->setDescriptions([ - self::PARAM_REFERENCE_TIMEOUT => 'Stop searching for references after this time (in seconds) has expired', - self::PARAM_REFERENCE_SOFT_TIMEOUT => 'Interupt and ask for confirmation to continue after this timeout (in seconds)', - ]); - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Model/Highlight.php b/lib/Extension/LanguageServerReferenceFinder/Model/Highlight.php deleted file mode 100644 index 19c52da154..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Model/Highlight.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ - public function highlightsFor(TextDocument $source, ByteOffset $offset): Promise - { - // ensure we only process one inlay request at a time - if ($this->previousCancellationSource) { - $this->previousCancellationSource->cancel(); - } - $cancellationSource = new CancellationTokenSource(); - $this->previousCancellationSource = $cancellationSource; - $cancellation = $cancellationSource->getToken(); - - return call(function () use ($source, $offset, $cancellation) { - $offsets = []; - $highlights = []; - foreach ($this->generate($source, $offset) as $highlight) { - yield delay(1); - if ($cancellation->isRequested()) { - return new Highlights(); - } - $offsets[] = $highlight->start; - $offsets[] = $highlight->end; - $highlights[] = $highlight; - } - - $lineCols = EfficientLineCols::fromByteOffsetInts($source, $offsets, true); - $lspHighlights = []; - - foreach ($highlights as $highlight) { - $startPos = $lineCols->get($highlight->start); - $endPos = $lineCols->get($highlight->end); - $lspHighlights[] = new DocumentHighlight( - new Range( - new Position($startPos->line() - 1, $startPos->col() - 1), - new Position($endPos->line() - 1, $endPos->col() - 1), - ), - $highlight->kind - ); - } - return new Highlights(...$lspHighlights); - }); - } - - /** - * @return Generator - */ - private function generate(TextDocument $source, ByteOffset $offset): Generator - { - $rootNode = $this->parser->get($source); - $node = $rootNode->getDescendantNodeAtPosition($offset->toInt()); - - if ($node instanceof Variable && $node->getFirstAncestor(PropertyDeclaration::class)) { - yield from $this->properties($rootNode, (string)$node->getName()); - return; - } - - if ($node instanceof Parameter) { - yield from (null === $node->visibilityToken) - ? $this->variables($rootNode, (string)$node->getName()) - : $this->properties($rootNode, (string)$node->getName()) - ; - return; - } - - if ($node instanceof Variable) { - yield from $this->variables($rootNode, (string)$node->getName()); - return; - } - - if ($node instanceof MethodDeclaration) { - yield from $this->methods($rootNode, $node->getName()); - return; - } - - if ($node instanceof ClassDeclaration) { - yield from $this->namespacedNames($rootNode, (string)$node->getNamespacedName()); - return; - } - - if ($node instanceof ConstElement) { - yield from $this->constants($rootNode, (string)$node->getNamespacedName()); - return; - } - - if ($node instanceof QualifiedName) { - yield from $this->namespacedNames($rootNode, (string)$node->getResolvedName() ?: (string)$node->getNamespacedName()); - return; - } - - if ($node instanceof ScopedPropertyAccessExpression) { - $memberName = $node->memberName; - if (!$memberName instanceof Token) { - return; - } - yield from $this->memberAccess($rootNode, $node, (string)$memberName->getText($rootNode->getFileContents())); - return; - } - - if ($node instanceof MemberAccessExpression) { - yield from $this->memberAccess($rootNode, $node, (string)$node->memberName->getText($rootNode->getFileContents())); - return; - } - - return; - } - - /** - * @return Generator - */ - private function variables(SourceFileNode $rootNode, string $name): Generator - { - $name = $this->normalizeVarName($name); - foreach ($rootNode->getDescendantNodes() as $childNode) { - if ($childNode instanceof Variable && $childNode->getName() === $name) { - yield new Highlight( - $childNode->getStartPosition(), - $childNode->getEndPosition(), - $this->variableKind($childNode) - ); - } - - if ($childNode instanceof Parameter && $this->normalizeVarName((string)$childNode->variableName->getText($childNode->getFileContents())) === $name) { - yield new Highlight( - $childNode->variableName->getStartPosition(), - $childNode->variableName->getEndPosition(), - DocumentHighlightKind::READ, - ); - } - } - } - - /** - * @return DocumentHighlightKind::* - * @phpstan-ignore-next-line - */ - private function variableKind(Node $node): int - { - $expression = $node->parent; - if ($expression instanceof AssignmentExpression) { - if ($expression->leftOperand === $node) { - return DocumentHighlightKind::WRITE; - } - } - - return DocumentHighlightKind::READ; - } - - /** - * @return Generator - */ - private function properties(Node $rootNode, string $name): Generator - { - foreach ($rootNode->getDescendantNodes() as $node) { - if ($node instanceof Parameter && null !== $node->visibilityToken && (string)$node->getName() === $name) { - yield new Highlight( - $node->variableName->getStartPosition(), - $node->variableName->getEndPosition(), - DocumentHighlightKind::TEXT, - ); - continue; - } - - if ($node instanceof Variable && $node->getFirstAncestor(PropertyDeclaration::class) && (string)$node->getName() === $name) { - yield new Highlight( - $node->getStartPosition(), - $node->getEndPosition(), - DocumentHighlightKind::TEXT, - ); - } - - if ($node instanceof MemberAccessExpression) { - if ($name === $node->memberName->getText($rootNode->getFileContents())) { - yield new Highlight( - $node->memberName->getStartPosition(), - $node->memberName->getEndPosition(), - $this->variableKind($node), - ); - } - } - } - } - - /** - * @return Generator - */ - private function memberAccess(SourceFileNode $rootNode, Node $node, string $memberName): Generator - { - if ($node->parent instanceof CallExpression) { - return yield from $this->methods($rootNode, $memberName); - } - - if (str_contains($node->getText(), '$')) { - return yield from $this->properties($rootNode, $memberName); - } - - return yield from $this->constants($rootNode, $memberName); - } - - /** - * @return Generator - */ - private function methods(SourceFileNode $rootNode, string $name): Generator - { - foreach ($rootNode->getDescendantNodes() as $node) { - if ($node instanceof MethodDeclaration && $node->getName() === $name) { - yield new Highlight( - $node->name->getStartPosition(), - $node->name->getEndPosition(), - DocumentHighlightKind::TEXT, - ); - } - if ($node instanceof MemberAccessExpression) { - if ($name === $node->memberName->getText($rootNode->getFileContents())) { - yield new Highlight( - $node->memberName->getStartPosition(), - $node->memberName->getEndPosition(), - $this->variableKind($node) - ); - } - } - if ($node instanceof ScopedPropertyAccessExpression) { - $memberName = $node->memberName; - if (!$memberName instanceof Token) { - return; - } - if ($name === $memberName->getText($rootNode->getFileContents())) { - yield new Highlight( - $memberName->getStartPosition(), - $memberName->getEndPosition(), - $this->variableKind($node) - ); - } - } - } - } - - /** - * @return Generator - */ - private function constants(SourceFileNode $rootNode, string $name): Generator - { - foreach ($rootNode->getDescendantNodes() as $node) { - if ($node instanceof ConstElement && (string)$node->getNamespacedName() === $name) { - yield new Highlight( - $node->name->getStartPosition(), - $node->name->getEndPosition(), - DocumentHighlightKind::TEXT - ); - } - if ($node instanceof ScopedPropertyAccessExpression) { - $memberName = $node->memberName; - if (!$memberName instanceof Token) { - return; - } - if ($name === $memberName->getText($rootNode->getFileContents())) { - yield new Highlight( - $memberName->getStartPosition(), - $memberName->getEndPosition(), - $this->variableKind($node) - ); - } - } - } - } - - /** - * @return Generator - */ - private function namespacedNames(Node $rootNode, string $fullyQualfiedName): Generator - { - foreach ($rootNode->getDescendantNodes() as $node) { - if ($node instanceof NamespaceUseClause && (string) $node->namespaceName === $fullyQualfiedName) { - $nameParts = $node->namespaceName->nameParts; - $name = end($nameParts); - - yield new Highlight( - $name->getStartPosition(), - $name->getEndPosition(), - DocumentHighlightKind::TEXT - ); - } - if ($node instanceof ClassDeclaration && (string)$node->getNamespacedName() === $fullyQualfiedName) { - yield new Highlight( - $node->name->getStartPosition(), - $node->name->getEndPosition(), - DocumentHighlightKind::TEXT - ); - } - if ($node instanceof QualifiedName) { - if ($fullyQualfiedName === (string)$node->getResolvedName()) { - yield new Highlight( - $node->getStartPosition(), - $node->getEndPosition(), - $this->variableKind($node) - ); - } - } - } - } - - private function normalizeVarName(string $varName): string - { - return ltrim($varName, '$'); - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Model/Highlights.php b/lib/Extension/LanguageServerReferenceFinder/Model/Highlights.php deleted file mode 100644 index 58819fa111..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Model/Highlights.php +++ /dev/null @@ -1,79 +0,0 @@ - - */ -class Highlights implements IteratorAggregate, Countable -{ - /** - * @var array - */ - private array $highlights; - - public function __construct(DocumentHighlight ...$highlights) - { - $this->highlights = $highlights; - } - - public function first(): DocumentHighlight - { - if ($this->highlights === []) { - throw new RuntimeException('Document highlights are empty'); - } - - return $this->highlights[0]; - } - - public function at(int $index): DocumentHighlight - { - if (!isset($this->highlights[$index])) { - throw new RuntimeException(sprintf( - 'No highlight at offset "%s"', - $index - )); - } - - return $this->highlights[$index]; - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->highlights); - } - - public static function fromIterator(Iterator $iterator): self - { - return new self(...iterator_to_array($iterator)); - } - - - public function count(): int - { - return count($this->highlights); - } - - /** - * @return array - */ - public function toArray(): array - { - return $this->highlights; - } - - public static function empty(): self - { - return new self(); - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Tests/Bench/HighlightBench.php b/lib/Extension/LanguageServerReferenceFinder/Tests/Bench/HighlightBench.php deleted file mode 100644 index 5e2e105635..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Tests/Bench/HighlightBench.php +++ /dev/null @@ -1,20 +0,0 @@ -highlightsFor( - TextDocumentBuilder::fromUri(__DIR__ . '/../../../../../vendor/microsoft/tolerant-php-parser/src/Parser.php')->build(), - ByteOffset::fromInt(176949) - ); - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Tests/Extension/TestIndexerExtension.php b/lib/Extension/LanguageServerReferenceFinder/Tests/Extension/TestIndexerExtension.php deleted file mode 100644 index 3fb98d9f89..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Tests/Extension/TestIndexerExtension.php +++ /dev/null @@ -1,27 +0,0 @@ -register(Indexer::class, function () { - return IndexAgentBuilder::create( - __DIR__ . '/../Workspace', - __DIR__ . '/../Workspace', - )->buildTestAgent()->indexer(); - }); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Tests/Integration/Model/HighlighterTest.php b/lib/Extension/LanguageServerReferenceFinder/Tests/Integration/Model/HighlighterTest.php deleted file mode 100644 index 4af3da0d76..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Tests/Integration/Model/HighlighterTest.php +++ /dev/null @@ -1,242 +0,0 @@ -highlightsFor( - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt((int)$offset) - )) - ); - } - - /** - * @return Generator - */ - public static function provideVariables(): Generator - { - yield 'none' => [ - ' [ - 'ar;', - function (Highlights $highlights): void { - self::assertCount(1, $highlights); - self::assertEquals(DocumentHighlightKind::READ, $highlights->first()->kind); - } - ]; - - yield 'two vars including method var' => [ - 'ar; }', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - } - ]; - - yield 'only method var' => [ - 'ar) {}', - function (Highlights $highlights): void { - self::assertCount(1, $highlights); - } - ]; - - yield 'write var including method var' => [ - 'ar;}', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::WRITE, $highlights->first()->kind); - } - ]; - } - - /** - * @return Generator - */ - public static function provideProperties(): Generator - { - yield 'property declaration' => [ - 'oobar; }', - function (Highlights $highlights): void { - self::assertCount(1, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - } - ]; - yield 'property declaration 2' => [ - 'oobar; private $barfoo;}', - function (Highlights $highlights): void { - self::assertCount(1, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - } - ]; - yield 'property read' => [ - 'oobar; function bar() { return $this->foobar; }', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - self::assertEquals(DocumentHighlightKind::READ, $highlights->at(1)->kind); - } - ]; - - yield 'promoted property read' => [ - 'oobar) {} function bar() { return $this->foobar; }', - - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - self::assertEquals(DocumentHighlightKind::READ, $highlights->at(1)->kind); - } - ]; - - yield 'property access' => [ - 'foo<>bar->barfoo; }', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - self::assertEquals(DocumentHighlightKind::READ, $highlights->at(1)->kind); - } - ]; - - yield 'promoted property access' => [ - 'foo<>bar->barfoo; }', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - self::assertEquals(DocumentHighlightKind::READ, $highlights->at(1)->kind); - } - ]; - - yield 'property write' => [ - 'oobar; function bar() { return $this->foobar = "barfoo"; }', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - self::assertEquals(DocumentHighlightKind::WRITE, $highlights->at(1)->kind); - } - ]; - } - - /** - * @return Generator - */ - public static function provideMethods(): Generator - { - yield 'method declaration' => [ - 'oobar() {} }', - function (Highlights $highlights): void { - self::assertCount(1, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - } - ]; - - yield 'method read' => [ - 'b<>ar(); }', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - self::assertEquals(DocumentHighlightKind::READ, $highlights->at(1)->kind); - } - ]; - - yield 'static method read' => [ - 'ar(); }', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - self::assertEquals(DocumentHighlightKind::READ, $highlights->at(1)->kind); - } - ]; - } - - /** - * @return Generator - */ - public static function provideNames(): Generator - { - yield 'class name' => [ - 'bar {}', - function (Highlights $highlights): void { - self::assertCount(1, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - } - ]; - - yield 'class name with fqn' => [ - 'bar {const BAR=1;} Foobar::BAR;', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - } - ]; - - yield 'class in use statement' => [ - 'o::class;', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - } - ]; - - yield 'class alias in use statement' => [ - 't::class;', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - } - ]; - } - - /** - * @return Generator - */ - public static function provideConstants(): Generator - { - yield 'class constant' => [ - 'AR = "";}', - function (Highlights $highlights): void { - self::assertCount(1, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - } - ]; - - yield 'class constants' => [ - 'bar {const BAR=1;} Foobar::BAR;', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - } - ]; - - yield 'class constants on reference' => [ - 'AR;', - function (Highlights $highlights): void { - self::assertCount(2, $highlights); - self::assertEquals(DocumentHighlightKind::TEXT, $highlights->at(0)->kind); - } - ]; - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/GotoDefinitionHandlerTest.php b/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/GotoDefinitionHandlerTest.php deleted file mode 100644 index 2cd0c4193f..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/GotoDefinitionHandlerTest.php +++ /dev/null @@ -1,89 +0,0 @@ -uri(self::EXAMPLE_URI)->build(); - - $locations = [ - new TypeLocation( - TypeFactory::class('Foo'), - PhpactorLocation::fromPathAndOffsets((string) $document->uriOrThrow(), 2, 2) - ) - ]; - [$tester, $_] = $this->createTester($locations); - - $response = $tester->requestAndWait(DefinitionRequest::METHOD, [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0), - ]); - - $location = $response->result; - - $this->assertInstanceOf(Location::class, $location); - $this->assertEquals(self::EXAMPLE_URI, $location->uri); - $this->assertEquals(2, $location->range->start->character); - } - - public function testPresentChoiceIfAmbiguous(): void - { - $locations = [ - new TypeLocation(TypeFactory::class('Foobar'), PhpactorLocation::fromPathAndOffsets(self::EXAMPLE_URI, 2, 2)), - new TypeLocation(TypeFactory::class('Barfoo'), PhpactorLocation::fromPathAndOffsets(self::EXAMPLE_URI, 2, 2)), - ]; - [$tester, $builder] = $this->createTester($locations); - $watcher = $builder->responseWatcher(); - $promise = $tester->request(DefinitionRequest::METHOD, [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0), - ]); - $watcher->resolveLastResponse(new MessageActionItem('Foobar')); - $response = wait($promise); - $location = $response->result; - $this->assertInstanceOf(Location::class, $location); - $this->assertEquals(self::EXAMPLE_URI, $location->uri); - $this->assertEquals(2, $location->range->start->character); - } - - /** - * @return array{LanguageServerTester,LanguageServerTesterBuilder} - * @param TypeLocation[] $locations - */ - private function createTester(array $locations): array - { - $builder = LanguageServerTesterBuilder::create(); - - $tester = $builder->addHandler(new GotoDefinitionHandler( - $builder->workspace(), - new TestDefinitionLocator(new TypeLocations($locations)), - new LocationConverter(new WorkspaceTextDocumentLocator($builder->workspace())), - $builder->clientApi() - ))->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_TEXT); - return [$tester, $builder]; - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/GotoImplementationHandlerTest.php b/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/GotoImplementationHandlerTest.php deleted file mode 100644 index 8d97aeabd4..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/GotoImplementationHandlerTest.php +++ /dev/null @@ -1,73 +0,0 @@ - - */ - private ObjectProphecy $finder; - - protected function setUp(): void - { - $this->finder = $this->prophesize(ClassImplementationFinder::class); - } - - public function testGoesToImplementation(): void - { - $document = TextDocumentBuilder::create(self::EXAMPLE_TEXT) - ->language('php') - ->uri(self::EXAMPLE_URI) - ->build() - ; - - $this->finder->findImplementations( - $document, - ByteOffset::fromInt(0) - )->willReturn(new Locations([ - new Location($document->uriOrThrow(), ByteOffsetRange::fromInts(2, 2)) - ])); - - $builder = LanguageServerTesterBuilder::create(); - $tester = $builder->addHandler(new GotoImplementationHandler( - $builder->workspace(), - $this->finder->reveal(), - new LocationConverter(new WorkspaceTextDocumentLocator($builder->workspace())) - ))->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_TEXT); - - $response = $tester->requestAndWait('textDocument/implementation', [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0), - ]); - - $locations = $response->result; - - $this->assertIsArray($locations); - $this->assertCount(1, $locations); - - $lspLocation = reset($locations); - $this->assertInstanceOf(LspLocation::class, $lspLocation); - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/ReferencesHandlerTest.php b/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/ReferencesHandlerTest.php deleted file mode 100644 index 3e11296431..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/ReferencesHandlerTest.php +++ /dev/null @@ -1,198 +0,0 @@ - - */ - private ObjectProphecy $finder; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $locator; - - protected function setUp(): void - { - $this->finder = $this->prophesize(ReferenceFinder::class); - $this->locator = $this->prophesize(DefinitionLocator::class); - } - - public function testFindsReferences(): void - { - $document = TextDocumentBuilder::create(self::EXAMPLE_TEXT) - ->language('php') - ->uri(self::EXAMPLE_URI) - ->build() - ; - - $document2 = TextDocumentBuilder::create(self::EXAMPLE_TEXT) - ->language('php') - ->uri(self::EXAMPLE_URI.'2') - ->build() - ; - - $this->finder->findReferences( - $document, - ByteOffset::fromInt(0) - )->willYield([ - PotentialLocation::surely(Location::fromPathAndOffsets($document->uriOrThrow(), 2, 2)), - PotentialLocation::surely(Location::fromPathAndOffsets($document2->uriOrThrow(), 3, 3)), - PotentialLocation::surely(Location::fromPathAndOffsets($document->uriOrThrow(), 5, 5)), - ])->shouldBeCalled(); - - $tester = $this->createTester(); - $tester->textDocument()->open(self::EXAMPLE_URI.'2', self::EXAMPLE_TEXT); - - $response = $tester->requestAndWait(ReferencesRequest::METHOD, [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0), - 'context' => new ReferenceContext(false), - ]); - - $locations = $response->result; - $this->assertIsArray($locations); - $this->assertEquals([ - new LspLocation( - (string) $document->uri(), - new Range(new Position(0, 2), new Position(0, 2)), - ), - new LspLocation( - (string) $document->uri(), - new Range(new Position(0, 5), new Position(0, 5)), - ), - new LspLocation( - (string) $document2->uri(), - new Range(new Position(0, 3), new Position(0, 3)), - ), - ], $locations); - } - - public function testFindsReferencesIncludingDeclaration(): void - { - $document = TextDocumentBuilder::create(self::EXAMPLE_TEXT) - ->uri(self::EXAMPLE_URI) - ->language('php') - ->build() - ; - - $this->finder->findReferences( - $document, - ByteOffset::fromInt(0) - )->willYield([ - PotentialLocation::surely(new Location($document->uriOrThrow(), ByteOffsetRange::fromInts(2, 5))) - ])->shouldBeCalled(); - - $this->locator->locateDefinition( - $document, - ByteOffset::fromInt(0) - )->willReturn( - TypeLocations::forLocation( - new TypeLocation( - TypeFactory::class('Foo'), - new Location($document->uriOrThrow(), ByteOffsetRange::fromInts(2, 5)) - ) - ) - )->shouldBeCalled(); - - $response = $this->createTester()->requestAndWait(ReferencesRequest::METHOD, [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0), - 'context' => new ReferenceContext(true), - ]); - $locations = $response->result; - $this->assertIsArray($locations); - $this->assertCount(2, $locations); - $lspLocation = reset($locations); - $this->assertInstanceOf(LspLocation::class, $lspLocation); - } - - public function testFindsReferencesIncludingDeclarationWhenDeclarationNotFound(): void - { - $document = TextDocumentBuilder::create(self::EXAMPLE_TEXT) - ->language('php') - ->uri(self::EXAMPLE_URI) - ->build() - ; - - $this->finder->findReferences( - $document, - ByteOffset::fromInt(0) - )->willYield([ - PotentialLocation::surely(new Location($document->uriOrThrow(), ByteOffsetRange::fromInts(2, 10))) - ])->shouldBeCalled(); - - $this->locator->locateDefinition( - $document, - ByteOffset::fromInt(0) - )->willReturn( - new Location($document->uriOrThrow(), ByteOffsetRange::fromInts(2, 10)) - )->willThrow(new CouldNotLocateDefinition('nope')); - - $tester = $this->createTester(); - - $response = $tester->requestAndWait('textDocument/references', [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0), - 'context' => new ReferenceContext(true), - ]); - $locations = $response->result; - $this->assertIsArray($locations); - $this->assertCount(1, $locations); - $lspLocation = reset($locations); - $this->assertInstanceOf(LspLocation::class, $lspLocation); - } - - private function createTester(float $timeout = 60, float $softTimeout = 10): LanguageServerTester - { - $builder = LanguageServerTesterBuilder::create(); - $builder->addHandler( - new ReferencesHandler( - $builder->workspace(), - $this->finder->reveal(), - $this->locator->reveal(), - new LocationConverter(new WorkspaceTextDocumentLocator($builder->workspace())), - new ClientApi(TestRpcClient::create()), - $timeout, - $softTimeout, - ) - ); - $tester = $builder->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_TEXT); - return $tester; - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/TypeDefinitionHandlerTest.php b/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/TypeDefinitionHandlerTest.php deleted file mode 100644 index 38c6f6e587..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/TypeDefinitionHandlerTest.php +++ /dev/null @@ -1,102 +0,0 @@ -createTester($locations); - $response = $tester->requestAndWait(TypeDefinitionRequest::METHOD, [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0), - ]); - - $location = $response->result; - - $this->assertInstanceOf(Location::class, $location); - $this->assertEquals(self::EXAMPLE_URI, $location->uri); - $this->assertEquals(self::EXAMPLE_OFFSET, $location->range->start->character); - $this->assertEquals(self::EXAMPLE_OFFSET_END, $location->range->end->character); - } - - public function testGoesToMultipleTypes(): void - { - $locations = [ - new TypeLocation( - TypeFactory::class('Foobar'), - PhpactorLocation::fromPathAndOffsets(self::EXAMPLE_URI, self::EXAMPLE_OFFSET, self::EXAMPLE_OFFSET_END), - ), - new TypeLocation( - TypeFactory::class('Barfoo'), - PhpactorLocation::fromPathAndOffsets(self::EXAMPLE_URI, self::EXAMPLE_OFFSET, self::EXAMPLE_OFFSET_END), - ) - ]; - [$tester, $watcher] = $this->createTester($locations); - $promise = $tester->request(TypeDefinitionRequest::METHOD, [ - 'textDocument' => ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_URI), - 'position' => ProtocolFactory::position(0, 0), - ]); - $watcher->resolveLastResponse(new MessageActionItem('Foobar')); - $response = wait($promise); - - $location = $response->result; - - $this->assertInstanceOf(Location::class, $location); - $this->assertEquals(self::EXAMPLE_URI, $location->uri); - $this->assertEquals(self::EXAMPLE_OFFSET, $location->range->start->character); - $this->assertEquals(self::EXAMPLE_OFFSET_END, $location->range->end->character); - } - - /** - * @return array{LanguageServerTester,TestResponseWatcher} - * @param TypeLocation[] $locations - */ - private function createTester(array $locations): array - { - $document = TextDocumentBuilder::create(self::EXAMPLE_TEXT)->uri(self::EXAMPLE_URI)->build(); - $builder = LanguageServerTesterBuilder::create(); - $tester = $builder->addHandler(new TypeDefinitionHandler( - $builder->workspace(), - new TestTypeLocator( - new TypeLocations($locations) - ), - new LocationConverter(new WorkspaceTextDocumentLocator($builder->workspace())), - $builder->clientApi(), - ))->build(); - $tester->textDocument()->open(self::EXAMPLE_URI, self::EXAMPLE_TEXT); - - return [$tester, $builder->responseWatcher()]; - } -} diff --git a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/LanguageServerReferenceFinderExtensionTest.php b/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/LanguageServerReferenceFinderExtensionTest.php deleted file mode 100644 index 16e4baf48c..0000000000 --- a/lib/Extension/LanguageServerReferenceFinder/Tests/Unit/LanguageServerReferenceFinderExtensionTest.php +++ /dev/null @@ -1,94 +0,0 @@ -workspace()->reset(); - } - - public function testDefinition(): void - { - $tester = $this->createTester(); - $tester->textDocument()->open(__FILE__, (string)file_get_contents(__FILE__)); - - $response = $tester->requestAndWait('textDocument/definition', [ - 'textDocument' => new TextDocumentIdentifier(__FILE__), - 'position' => [], - ]); - $this->assertNull($response->result, 'Definition was not found'); - } - - public function testTypeDefinition(): void - { - $tester = $this->createTester(); - $tester->textDocument()->open(__FILE__, (String)file_get_contents(__FILE__)); - - $response = $tester->requestAndWait('textDocument/typeDefinition', [ - 'textDocument' => new TextDocumentIdentifier(__FILE__), - 'position' => [ - ], - ]); - $this->assertNull($response->result, 'Type was not found'); - } - - public function testReferenceFinder(): void - { - $tester = $this->createTester(); - $tester->textDocument()->open(__FILE__, (string)file_get_contents(__FILE__)); - - $response = $tester->requestAndWait('textDocument/references', [ - 'textDocument' => new TextDocumentIdentifier(__FILE__), - 'position' => [ - 'line' => 0, - 'character' => 0, - ], - 'context' => new ReferenceContext(false), - ]); - $tester->assertSuccess($response); - $this->assertIsArray($response->result, 'Returned empty references'); - } - - protected function workspace(): Workspace - { - return Workspace::create(__DIR__ . '/../Workspace'); - } - - private function createTester(): LanguageServerTester - { - $container = PhpactorContainer::fromExtensions([ - LoggingExtension::class, - LanguageServerExtension::class, - LanguageServerReferenceFinderExtension::class, - ReferenceFinderExtension::class, - FilePathResolverExtension::class, - LanguageServerBridgeExtension::class, - TestIndexerExtension::class, - ], [ - LanguageServerExtension::PARAM_ENABLE_TRUST_CHECK => false, - ]); - - $builder = $container->get(LanguageServerBuilder::class); - $this->assertInstanceOf(LanguageServerBuilder::class, $builder); - - return $builder->tester(ProtocolFactory::initializeParams(__DIR__)); - } -} diff --git a/lib/Extension/LanguageServerRename/Handler/FileRenameHandler.php b/lib/Extension/LanguageServerRename/Handler/FileRenameHandler.php deleted file mode 100644 index 416198f0cf..0000000000 --- a/lib/Extension/LanguageServerRename/Handler/FileRenameHandler.php +++ /dev/null @@ -1,67 +0,0 @@ - 'willRenameFiles' - ]; - } - - /** - * @return Promise - */ - public function willRenameFiles(RenameFilesParams $params): Promise - { - return call(function () use ($params) { - $workspaceEdits = LocatedTextEditsMap::create(); - foreach ($params->files as $rename) { - assert($rename instanceof FileRename); - - $workspaceEdits = $workspaceEdits->merge(yield $this->renamer->renameFile(TextDocumentUri::fromString($rename->oldUri), TextDocumentUri::fromString($rename->newUri))); - } - - return $this->converter->toWorkspaceEdit($workspaceEdits); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->workspace['fileOperations'] = new FileOperationOptions(willRename: new FileOperationRegistrationOptions( - filters: [ - new FileOperationFilter( - new FileOperationPattern( - glob: '**/*.php' - ) - ), - ] - )); - } -} diff --git a/lib/Extension/LanguageServerRename/Handler/RenameHandler.php b/lib/Extension/LanguageServerRename/Handler/RenameHandler.php deleted file mode 100644 index 14bb954d74..0000000000 --- a/lib/Extension/LanguageServerRename/Handler/RenameHandler.php +++ /dev/null @@ -1,128 +0,0 @@ - - */ - public function methods(): array - { - return [ - PrepareRenameRequest::METHOD => 'prepareRename', - RenameRequest::METHOD => 'rename', - ]; - } - - /** - * @return Promise - */ - public function rename(RenameParams $params): Promise - { - return call(function () use ($params) { - $locatedEdits = []; - $document = $document = $this->documentLocator->get(TextDocumentUri::fromString($params->textDocument->uri)); - $count = 0; - - try { - $rename = $this->renamer->rename( - $document, - PositionConverter::positionToByteOffset( - $params->position, - (string)$document - ), - $params->newName - ); - foreach ($rename as $result) { - if ($count++ === 10) { - yield delay(1); - } - $locatedEdits[] = $result; - } - - return $this->resultToWorkspaceEdit($locatedEdits, $rename->getReturn()); - } catch (CouldNotRename $error) { - $previous = $error->getPrevious(); - - $this->clientApi->window()->showMessage()->error(sprintf( - $error->getMessage() . ($previous?->getTraceAsString() ?? '') - )); - - return new WorkspaceEdit(null, []); - } - }); - } - - /** - * @return Promise - */ - public function prepareRename(PrepareRenameParams $params): Promise - { - // https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename - return call(function () use ($params) { - $range = $this->renamer->getRenameRange( - $document = $this->documentLocator->get(TextDocumentUri::fromString($params->textDocument->uri)), - PositionConverter::positionToByteOffset( - $params->position, - (string)$document - ), - ); - if ($range == null) { - return null; - } - return RangeConverter::toLspRange($range, (string)$document); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->renameProvider = new RenameOptions(true); - } - - /** - * @param LocatedTextEdit[] $locatedEdits - */ - private function resultToWorkspaceEdit(array $locatedEdits, ?RenameResult $renameResult): WorkspaceEdit - { - return $this->converter->toWorkspaceEdit( - LocatedTextEditsMap::fromLocatedEdits($locatedEdits), - $renameResult - ); - } -} diff --git a/lib/Extension/LanguageServerRename/LanguageServerRenameExtension.php b/lib/Extension/LanguageServerRename/LanguageServerRenameExtension.php deleted file mode 100644 index 2362c4657a..0000000000 --- a/lib/Extension/LanguageServerRename/LanguageServerRenameExtension.php +++ /dev/null @@ -1,64 +0,0 @@ -register(Renamer::class, function (Container $container) { - return new ChainRenamer(array_map(function (string $serviceId) use ($container) { - return $container->get($serviceId); - }, array_keys($container->getServiceIdsForTag(self::TAG_RENAMER)))); - }); - - $container->register(RenameHandler::class, function (Container $container) { - return new RenameHandler( - $container->get(LocatedTextEditConverter::class), - $container->get(TextDocumentLocator::class), - $container->get(Renamer::class), - $container->get(ClientApi::class) - ); - }, [ - LanguageServerExtension::TAG_METHOD_HANDLER => [] - ]); - - $container->register(FileRenameHandler::class, function (Container $container) { - return new FileRenameHandler( - $container->get(FileRenamer::class), - $container->get(LocatedTextEditConverter::class), - ); - }, [ - LanguageServerExtension::TAG_METHOD_HANDLER => [] - ]); - - $container->register(LocatedTextEditConverter::class, function (Container $container) { - return new LocatedTextEditConverter( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(TextDocumentLocator::class), - ); - }); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerRename/LanguageServerRenameWorseExtension.php b/lib/Extension/LanguageServerRename/LanguageServerRenameWorseExtension.php deleted file mode 100644 index 9e87668b1c..0000000000 --- a/lib/Extension/LanguageServerRename/LanguageServerRenameWorseExtension.php +++ /dev/null @@ -1,117 +0,0 @@ -register(VariableRenamer::class, function (Container $container) { - $parser = $container->get(AstProvider::class); - - return new VariableRenamer( - new TolerantVariableReferenceFinder($parser, true), - $container->get(TextDocumentLocator::class), - $parser, - ); - }, [ - LanguageServerRenameExtension::TAG_RENAMER => [] - ]); - - $container->register(WorseReflectionMemberRenamer::class, function (Container $container) { - return new WorseReflectionMemberRenamer( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - ); - }, [ - LanguageServerRenameExtension::TAG_RENAMER => [] - ]); - - $container->register(MemberRenamer::class, function (Container $container) { - return new MemberRenamer( - $container->get(DefinitionAndReferenceFinder::class), - $container->get(TextDocumentLocator::class), - $container->get(WorseReflectionExtension::SERVICE_AST_PROVIDER), - $container->get(IndexedImplementationFinder::class), - ); - }, [ - LanguageServerRenameExtension::TAG_RENAMER => [] - ]); - - $container->register(ClassRenamer::class, function (Container $container) { - return new ClassRenamer( - new WorseNameToUriConverter($container->get(WorseReflectionExtension::SERVICE_REFLECTOR)), - new ClassToFileNameToUriConverter($container->get(ClassToFileExtension::SERVICE_CONVERTER)), - $container->get(DefinitionAndReferenceFinder::class), - $container->get(TextDocumentLocator::class), - $container->get(AstProvider::class), - $container->get(ClassMover::class) - ); - }, [ - LanguageServerRenameExtension::TAG_RENAMER => [] - ]); - - $container->register(DefinitionAndReferenceFinder::class, function (Container $container) { - // wrap the definiton and reference finder to update the index with the current workspace - return new WorkspaceUpdateReferenceFinder( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(Indexer::class), - new DefinitionAndReferenceFinder( - $container->get(ReferenceFinderExtension::SERVICE_DEFINITION_LOCATOR), - $container->get(ReferenceFinder::class) - ) - ); - }); - - $container->register(FileRenamer::class, function (Container $container) { - $renamer = new PhpactorFileRenamer( - new ClassToFileUriToNameConverter($container->get(ClassToFileExtension::SERVICE_CONVERTER)), - $container->get(TextDocumentLocator::class), - $container->get(QueryClient::class), - $container->get(ClassMover::class) - ); - return new LoggingFileRenamer( - $renamer, - LoggingExtension::channelLogger($container, 'LSP-RENAME') - ); - }); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerRename/Tests/Extension/TestExtension.php b/lib/Extension/LanguageServerRename/Tests/Extension/TestExtension.php deleted file mode 100644 index 05c2178970..0000000000 --- a/lib/Extension/LanguageServerRename/Tests/Extension/TestExtension.php +++ /dev/null @@ -1,42 +0,0 @@ -register(InMemoryRenamer::class, function (Container $container) { - return new InMemoryRenamer( - $container->parameter('range')->value(), - $container->parameter('results')->value(), - ); - }, [ - LanguageServerRenameExtension::TAG_RENAMER => [] - ]); - - $container->register(FileRenamer::class, function (Container $container) { - return new TestFileRenamer(); - }, [ - ]); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - 'range' => ByteOffsetRange::fromInts(0, 10), - 'results' => [], - ]); - } -} diff --git a/lib/Extension/LanguageServerRename/Tests/IntegrationTestCase.php b/lib/Extension/LanguageServerRename/Tests/IntegrationTestCase.php deleted file mode 100644 index 0a78f79e7e..0000000000 --- a/lib/Extension/LanguageServerRename/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,45 +0,0 @@ -workspace()->reset(); - } - - protected function workspace(): Workspace - { - return Workspace::create(__DIR__ . '/Workspace'); - } - - protected function container(array $config = []): Container - { - $container = PhpactorContainer::fromExtensions([ - LanguageServerExtension::class, - TestExtension::class, - LanguageServerRenameExtension::class, - FilePathResolverExtension::class, - LanguageServerBridgeExtension::class, - LoggingExtension::class, - ReferenceFinderExtension::class, - ], array_merge([ - LanguageServerExtension::PARAM_ENABLE_TRUST_CHECK => false, - ], $config)); - - return $container; - } -} diff --git a/lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php b/lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php deleted file mode 100644 index c2c4b5b2e9..0000000000 --- a/lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php +++ /dev/null @@ -1,85 +0,0 @@ -createServer(); - $result = $server->initialize(); - - self::assertInstanceOf(FileOperationRegistrationOptions::class, $result->capabilities->workspace['fileOperations']->willRename); - } - - public function testMoveFileNoEdits(): void - { - $server = $this->createServer(); - $server->initialize(); - $response = wait($server->request('workspace/willRenameFiles', new RenameFilesParams([ - new FileRename('file:///file1', 'file:///file2'), - ]))); - assert($response instanceof ResponseMessage); - - self::assertInstanceOf(WorkspaceEdit::class, $response->result); - } - - public function testMoveFileEdits(): void - { - $server = $this->createServer(false, [ - 'file:///file1' => TextEdits::one(TextEdit::create(0, 0, 'Hello')), - 'file:///file2' => TextEdits::one(TextEdit::create(0, 0, 'Hello')), - ]); - $server->initialize(); - - $response = wait($server->request('workspace/willRenameFiles', new RenameFilesParams([ - new FileRename('file:///file1', 'file:///file2'), - ]))); - - assert($response instanceof ResponseMessage); - - $edits = $response->result; - self::assertInstanceOf(WorkspaceEdit::class, $edits); - assert($edits instanceof WorkspaceEdit); - self::assertCount(2, $edits->documentChanges); - } - - private function createServer(bool $willFail = false, array $workspaceEdits = []): LanguageServerTester - { - $builder = LanguageServerTesterBuilder::createBare() - ->enableTextDocuments() - ->enableFileEvents(); - $builder->addHandler($this->createHandler($builder, $willFail, $workspaceEdits)); - $server = $builder->build(); - - foreach ($workspaceEdits as $path => $_) { - $server->textDocument()->open($path, ''); - } - return $server; - } - - private function createHandler(LanguageServerTesterBuilder $builder, bool $willError = false, array $workspaceEdits = []): FileRenameHandler - { - return new FileRenameHandler( - new TestFileRenamer($willError, new LocatedTextEditsMap($workspaceEdits)), - new LocatedTextEditConverter($builder->workspace(), new WorkspaceTextDocumentLocator($builder->workspace())) - ); - } -} diff --git a/lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php b/lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php deleted file mode 100644 index 8a826dfcb5..0000000000 --- a/lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php +++ /dev/null @@ -1,117 +0,0 @@ -bootContainerWithRangeAndResults(null, []); - $result = $this->tester->initialize(); - self::assertTrue($result->capabilities->renameProvider->prepareProvider); - } - - public function testPrepareRenameReturnsNullIfItCouldNotPrepareAnything(): void - { - $this->bootContainerWithRangeAndResults(null, []); - $this->tester->textDocument()->open(self::EXAMPLE_FILE, 'tester->requestAndWait( - PrepareRenameRequest::METHOD, - new PrepareRenameParams( - ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_FILE), - ProtocolFactory::position(0, 0), - ) - ); - - $this->tester->assertSuccess($response); - self::assertNull($response->result); - } - - public function testPrepareRename(): void - { - $expectedCharOffset = 3; - - $this->bootContainerWithRangeAndResults(ByteOffsetRange::fromInts(0, $expectedCharOffset), []); - $this->tester->textDocument()->open(self::EXAMPLE_FILE, 'tester->requestAndWait( - PrepareRenameRequest::METHOD, - new PrepareRenameParams( - ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_FILE), - ProtocolFactory::position(0, 0), - ) - ); - - $this->tester->assertSuccess($response); - self::assertEquals(ProtocolFactory::range(0, 0, 0, $expectedCharOffset), $response->result); - } - - public function testRename(): void - { - $expectedUri = TextDocumentUri::fromString(self::EXAMPLE_FILE); - $this->bootContainerWithRangeAndResults(ByteOffsetRange::fromInts(0, 0), [ - new LocatedTextEdit( - $expectedUri, - TextEdit::create(ByteOffset::fromInt(1), 0, self::EXAMPLE_NEW_NAME) - ) - ]); - $this->tester->textDocument()->open(self::EXAMPLE_FILE, 'tester->requestAndWait( - RenameRequest::METHOD, - new RenameParams( - ProtocolFactory::textDocumentIdentifier(self::EXAMPLE_FILE), - ProtocolFactory::position(0, 0), - self::EXAMPLE_NEW_NAME - ) - ); - - $this->tester->assertSuccess($response); - assert($response->result instanceof WorkspaceEdit); - self::assertNull($response->result->changes); - $edit = $response->result->documentChanges[0]; - assert($edit instanceof TextDocumentEdit); - self::assertEquals(self::EXAMPLE_FILE, $edit->textDocument->uri); - $edit = reset($edit->edits); - assert($edit instanceof PhpactorTextEdit); - self::assertEquals(self::EXAMPLE_NEW_NAME, $edit->newText); - } - - protected function bootContainerWithRangeAndResults(?ByteOffsetRange $range, array $results): void - { - $container = $this->container([ - 'range' => $range, - 'results' => $results, - ]); - $this->tester = $container->get(LanguageServerBuilder::class)->tester( - ProtocolFactory::initializeParams($this->workspace()->path()) - ); - $this->renamer = $container->get(InMemoryRenamer::class); - } -} diff --git a/lib/Extension/LanguageServerRename/Tests/Unit/PredefiniedImplementationFinder.php b/lib/Extension/LanguageServerRename/Tests/Unit/PredefiniedImplementationFinder.php deleted file mode 100644 index 54ce7448e8..0000000000 --- a/lib/Extension/LanguageServerRename/Tests/Unit/PredefiniedImplementationFinder.php +++ /dev/null @@ -1,23 +0,0 @@ -locations; - } -} diff --git a/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php b/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php deleted file mode 100644 index 3f8f7e55cc..0000000000 --- a/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php +++ /dev/null @@ -1,83 +0,0 @@ -points[$marker] = $name; - return $this; - } - - public function registerRange(string $name, string $openMarker, string $closeMarker): OffsetExtractor - { - $this->rangeOpenMarkers[$openMarker] = $name; - $this->rangeCloseMarkers[$closeMarker] = $name; - return $this; - } - - public function parse(string $source): OffsetExtractorResult - { - $markers = array_merge( - array_keys($this->points), - array_keys($this->rangeOpenMarkers), - array_keys($this->rangeCloseMarkers), - ); - $results = preg_split('/('. implode('|', $markers) .')/u', $source, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - - if (!is_array($results)) { - return $this; - } - - $newSource = ''; - $pointResults = []; - $rangeResults = []; - $offset = 0; - $currentRangeStartOffset = 0; - - foreach ($this->points as $marker=>$name) { - $pointResults[$name] = []; - } - foreach ($this->rangeCloseMarkers as $marker=>$name) { - $rangeResults[$name] = []; - } - - foreach ($results as $result) { - if (isset($this->points[$result])) { - $pointResults[$this->points[$result]][] = ByteOffset::fromInt($offset); - continue; - } - - if (isset($this->rangeOpenMarkers[$result])) { - $currentRangeStartOffset = $offset; - continue; - } - - if (isset($this->rangeCloseMarkers[$result])) { - $rangeResults[$this->rangeCloseMarkers[$result]][] = ByteOffsetRange::fromInts($currentRangeStartOffset, $offset); - continue; - } - - $offset += strlen($result); - $newSource .= $result; - } - - return new OffsetExtractorResult($newSource, $pointResults, $rangeResults); - ; - } -} diff --git a/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractorResult.php b/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractorResult.php deleted file mode 100644 index e688148ae1..0000000000 --- a/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractorResult.php +++ /dev/null @@ -1,103 +0,0 @@ - $offsets - * @param array $ranges - */ - public function __construct( - private string $source, - private array $offsets, - private array $ranges - ) { - } - - public function source(): string - { - return $this->source; - } - - /** - * @return ByteOffset[] - */ - public function offsets(?string $name = null): array - { - if (null === $name) { - return array_reduce($this->offsets, function (array $carry, array $offsets) { - return array_merge($carry, $offsets); - }, []); - } - - if (!isset($this->offsets[$name])) { - throw new RuntimeException(sprintf( - 'No offset registered with name "%s", known names "%s"', - $name, - implode('", "', array_keys($this->offsets)) - )); - } - - return $this->offsets[$name]; - } - - public function offset(?string $name = null): ByteOffset - { - $offsets = $this->offsets($name); - - if (!count($offsets)) { - throw new RuntimeException(sprintf( - 'No "%s" offsets found in source code', - $name - )); - } - - $offset = reset($offsets); - - return $offset; - } - - /** - * @return ByteOffsetRange[] - */ - public function ranges(?string $name = null): array - { - if (null === $name) { - return array_reduce($this->ranges, function (array $carry, array $ranges) { - return array_merge($carry, $ranges); - }, []); - } - - if (!isset($this->ranges[$name])) { - throw new RuntimeException(sprintf( - 'No range registered with name "%s", known names "%s"', - $name, - implode('", "', array_keys($this->ranges)) - )); - } - - return $this->ranges[$name]; - } - - public function range(?string $name = null): ByteOffsetRange - { - $ranges = $this->ranges($name); - - if (!count($ranges)) { - throw new RuntimeException(sprintf( - 'No "%s" ranges found in source code', - $name - )); - } - - $range = reset($ranges); - - return $range; - } -} diff --git a/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractorTest.php b/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractorTest.php deleted file mode 100644 index 112a83545a..0000000000 --- a/lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractorTest.php +++ /dev/null @@ -1,184 +0,0 @@ -registerOffset('selection', '<>') - ->parse('Test string with<> selector'); - - $selection = $extractor->offset('selection'); - $newSource = $extractor->source(); - - $this->assertEquals(ByteOffset::fromInt(16), $selection); - $this->assertEquals('Test string with selector', $newSource); - } - - public function testFirstOffset(): void - { - $extractor = OffsetExtractor::create() - ->registerOffset('selection', '<>') - ->parse('Test string with<> selector'); - - $selection = $extractor->offset(); - $newSource = $extractor->source(); - - $this->assertEquals(ByteOffset::fromInt(16), $selection); - $this->assertEquals('Test string with selector', $newSource); - } - - public function testPreservesMultibyteOffset(): void - { - $extractor = OffsetExtractor::create() - ->registerOffset('selection', '<>') - ->parse('Test string 🐱 <> selector'); - - $selection = $extractor->offset('selection'); - $newSource = $extractor->source(); - - $this->assertEquals(ByteOffset::fromInt(19), $selection); - $this->assertEquals('Test string 🐱 selector', $newSource); - } - - public function testExceptionWhenNoOffsetIsFound(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('No "selection" offsets found'); - - $extractor = OffsetExtractor::create() - ->registerOffset('selection', '<>') - ->parse('Test string without selector'); - - $extractor->offset('selection'); - } - - public function testExceptionWhenNoOffsetIsRegistered(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('No offset registered'); - - $extractor = OffsetExtractor::create() - ->parse('Test string without selector'); - - $extractor->offset('selection'); - } - - public function testOffsets(): void - { - $extractor = OffsetExtractor::create() - ->registerOffset('selection', '<>') - ->parse('Test string with<> two select<>ors'); - $selection = $extractor->offsets('selection'); - $newSource = $extractor->source(); - $this->assertEquals([ - ByteOffset::fromInt(16), - ByteOffset::fromInt(27) - ], $selection); - $this->assertEquals('Test string with two selectors', $newSource); - } - - public function testAllOffsets(): void - { - $extractor = OffsetExtractor::create() - ->registerOffset('selection', '<>') - ->parse('Test string with<> two select<>ors'); - $selection = $extractor->offsets(); - $newSource = $extractor->source(); - $this->assertEquals([ - ByteOffset::fromInt(16), - ByteOffset::fromInt(27) - ], $selection); - $this->assertEquals('Test string with two selectors', $newSource); - } - - public function testRange(): void - { - $extractor = OffsetExtractor::create() - ->registerRange('textEdit', '{{', '}}') - ->parse('Test string {{with}} selector'); - - $textEdit = $extractor->range('textEdit'); - $newSource = $extractor->source(); - - $this->assertEquals(ByteOffsetRange::fromInts(12, 16), $textEdit); - $this->assertEquals('Test string with selector', $newSource); - } - - public function testFirstRange(): void - { - $extractor = OffsetExtractor::create() - ->registerRange('textEdit', '{{', '}}') - ->parse('Test string {{with}} selector'); - - $textEdit = $extractor->range(); - $newSource = $extractor->source(); - - $this->assertEquals(ByteOffsetRange::fromInts(12, 16), $textEdit); - $this->assertEquals('Test string with selector', $newSource); - } - - public function testRanges(): void - { - $extractor = OffsetExtractor::create() - ->registerRange('textEdit', '{{', '}}') - ->parse('Test string {{with}} two {{selectors}}'); - $textEdit = $extractor->ranges('textEdit'); - $newSource = $extractor->source(); - $this->assertEquals( - [ - ByteOffsetRange::fromInts(12, 16), - ByteOffsetRange::fromInts(21, 30), - ], - $textEdit - ); - $this->assertEquals('Test string with two selectors', $newSource); - } - - public function testReturnsAllRanges(): void - { - $extractor = OffsetExtractor::create() - ->registerRange('textEdit', '{{', '}}') - ->parse('Test string {{with}} two {{selectors}}'); - $textEdit = $extractor->ranges(); - $newSource = $extractor->source(); - $this->assertEquals( - [ - ByteOffsetRange::fromInts(12, 16), - ByteOffsetRange::fromInts(21, 30), - ], - $textEdit - ); - $this->assertEquals('Test string with two selectors', $newSource); - } - - public function testExceptionWhenNoRangeIsFound(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('No "selection" ranges found'); - - $extractor = OffsetExtractor::create() - ->registerRange('selection', '<', '>') - ->parse('Test string without selector'); - - $extractor->range('selection'); - } - - public function testExceptionWhenNoRangeIsRegistered(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('No range registered'); - - $extractor = OffsetExtractor::create() - ->parse('Test string without selector'); - - $extractor->range('selection'); - } -} diff --git a/lib/Extension/LanguageServerRename/Util/LocatedTextEditConverter.php b/lib/Extension/LanguageServerRename/Util/LocatedTextEditConverter.php deleted file mode 100644 index 1f43d19ce6..0000000000 --- a/lib/Extension/LanguageServerRename/Util/LocatedTextEditConverter.php +++ /dev/null @@ -1,71 +0,0 @@ -toLocatedTextEdits() as $result) { - $version = $this->getDocumentVersion((string)$result->documentUri()); - $documentEdits[] = new TextDocumentEdit( - new OptionalVersionedTextDocumentIdentifier( - uri: (string)$result->documentUri(), - version: $version, - ), - TextEditConverter::toLspTextEdits( - $result->textEdits(), - (string)$this->locator->get($result->documentUri()) - ) - ); - } - - // deduplicate the edits: with renaming we currently have multiple - // references to the declaration. - $documentEdits = array_map(function (TextDocumentEdit $documentEdit) { - $new = []; - foreach ($documentEdit->edits as $edit) { - $new[sprintf( - '%s-%s-%s', - $edit->range->start->line, - $edit->range->start->character, - $edit->newText - )] = $edit; - } - $documentEdit->edits = array_values($new); - return $documentEdit; - }, $documentEdits); - - if (null !== $renameResult) { - $documentEdits[] = new RenameFile( - 'rename', - $renameResult->oldUri(), - $renameResult->newUri(), - ); - } - - return new WorkspaceEdit(null, $documentEdits); - } - - private function getDocumentVersion(string $uri): int - { - return $this->workspace->has($uri) ? $this->workspace->get($uri)->version : 0; - } -} diff --git a/lib/Extension/LanguageServerSelectionRange/Handler/SelectionRangeHandler.php b/lib/Extension/LanguageServerSelectionRange/Handler/SelectionRangeHandler.php deleted file mode 100644 index 3b079ae799..0000000000 --- a/lib/Extension/LanguageServerSelectionRange/Handler/SelectionRangeHandler.php +++ /dev/null @@ -1,55 +0,0 @@ - 'selectionRange', - ]; - } - - /** - * @return Promise - */ - public function selectionRange(SelectionRangeParams $params): Promise - { - $textDocument = $this->workspace->get($params->textDocument->uri); - $offsets = array_map(function (Position $position) use ($textDocument) { - return PositionConverter::positionToByteOffset($position, $textDocument->text); - }, $params->positions); - - return new Success($this->provider->provide( - TextDocumentConverter::fromLspTextItem($textDocument), - $offsets - )); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->selectionRangeProvider = true; - } -} diff --git a/lib/Extension/LanguageServerSelectionRange/LanguageServerSelectionRangeExtension.php b/lib/Extension/LanguageServerSelectionRange/LanguageServerSelectionRangeExtension.php deleted file mode 100644 index 87ed2ec91c..0000000000 --- a/lib/Extension/LanguageServerSelectionRange/LanguageServerSelectionRangeExtension.php +++ /dev/null @@ -1,35 +0,0 @@ -register(SelectionRangeHandler::class, function (Container $container) { - return new SelectionRangeHandler( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(RangeProvider::class) - ); - }, [ - LanguageServerExtension::TAG_METHOD_HANDLER => [], - ]); - $container->register(RangeProvider::class, function (Container $container) { - return new RangeProvider(new TolerantAstProvider()); - }); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerSelectionRange/Model/RangeProvider.php b/lib/Extension/LanguageServerSelectionRange/Model/RangeProvider.php deleted file mode 100644 index 50275c67f4..0000000000 --- a/lib/Extension/LanguageServerSelectionRange/Model/RangeProvider.php +++ /dev/null @@ -1,56 +0,0 @@ - $offsets - * - * @return array - */ - public function provide(TextDocument $source, array $offsets): array - { - $rootNode = $this->parser->get($source); - - $selectionRanges = []; - foreach ($offsets as $byteOffset) { - $node = $rootNode->getDescendantNodeAtPosition($byteOffset->toInt()); - $range = $this->buildRange($node, $source); - if ($range->parent) { - $range->parent = $this->buildRange($node->parent, $source); - } - $selectionRanges[] = $range; - } - - return $selectionRanges; - } - - private function buildRange(Node $node, string $source): SelectionRange - { - return new SelectionRange( - new Range( - PositionConverter::intByteOffsetToPosition( - $node->getStartPosition(), - $source - ), - PositionConverter::intByteOffsetToPosition( - $node->getEndPosition(), - $source - ) - ) - ); - } -} diff --git a/lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php b/lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php deleted file mode 100644 index a88d900e45..0000000000 --- a/lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php +++ /dev/null @@ -1,251 +0,0 @@ -parser->get($document); - - return $this->buildNodes($rootNode->getChildNodes(), $document->__toString()); - } - - /** - * @return array - */ - private function buildNodes(Generator $nodes, string $source): array - { - $symbols = []; - foreach ($nodes as $childNode) { - if (null !== $symbol = $this->buildNode($childNode, $source)) { - $symbols[] = $symbol; - } - } - - return $symbols; - } - - private function buildNode(Node $node, string $source): ?DocumentSymbol - { - if ($node instanceof FunctionDeclaration) { - return new DocumentSymbol( - name: (string)$node->name->getText($source), - kind: SymbolKind::FUNCTION, - range: new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - selectionRange: new Range( - PositionConverter::intByteOffsetToPosition($node->name->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->name->getEndPosition(), $source) - ), - children: $this->buildNodes($this->memberNodes($node), $source) - ); - } - - if ($node instanceof ClassDeclaration) { - return new DocumentSymbol( - (string)$node->name->getText($source), - SymbolKind::CLASS_, - new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - new Range( - PositionConverter::intByteOffsetToPosition($node->name->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->name->getEndPosition(), $source) - ), - children: $this->buildNodes($this->memberNodes($node), $source) - ); - } - - if ($node instanceof InterfaceDeclaration) { - return new DocumentSymbol( - (string)$node->name->getText($source), - SymbolKind::INTERFACE, - new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - new Range( - PositionConverter::intByteOffsetToPosition($node->name->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->name->getEndPosition(), $source) - ), - children: $this->buildNodes($this->memberNodes($node), $source) - ); - } - - - if ($node instanceof TraitDeclaration) { - return new DocumentSymbol( - (string)$node->name->getText($source), - SymbolKind::CLASS_, - new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - new Range( - PositionConverter::intByteOffsetToPosition($node->name->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->name->getEndPosition(), $source) - ), - children: $this->buildNodes($this->memberNodes($node), $source) - ); - } - - if ($node instanceof MethodDeclaration) { - $name = (string)$node->name->getText($source); - return new DocumentSymbol( - $name, - $name === '__construct' ? SymbolKind::CONSTRUCTOR : SymbolKind::METHOD, - new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - new Range( - PositionConverter::intByteOffsetToPosition($node->name->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->name->getEndPosition(), $source) - ), - children: [] - ); - } - - if ($node instanceof PropertyDeclaration) { - // note this only supports single property declarations - foreach ($node->propertyElements->getChildNodes() as $element) { - assert($element instanceof PropertyElement); - return $this->resolvePropertyVariable($element->variable, $source); - } - } - - if ($node instanceof AssignmentExpression) { - /** @var Expression $left */ - $left = $node->leftOperand; - return $this->resolvePropertyVariable($left, $source); - } - - if ($node instanceof ConstElement) { - return new DocumentSymbol( - (string)$node->getName(), - SymbolKind::CONSTANT, - new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - new Range( - PositionConverter::intByteOffsetToPosition($node->name->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->name->getEndPosition(), $source) - ), - children: [] - ); - } - - if ($node instanceof EnumDeclaration) { - return new DocumentSymbol( - (string)$node->name->getText($source), - SymbolKind::ENUM, - new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - new Range( - PositionConverter::intByteOffsetToPosition($node->name->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->name->getEndPosition(), $source) - ), - children: $this->buildNodes($this->memberNodes($node), $source) - ); - } - - if ($node instanceof EnumCaseDeclaration) { - return new DocumentSymbol( - (string)$node->name->getText($source), - SymbolKind::ENUM_MEMBER, - new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - new Range( - PositionConverter::intByteOffsetToPosition($node->name->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->name->getEndPosition(), $source) - ), - children: [] - ); - } - - return null; - } - - - private function resolvePropertyVariable(?Node $node, string $source): ?DocumentSymbol - { - if (!$node instanceof Variable) { - return null; - } - if (!$node->getFirstAncestor(PropertyDeclaration::class)) { - return null; - } - return new DocumentSymbol( - (string)$node->getName(), - SymbolKind::PROPERTY, - new Range( - PositionConverter::intByteOffsetToPosition($node->parent->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->parent->getEndPosition(), $source) - ), - new Range( - PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source), - PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source) - ), - children: [] - ); - } - - private function memberNodes(Node $node): Generator - { - return $node->getDescendantNodes(function (Node $node) { - return - $node instanceof InterfaceMembers || - $node instanceof TraitMembers || - $node instanceof ClassMembersNode || - $node instanceof EnumMembers || - $node instanceof MethodDeclaration || - $node instanceof PropertyDeclaration || - $node instanceof ClassConstDeclaration || - ($node instanceof ExpressionList && $node->parent instanceof PropertyDeclaration) || - ($node instanceof ConstElementList && $node->parent instanceof ClassConstDeclaration); - }); - } -} diff --git a/lib/Extension/LanguageServerSymbolProvider/Handler/DocumentSymbolProviderHandler.php b/lib/Extension/LanguageServerSymbolProvider/Handler/DocumentSymbolProviderHandler.php deleted file mode 100644 index b89dc51be5..0000000000 --- a/lib/Extension/LanguageServerSymbolProvider/Handler/DocumentSymbolProviderHandler.php +++ /dev/null @@ -1,46 +0,0 @@ - 'documentSymbols', - ]; - } - - /** - * @return Promise - */ - public function documentSymbols(DocumentSymbolParams $params): Promise - { - $textDocument = $this->workspace->get($params->textDocument->uri); - - return new Success($this->provider->provideFor(TextDocumentConverter::fromLspTextItem($textDocument))); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->documentSymbolProvider = true; - } -} diff --git a/lib/Extension/LanguageServerSymbolProvider/LanguageServerSymbolProviderExtension.php b/lib/Extension/LanguageServerSymbolProvider/LanguageServerSymbolProviderExtension.php deleted file mode 100644 index 1f66a6190b..0000000000 --- a/lib/Extension/LanguageServerSymbolProvider/LanguageServerSymbolProviderExtension.php +++ /dev/null @@ -1,36 +0,0 @@ -register(DocumentSymbolProviderHandler::class, function (Container $container) { - return new DocumentSymbolProviderHandler( - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - $container->get(DocumentSymbolProvider::class) - ); - }, [ - LanguageServerExtension::TAG_METHOD_HANDLER => [], - ]); - $container->register(DocumentSymbolProvider::class, function (Container $container) { - return new TolerantDocumentSymbolProvider(new TolerantAstProvider()); - }); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/LanguageServerSymbolProvider/Model/DocumentSymbolProvider.php b/lib/Extension/LanguageServerSymbolProvider/Model/DocumentSymbolProvider.php deleted file mode 100644 index 1e4a31dd8e..0000000000 --- a/lib/Extension/LanguageServerSymbolProvider/Model/DocumentSymbolProvider.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ - public function provideFor(TextDocument $document): array; -} diff --git a/lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php b/lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php deleted file mode 100644 index a889c2230e..0000000000 --- a/lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php +++ /dev/null @@ -1,403 +0,0 @@ -provideFor( - TextDocumentBuilder::create($source)->build() - ); - $this->assertTree($actual, $expected); - } - - public function provideFunctions(): Generator - { - yield 'functions' => [ - 'dummyRange(), - $this->dummyRange(), - null, - null, - children: [] - ), - ] - ]; - } - - public function provideClasses(): Generator - { - yield 'class' => [ - ' [ - ' 'Foo', - 'kind' => SymbolKind::CLASS_, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [ - DocumentSymbol::fromArray([ - 'name' => 'bar', - 'kind' => SymbolKind::METHOD, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ]) - ] - ]; - - yield 'class construct' => [ - ' 'Foo', - 'kind' => SymbolKind::CLASS_, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [ - DocumentSymbol::fromArray([ - 'name' => '__construct', - 'kind' => SymbolKind::CONSTRUCTOR, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ]) - ] - ]; - - yield 'class property with value' => [ - ' 'Foo', - 'kind' => SymbolKind::CLASS_, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [ - DocumentSymbol::fromArray([ - 'name' => 'bar', - 'kind' => SymbolKind::PROPERTY, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ]) - ] - ]; - - yield 'class property' => [ - ' 'Foo', - 'kind' => SymbolKind::CLASS_, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [ - DocumentSymbol::fromArray([ - 'name' => 'bar', - 'kind' => SymbolKind::PROPERTY, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ]) - ] - ]; - - yield 'class constant' => [ - ' 'Foo', - 'kind' => SymbolKind::CLASS_, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [ - DocumentSymbol::fromArray([ - 'name' => 'BAR', - 'kind' => SymbolKind::CONSTANT, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ]) - ] - ]; - } - - public function provideInterfaces(): Generator - { - yield 'interface' => [ - ' [ - ' 'Foo', - 'kind' => SymbolKind::INTERFACE, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [ - DocumentSymbol::fromArray([ - 'name' => 'bar', - 'kind' => SymbolKind::METHOD, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ]) - ] - ]; - - yield 'interface constant' => [ - ' 'Foo', - 'kind' => SymbolKind::INTERFACE, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [ - DocumentSymbol::fromArray([ - 'name' => 'BAR', - 'kind' => SymbolKind::CONSTANT, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ]) - ] - ]; - } - - public function provideTraits(): Generator - { - yield 'trait' => [ - 'dummyRange(), - $this->dummyRange(), - null, - null, - children: [] - ), - ] - ]; - - yield 'property in trait' => [ - 'dummyRange(), - $this->dummyRange(), - null, - null, - children: [ - DocumentSymbol::fromArray([ - 'name' => 'foo', - 'kind' => SymbolKind::PROPERTY, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ), - ] - ]; - - yield 'method in trait' => [ - 'dummyRange(), - $this->dummyRange(), - null, - null, - children: [ - DocumentSymbol::fromArray([ - 'name' => 'foo', - 'kind' => SymbolKind::METHOD, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ), - ] - ]; - } - - public function provideEnums(): Generator - { - yield 'enum' => [ - 'dummyRange(), - $this->dummyRange(), - null, - null, - children: [] - ), - ] - ]; - - yield 'members of enum' => [ - 'dummyRange(), - $this->dummyRange(), - null, - null, - children: [ - DocumentSymbol::fromArray([ - 'name' => 'BAR', - 'kind' => SymbolKind::ENUM_MEMBER, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - DocumentSymbol::fromArray([ - 'name' => 'SPAM', - 'kind' => SymbolKind::ENUM_MEMBER, - 'range' => $this->dummyRange(), - 'selectionRange' => $this->dummyRange(), - 'children' => [], - ]), - ] - ), - ] - ]; - } - - private function dummyRange(): Range - { - return ProtocolFactory::range(0, 0, self::DUMMY_RANGE, 0); - } - - /** - * @param DocumentSymbol[] $actual - * @param DocumentSymbol[] $expected - * @throws InvalidArgumentException - * @throws FrameworkException - * @throws ExpectationFailedException - */ - private function assertTree(array $actual, array $expected): void - { - self::assertCount(count($expected), $actual, 'Expected number of children'); - - foreach ($actual as $index => $symbol) { - assert($symbol instanceof DocumentSymbol); - $expectedSymbol = $expected[$index]; - self::assertNotNull($expected, 'Missing document symbol'); - assert($expectedSymbol instanceof DocumentSymbol); - - if ($expectedSymbol->range->end->line === self::DUMMY_RANGE) { - $expectedSymbol->range = $symbol->range; - } - if ($expectedSymbol->selectionRange->end->line === self::DUMMY_RANGE) { - $expectedSymbol->selectionRange = $symbol->selectionRange; - } - - $actualChildren = $symbol->children; - $symbol->children = []; - $expectedChildren = $expectedSymbol->children; - $expectedSymbol->children = []; - - self::assertEquals($expectedSymbol, $symbol); - if (null !== $actualChildren) { - self::assertNotNull($expectedChildren); - if (null !== $expectedChildren) { - $this->assertTree($actualChildren, $expectedChildren); - } - } else { - self::assertNull($expectedChildren); - } - } - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/DiagnosticProvider/WorseDiagnosticProvider.php b/lib/Extension/LanguageServerWorseReflection/DiagnosticProvider/WorseDiagnosticProvider.php deleted file mode 100644 index 6fbe4c45aa..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/DiagnosticProvider/WorseDiagnosticProvider.php +++ /dev/null @@ -1,98 +0,0 @@ -reflector->diagnostics(TextDocumentConverter::fromLspTextItem($textDocument)) as $diagnostic) { - /** @var Diagnostic $diagnostic */ - $range = RangeConverter::toLspRange($diagnostic->range(), $textDocument->text); - - $lspDiagnostic = ProtocolFactory::diagnostic($range, $diagnostic->message()); - $lspDiagnostic->severity = self::toLspSeverity($diagnostic->severity()); - $lspDiagnostic->source = 'phpactor'; - $lspDiagnostic->tags = self::toLspTags($diagnostic->tags()); - $lspDiagnostic->code = 'worse.'.$diagnostic->code(); - - if ($diagnostic instanceof DeprecatedUsageDiagnostic) { - $lspDiagnostic->tags[] = DiagnosticTag::DEPRECATED; - } - - if ($diagnostic instanceof UnusedImportDiagnostic) { - $lspDiagnostic->tags[] = DiagnosticTag::UNNECESSARY; - } - - $lspDiagnostics[] = $lspDiagnostic; - if ($cancel->isRequested()) { - return $lspDiagnostics; - } - } - - return $lspDiagnostics; - }); - } - - public function name(): string - { - return 'worse'; - } - - /** - * @return LanguageServerProtocolDiagnosticSeverity::* - */ - private static function toLspSeverity(DiagnosticSeverity $diagnosticSeverity): int - { - if ($diagnosticSeverity->isError()) { - return LanguageServerProtocolDiagnosticSeverity::ERROR; - } - - if ($diagnosticSeverity->isWarning()) { - return LanguageServerProtocolDiagnosticSeverity::WARNING; - } - if ($diagnosticSeverity->isHint()) { - return LanguageServerProtocolDiagnosticSeverity::HINT; - } - - return LanguageServerProtocolDiagnosticSeverity::INFORMATION; - } - /** - * @param array $tags - * - * @return array - */ - private static function toLspTags(array $tags): array - { - return array_map( - fn ($tag) => match($tag) { - PhpactorDiagnosticTag::DEPRECATED => DiagnosticTag::DEPRECATED, - PhpactorDiagnosticTag::UNNECESSARY => DiagnosticTag::UNNECESSARY, - }, - $tags - ); - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Handler/InlayHintHandler.php b/lib/Extension/LanguageServerWorseReflection/Handler/InlayHintHandler.php deleted file mode 100644 index f0b73c47c8..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Handler/InlayHintHandler.php +++ /dev/null @@ -1,48 +0,0 @@ - 'inlayHint', - ]; - } - - /** - * @return Promise> - */ - public function inlayHint(TextDocumentIdentifier $textDocument, Range $range): Promise - { - $document = $this->workspace->get($textDocument->uri); - - return $this->provider->inlayHints( - TextDocumentConverter::fromLspTextItem($document), - RangeConverter::toPhpactorRange($range, $document->text) - ); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->inlayHintProvider = true; - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/InlayHint/InlayHintOptions.php b/lib/Extension/LanguageServerWorseReflection/InlayHint/InlayHintOptions.php deleted file mode 100644 index ef54445226..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/InlayHint/InlayHintOptions.php +++ /dev/null @@ -1,12 +0,0 @@ -> - */ - public function inlayHints(TextDocument $document, ByteOffsetRange $range): Promise - { - // ensure we only process one inlay request at a time - if ($this->previousCancellationSource) { - $this->previousCancellationSource->cancel(); - } - $cancellationSource = new CancellationTokenSource(); - $this->previousCancellationSource = $cancellationSource; - $cancellation = $cancellationSource->getToken(); - - return call(function () use ($document, $range, $cancellation) { - $walker = new InlayHintWalker($range, $this->options); - foreach ($this->reflector->walk($document, $walker) as $tick) { - yield delay(0); - if ($cancellation->isRequested()) { - return $walker->hints(); - } - } - - return $walker->hints(); - }); - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/InlayHint/InlayHintWalker.php b/lib/Extension/LanguageServerWorseReflection/InlayHint/InlayHintWalker.php deleted file mode 100644 index 1e9fccabdb..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/InlayHint/InlayHintWalker.php +++ /dev/null @@ -1,240 +0,0 @@ -getStartPosition() < $this->range->start()->toInt()) { - return $frame; - } - if ($node->getEndPosition() > $this->range->end()->toInt()) { - return $frame; - } - if ($this->options->types && $node instanceof Variable) { - $this->fromVariable($resolver, $frame, $node); - } - if ($this->options->types && $node instanceof ClassLike) { - $this->fromClassLike($resolver, $frame, $node); - } - if ($this->options->types && $node instanceof FunctionLike) { - $this->fromFunctionLike($resolver, $frame, $node); - } - if ($this->options->params && $node instanceof CallExpression) { - $this->fromCall($resolver, $frame, $node); - } - if ($this->options->params && $node instanceof ObjectCreationExpression) { - $this->fromObjectCreation($resolver, $frame, $node); - } - return $frame; - } - - - /** - * @return InlayHint[] - */ - public function hints(): array - { - return $this->hints; - } - - private function fromCall(FrameResolver $resolver, Frame $frame, CallExpression $node): void - { - $parameters = (function (NodeContext $context): ?ReflectionParameterCollection { - if ($context instanceof MemberAccessContext) { - $method = $context->accessedMember(); - if (!$method instanceof ReflectionMethod) { - return null; - } - return $method->parameters(); - } - - if ($context instanceof FunctionCallContext) { - return $context->function()->parameters(); - } - - return null; - })($resolver->resolveNode($frame, $node)); - - if (null === $parameters) { - return; - } - - foreach ($node->argumentExpressionList?->getValues() ?? [] as $index => $argument) { - if (!$argument instanceof ArgumentExpression) { - continue; - } - if ($argument->name) { - continue; - } - $parameter = $parameters->at($index); - if (null === $parameter) { - break; - } - - if ($argument->expression instanceof Variable) { - $name = NodeUtil::nameFromTokenOrNode($argument->expression, $argument->expression->name); - if (ltrim($name, '$') === $parameter->name()) { - continue; - } - } - $this->hints[] = new InlayHint( - position: PositionConverter::intByteOffsetToPosition($argument->getStartPosition(), $node->getFileContents()), - label: sprintf('%s:', $parameter->name()), - kind: InlayHintKind::PARAMETER, - textEdits: null, - tooltip: $parameter->type()->__toString(), - paddingRight: true, - ); - } - } - - private function fromVariable(FrameResolver $resolver, Frame $frame, Variable $node): void - { - $name = $node->getName(); - if (!$node->parent instanceof AssignmentExpression) { - return; - } - - if (null === $name) { - return; - } - - $variable = $resolver->resolveNode($frame, $node); - - if (false === $variable->type()->isDefined()) { - return; - } - - $this->hints[] = new InlayHint( - position: PositionConverter::intByteOffsetToPosition( - $node->getEndPosition(), - $node->getFileContents() - ), - label: sprintf(': %s', $variable->type()->short()), - tooltip: $variable->type()->__toString(), - kind: InlayHintKind::TYPE, - textEdits: null, - ); - } - - private function fromObjectCreation(FrameResolver $resolver, Frame $frame, ObjectCreationExpression $node): void - { - $context = $resolver->resolveNode($frame, $node); - if (!$context instanceof ClassLikeContext) { - return; - } - - $method = $context->classLike()->methods()->byName('__construct')->firstOrNull(); - - if (!$method instanceof ReflectionMethod) { - return; - } - - $parameters = $method->parameters(); - foreach ($node->argumentExpressionList?->getValues() ?? [] as $index => $argument) { - if (!$argument instanceof ArgumentExpression) { - continue; - } - if ($argument->name !== null) { - continue; - } - $parameter = $parameters->at($index); - if (null === $parameter) { - break; - } - $this->hints[] = new InlayHint( - position: PositionConverter::intByteOffsetToPosition($argument->getStartPosition(), $node->getFileContents()), - label: sprintf('%s:', $parameter->name()), - kind: InlayHintKind::PARAMETER, - textEdits: null, - tooltip: $parameter->type()->__toString(), - paddingRight: true, - ); - } - } - - private function fromClassLike(FrameResolver $resolver, Frame $frame, Node&ClassLike $node): void - { - if (!$node instanceof ClassDeclaration && !$node instanceof EnumDeclaration && !$node instanceof TraitDeclaration && !$node instanceof InterfaceDeclaration) { - return; - } - $context = $resolver->resolveNode($frame, $node); - - $this->hints[] = new InlayHint( - position: PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $node->getFileContents()), - label: sprintf('%s %s', $context->symbol()->symbolType(), $context->symbol()->name()), - kind: InlayHintKind::TYPE, - textEdits: null, - paddingLeft: true, - ); - } - - private function fromFunctionLike(FrameResolver $resolver, Frame $frame, Node&FunctionLike $node): void - { - if (!$node instanceof MethodDeclaration && !$node instanceof FunctionDeclaration) { - return; - } - $name = NodeUtil::nameFromTokenOrNode($node, $node->name); - $type = $node instanceof MethodDeclaration ? 'method' : 'function'; - - $this->hints[] = new InlayHint( - position: PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $node->getFileContents()), - label: sprintf('%s %s', $type, $name), - kind: InlayHintKind::TYPE, - textEdits: null, - paddingLeft: true, - ); - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/LanguageServerWorseReflectionExtension.php b/lib/Extension/LanguageServerWorseReflection/LanguageServerWorseReflectionExtension.php deleted file mode 100644 index fead350617..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/LanguageServerWorseReflectionExtension.php +++ /dev/null @@ -1,118 +0,0 @@ -registerSourceLocator($container); - - $container->register(StubValidationListener::class, function (Container $container) { - return new StubValidationListener( - $container->get(ClientApi::class), - WorseReflectionExtension::additiveStubPaths($container) - ); - }, [ LanguageServerExtension::TAG_LISTENER_PROVIDER => [] ]); - } - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_UPDATE_INTERVAL => 100, - self::PARAM_INLAY_HINTS_ENABLE => false, - self::PARAM_INLAY_HINTS_TYPES => false, - self::PARAM_INLAY_HINTS_PARAMS => true, - self::PARAM_DIAGNOSTICS_ENABLE => true, - ]); - $schema->setDescriptions([ - self::PARAM_UPDATE_INTERVAL => 'Minimum interval to update the workspace index as documents are updated (in milliseconds)', - self::PARAM_INLAY_HINTS_ENABLE => 'Enable inlay hints (experimental)', - self::PARAM_INLAY_HINTS_TYPES => 'Show inlay type hints for variables', - self::PARAM_INLAY_HINTS_PARAMS => 'Show inlay hints for parameters', - self::PARAM_DIAGNOSTICS_ENABLE => 'Enable diagnostics', - ]); - } - - private function registerSourceLocator(ContainerBuilder $container): void - { - $container->register(WorkspaceSourceLocator::class, function (Container $container) { - return new WorkspaceSourceLocator( - $container->get(WorkspaceIndex::class) - ); - }, [ WorseReflectionExtension::TAG_SOURCE_LOCATOR => [ - 'priority' => 255, - ]]); - - $container->register(WorkspaceIndexListener::class, function (Container $container) { - return new WorkspaceIndexListener( - $container->get(WorkspaceIndex::class), - ); - }, [ LanguageServerExtension::TAG_LISTENER_PROVIDER => [] ]); - - $container->register(InvalidateDocumentCacheListener::class, function (Container $container) { - return new InvalidateDocumentCacheListener($container->get(CacheForDocument::class)); - }, [ LanguageServerExtension::TAG_LISTENER_PROVIDER => [] ]); - - $container->register(WorkspaceIndex::class, function (Container $container) { - return new WorkspaceIndex( - ReflectorBuilder::create()->build(), - $container->parameter(self::PARAM_UPDATE_INTERVAL)->int() - ); - }); - - $container->register(WorseDiagnosticProvider::class, function (Container $container) { - if (false === $container->parameter(self::PARAM_DIAGNOSTICS_ENABLE)->bool()) { - return null; - } - return new WorseDiagnosticProvider( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - ); - }, [ LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('code-action', true) ]); - - $container->register(InlayHintHandler::class, function (Container $container) { - if (false === $container->parameter(self::PARAM_INLAY_HINTS_ENABLE)->bool()) { - return null; - } - return new InlayHintHandler( - new InlayHintProvider( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, SourceCodeReflector::class), - new InlayHintOptions( - $container->parameter(self::PARAM_INLAY_HINTS_TYPES)->bool(), - $container->parameter(self::PARAM_INLAY_HINTS_PARAMS)->bool(), - ) - ), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class) - ); - }, [ LanguageServerExtension::TAG_METHOD_HANDLER => []]); - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Listener/InvalidateDocumentCacheListener.php b/lib/Extension/LanguageServerWorseReflection/Listener/InvalidateDocumentCacheListener.php deleted file mode 100644 index 0505700c29..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Listener/InvalidateDocumentCacheListener.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ - public function getListenersForEvent($event): iterable - { - if ($event instanceof TextDocumentUpdated) { - yield function () use ($event): void { - $this->cache->purge(TextDocumentUri::fromString($event->identifier()->uri)); - }; - } - if ($event instanceof TextDocumentClosed) { - yield function () use ($event): void { - $this->cache->purge(TextDocumentUri::fromString($event->identifier()->uri)); - }; - } - if ($event instanceof TextDocumentSaved) { - yield function () use ($event): void { - $this->cache->purge(TextDocumentUri::fromString($event->identifier()->uri)); - }; - } - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Listener/StubValidationListener.php b/lib/Extension/LanguageServerWorseReflection/Listener/StubValidationListener.php deleted file mode 100644 index b209841636..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Listener/StubValidationListener.php +++ /dev/null @@ -1,50 +0,0 @@ - $stubPaths - */ - public function __construct( - private ClientApi $api, - private array $stubPaths - ) { - } - - /** - * @return iterable - */ - public function getListenersForEvent($event): iterable - { - if (!$event instanceof Initialized) { - return; - } - - - yield function (): void { - $invalidPaths = []; - foreach ($this->stubPaths as $stubPath) { - if (file_exists($stubPath) && is_file($stubPath)) { - continue; - } - - $invalidPaths[] = $stubPath; - } - - if ([] === $invalidPaths) { - return; - } - - $this->api->window()->showMessage()->warning(sprintf( - 'The following stubs could not be found or were not files: "%s"', - implode('", "', $invalidPaths) - )); - }; - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/SourceLocator/WorkspaceSourceLocator.php b/lib/Extension/LanguageServerWorseReflection/SourceLocator/WorkspaceSourceLocator.php deleted file mode 100644 index f58571400e..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/SourceLocator/WorkspaceSourceLocator.php +++ /dev/null @@ -1,29 +0,0 @@ -index->documentForName($name)) { - throw new SourceNotFound(sprintf( - 'Class "%s" not found', - (string) $name - )); - } - - return $document; - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Tests/Benchmark/WorkspaceIndexBench.php b/lib/Extension/LanguageServerWorseReflection/Tests/Benchmark/WorkspaceIndexBench.php deleted file mode 100644 index 0944635872..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Tests/Benchmark/WorkspaceIndexBench.php +++ /dev/null @@ -1,58 +0,0 @@ -tester = $this->createTester(); - $this->tester->initialize(); - $this->tester->textDocument()->open('file:///foobar', ''); - } - - /** - * @ParamProviders({"provideUpdate"}) - * @Revs(10) - * @Iterations(10) - */ - public function benchUpdate(array $params): void - { - $this->tester->textDocument()->update('file:///foobar', $params['text']); - } - - public function provideUpdate(): Generator - { - $source = mb_str_split((string)file_get_contents( - __DIR__ . '/source/source.php.example' - ), 1); - - $buffer = ''; - - foreach ($source as $index => $char) { - $buffer .= $char; - if (0 === $index % 1000) { - yield 'length: ' . strlen($buffer) => [ - 'text' => $buffer - ]; - } - } - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Tests/Benchmark/source/source.php.example b/lib/Extension/LanguageServerWorseReflection/Tests/Benchmark/source/source.php.example deleted file mode 100644 index c6d54caefd..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Tests/Benchmark/source/source.php.example +++ /dev/null @@ -1,195 +0,0 @@ -reflector = $reflector; - $this->renderer = $renderer; - $this->workspace = $workspace; - } - - public function methods(): array - { - return [ - 'textDocument/hover' => 'hover', - ]; - } - - public function hover( - TextDocumentIdentifier $textDocument, - Position $position - ): Promise { - return \Amp\call(function () use ($textDocument, $position) { - $document = $this->workspace->get($textDocument->uri); - $offset = PositionConverter::positionToByteOffset($position, $document->text); - $document = TextDocumentBuilder::create($document->text) - ->uri($document->uri) - ->language('php') - ->build(); - - $offsetReflection = $this->reflector->reflectOffset($document, $offset); - $nodeContext = $offsetReflection->nodeContext(); - $info = $this->infoFromReflecionOffset($offsetReflection); - $string = new MarkupContent('markdown', $info); - - return new Hover($string, new Range( - PositionConverter::byteOffsetToPosition( - ByteOffset::fromInt($nodeContext->symbol()->position()->start()), - $document->__toString() - ), - PositionConverter::byteOffsetToPosition( - ByteOffset::fromInt($nodeContext->symbol()->position()->end()), - $document->__toString() - ) - )); - }); - } - - public function registerCapabiltiies(ServerCapabilities $capabilities): void - { - $capabilities->hoverProvider = true; - } - - private function infoFromReflecionOffset(ReflectionOffset $offset): string - { - $nodeContext = $offset->nodeContext(); - - if ($info = $this->infoFromSymbolContext($nodeContext)) { - return $info; - } - - return $this->renderer->render($offset); - } - - private function infoFromSymbolContext(SymbolContext $nodeContext): ?string - { - try { - return $this->renderSymbolContext($nodeContext); - } catch (CouldNotFormat $e) { - } - - return null; - } - - private function renderSymbolContext(SymbolContext $nodeContext): ?string - { - switch ($nodeContext->symbol()->symbolType()) { - case Symbol::METHOD: - case Symbol::PROPERTY: - case Symbol::CONSTANT: - return $this->renderMember($nodeContext); - case Symbol::CLASS_: - return $this->renderClass($nodeContext->type()); - case Symbol::FUNCTION: - return $this->renderFunction($nodeContext); - } - - return null; - } - - private function renderMember(SymbolContext $nodeContext): string - { - $name = $nodeContext->symbol()->name(); - $container = $nodeContext->containerType(); - - try { - $class = $this->reflector->reflectClassLike((string) $container); - $member = null; - $sep = '#'; - - // note that all class-likes (classes, traits and interfaces) have - // methods but not all have constants or properties, so we play safe - // with members() which is first-come-first-serve, rather than risk - // a fatal error because of a non-existing method. - $symbolType = $nodeContext->symbol()->symbolType(); - switch ($symbolType) { - case Symbol::METHOD: - $member = $class->methods()->get($name); - $sep = '#'; - break; - case Symbol::CONSTANT: - $sep = '::'; - $member = $class->members()->get($name); - break; - case Symbol::PROPERTY: - $sep = '$'; - $member = $class->members()->get($name); - break; - default: - return sprintf('Unknown symbol type "%s"', $symbolType); - } - - return $this->renderer->render(new HoverInformation( - (string)$container.' '.$sep.' '.(string)$member->name(), - $this->renderer->render( - new MemberDocblock($member) - ), - $member - )); - } catch (NotFound $e) { - return $e->getMessage(); - } - } - - private function renderFunction(SymbolContext $nodeContext): string - { - $name = $nodeContext->symbol()->name(); - $function = $this->reflector->reflectFunction($name); - - return $this->renderer->render(new HoverInformation($name, $function->docblock()->formatted(), $function)); - } - - private function renderClass(Type $type): string - { - try { - $class = $this->reflector->reflectClassLike((string) $type); - return $this->renderer->render(new HoverInformation((string)$type, $class->docblock()->formatted(), $class)); - } catch (NotFound $e) { - return $e->getMessage(); - } - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Tests/InlayHint/InlayHintProviderTest.php b/lib/Extension/LanguageServerWorseReflection/Tests/InlayHint/InlayHintProviderTest.php deleted file mode 100644 index 82c8b326e3..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Tests/InlayHint/InlayHintProviderTest.php +++ /dev/null @@ -1,159 +0,0 @@ -): void $assertion - */ - #[DataProvider('provideInlayHintProvider')] - public function testInlayHintProvider( - string $source, - Closure $assertion - ): void { - $hints = wait((new InlayHintProvider( - ReflectorBuilder::create()->addSource($source)->build(), - new InlayHintOptions(true, true), - ))->inlayHints( - TextDocumentBuilder::create( - $source - )->build(), - ByteOffsetRange::fromInts( - 0, - strlen($source) - ), - )); - $assertion($hints); - } - - /** - * @return Generator): void}> - */ - public static function provideInlayHintProvider(): Generator - { - yield 'inlay hint for member' => [ - 'bar("hello");', - function (array $hints): void { - self::assertCount(3, $hints); - $hint = $hints[2]; - assert($hint instanceof InlayHint); - self::assertEquals(0, $hint->position->line); - self::assertEquals('bar:', $hint->label); - self::assertEquals('string', $hint->tooltip); - } - ]; - yield 'inlay hint for variable only definitions' => [ - 'position->line); - self::assertEquals(': string', $hint->label); - self::assertEquals('"foo"', $hint->tooltip); - } - ]; - yield 'inlay hint for variable only definitions 2' => [ - 'bar();', - function (array $hints): void { - self::assertCount(1, $hints); - $hint = $hints[0]; - assert($hint instanceof InlayHint); - self::assertEquals(0, $hint->position->line); - self::assertEquals(': string', $hint->label); - self::assertEquals('"foo"', $hint->tooltip); - } - ]; - yield 'inlay hint for variable only definitions 3' => [ - 'bar(); $bar = $foo->bar();', - function (array $hints): void { - self::assertCount(4, $hints); - $hint = $hints[2]; - assert($hint instanceof InlayHint); - self::assertEquals(0, $hint->position->line); - self::assertEquals(': Fo', $hint->label); - } - ]; - yield 'inlay hint for variable only definitions 4' => [ - 'bar() == "bar") {}; }};', - function (array $hints): void { - self::assertCount(2, $hints); - } - ]; - yield 'inlay hint for class instantiation' => [ - 'position->line); - self::assertEquals('b:', $hint->label); - } - ]; - yield 'inlay hint for function call' => [ - 'position->line); - self::assertEquals('b:', $hint->label); - } - ]; - yield 'inlay hint for function call ignore named' => [ - ' [ - ' [ - 'position->line); - self::assertEquals('arr:', $hint->label); - } - ]; - yield 'inlay hint for class' => [ - 'position->line); - self::assertEquals('class Foobar', $hint->label); - } - ]; - yield 'inlay hint for method' => [ - 'position->line); - self::assertEquals('class Foobar', $hint->label); - } - ]; - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Tests/IntegrationTestCase.php b/lib/Extension/LanguageServerWorseReflection/Tests/IntegrationTestCase.php deleted file mode 100644 index 7aaa1c5ebf..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,46 +0,0 @@ - __DIR__ . '/../', - FilePathResolverExtension::PARAM_PROJECT_ROOT => $this->workspace()->path(), - WorseReflectionExtension::PARAM_ENABLE_CACHE=> false, - ]); - - return $container; - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Tests/Unit/DiagnosticProvider/WorseDiagnosticProviderTest.php b/lib/Extension/LanguageServerWorseReflection/Tests/Unit/DiagnosticProvider/WorseDiagnosticProviderTest.php deleted file mode 100644 index f4368992af..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Tests/Unit/DiagnosticProvider/WorseDiagnosticProviderTest.php +++ /dev/null @@ -1,41 +0,0 @@ -addDiagnosticProvider(new InMemoryDiagnosticProvider([ - new BareDiagnostic(ByteOffsetRange::fromInts(1, 1), DiagnosticSeverity::WARNING(), 'Foo', 'foo') - ]))->build(); - - $cancel = (new CancellationTokenSource())->getToken(); - $lspDiagnostics = wait(( - new WorseDiagnosticProvider($reflector) - )->provideDiagnostics( - ProtocolFactory::textDocumentItem('file:///foo', 'foo'), - $cancel - )); - - /** @var Diagnostic[] $lspDiagnostics */ - self::assertCount(1, $lspDiagnostics); - self::assertInstanceOf(Diagnostic::class, $lspDiagnostics[0]); - self::assertEquals('Foo', $lspDiagnostics[0]->message); - self::assertEquals('worse.foo', $lspDiagnostics[0]->code); - self::assertEquals(PhpactorDiagnosticSeverity::WARNING, $lspDiagnostics[0]->severity); - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Tests/Unit/LanguageServerWorseReflectionExtensionTest.php b/lib/Extension/LanguageServerWorseReflection/Tests/Unit/LanguageServerWorseReflectionExtensionTest.php deleted file mode 100644 index e4ecd0897c..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Tests/Unit/LanguageServerWorseReflectionExtensionTest.php +++ /dev/null @@ -1,15 +0,0 @@ -container()->get(WorkspaceSourceLocator::class); - self::assertInstanceOf(WorkspaceSourceLocator::class, $locator); - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Tests/Unit/Listener/StubValidationListenerTest.php b/lib/Extension/LanguageServerWorseReflection/Tests/Unit/Listener/StubValidationListenerTest.php deleted file mode 100644 index 5f16661547..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Tests/Unit/Listener/StubValidationListenerTest.php +++ /dev/null @@ -1,81 +0,0 @@ -workspace()->reset(); - } - - public function testFileDoesntExist(): void - { - $paths = [ - $this->workspace()->path('foobar.php'), - ]; - $builder = $this->createBuilder($paths); - $tester = $builder->build(); - $tester->initialize(); - $notification = $builder->transmitter()->shiftNotification(); - self::assertNotNull($notification); - self::assertIsString($notification->params['message'] ?? ''); - self::assertStringContainsString( - 'The following stubs could not be found', - $notification->params['message'] ?? '' - ); - } - - public function testNotAFile(): void - { - $this->workspace()->mkdir('foobar'); - - $paths = [ - $this->workspace()->path('foobar'), - ]; - $builder = $this->createBuilder($paths); - $tester = $builder->build(); - $tester->initialize(); - $notification = $builder->transmitter()->shiftNotification(); - self::assertNotNull($notification); - self::assertIsString($notification->params['message'] ?? ''); - self::assertStringContainsString( - 'The following stubs could not be found', - $notification->params['message'] ?? '' - ); - } - - public function testValidFiles(): void - { - $this->workspace()->put('foobar1.stub', ''); - $this->workspace()->put('foobar2.stub', ''); - - $paths = [ - $this->workspace()->path('foobar1.stub'), - $this->workspace()->path('foobar2.stub'), - ]; - $builder = $this->createBuilder($paths); - $tester = $builder->build(); - $tester->initialize(); - $notification = $builder->transmitter()->shiftNotification(); - self::assertNull($notification); - } - - /** - * @param list $paths - */ - private function createBuilder(array $paths): LanguageServerTesterBuilder - { - $builder = LanguageServerTesterBuilder::create(); - $listener = (new StubValidationListener( - $builder->clientApi(), - $paths, - )); - $builder->addListenerProvider($listener); - return $builder; - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php b/lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php deleted file mode 100644 index ef67a3f054..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php +++ /dev/null @@ -1,146 +0,0 @@ - - */ - private $byName = []; - /** - * @var array - */ - private $documents = []; - /** - * @var array> - */ - private $documentToNameMap = []; - - /** - * @var TextDocument|null - */ - private $documentToUpdate; - - /** - * @var bool - */ - private $waiting = false; - - public function __construct(private SourceCodeReflector $reflector, - private int $updateInterval = 1000) - { - } - - public function documentForName(Name $name): ?TextDocument - { - return $this->byName[$name->full()] ?? null; - } - - public function index(TextDocument $textDocument): void - { - $this->documents[(string)$textDocument->uri()] = $textDocument; - $this->updateDocument($textDocument); - } - - /** - * Refresh the document. - * - * In order to prevent continuous reparsing the document will - * only be refreshed at the sepecified interval. - */ - private function updateDocument(TextDocument $textDocument): void - { - if ($this->waiting) { - $this->documentToUpdate = $textDocument; - return; - } - - $this->documentToUpdate = null; - - $newNames = []; - foreach ($this->reflector->reflectClassesIn($textDocument) as $reflectionClass) { - $newNames[] = $reflectionClass->name()->full(); - } - - foreach ($this->reflector->reflectFunctionsIn($textDocument) as $reflectionFunction) { - $newNames[] = $reflectionFunction->name()->full(); - } - - $this->updateNames( - $textDocument, - $newNames, - $this->documentToNameMap[(string)$textDocument->uri()] ?? [] - ); - - if ($this->updateInterval === 0) { - return; - } - - $this->waiting = true; - - asyncCall(function () { - yield delay($this->updateInterval); - - $this->waiting = false; - - if (null === $this->documentToUpdate) { - return; - } - - $this->updateDocument($this->documentToUpdate); - }); - } - - private function updateNames(TextDocument $textDocument, array $newNames, array $currentNames): void - { - $namesToRemove = array_diff($currentNames, $newNames); - - foreach ($newNames as $name) { - $this->byName[(string)$name] = $textDocument; - } - foreach ($namesToRemove as $name) { - unset($this->byName[$name]); - } - - if ($newNames !== []) { - $this->documentToNameMap[(string)$textDocument->uri()] = $newNames; - } else { - unset($this->documentToNameMap[(string)$textDocument->uri()]); - } - } - - public function update(TextDocumentUri $textDocumentUri, string $updatedText): void - { - $textDocument = $this->documents[(string)$textDocumentUri] ?? null; - if ($textDocument === null) { - throw new RuntimeException(sprintf( - 'Could not find document "%s"', - $textDocumentUri->__toString() - )); - } - $this->updateDocument(TextDocumentBuilder::fromTextDocument($textDocument)->text($updatedText)->build()); - } - - public function remove(TextDocumentUri $textDocumentUri): void - { - $textDocument = $this->documents[(string)$textDocumentUri] ?? null; - if ($textDocument === null) { - throw new RuntimeException(sprintf( - 'Could not find document "%s"', - $textDocumentUri->__toString() - )); - } - $this->updateNames($textDocument, [], $this->documentToNameMap[(string)$textDocument->uri()] ?? []); - unset($this->documents[(string)$textDocumentUri]); - } -} diff --git a/lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndexListener.php b/lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndexListener.php deleted file mode 100644 index 74b57d5a01..0000000000 --- a/lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndexListener.php +++ /dev/null @@ -1,60 +0,0 @@ -updated(...)]; - } - - if ($event instanceof TextDocumentClosed) { - return [$this->closed(...)]; - } - - if ($event instanceof TextDocumentOpened) { - return [$this->opened(...)]; - } - - return []; - } - - public function opened(TextDocumentOpened $opened): void - { - $item = $opened->textDocument(); - $builder = TextDocumentBuilder::create($item->text ?? ''); - - if ($item->uri) { - $builder->uri($item->uri); - } - - if ($item->languageId) { - $builder->language($item->languageId); - } - - $this->index->index($builder->build()); - } - - public function updated(TextDocumentUpdated $updated): void - { - $this->index->update(TextDocumentUri::fromString($updated->identifier()->uri), $updated->updatedText()); - } - - public function closed(TextDocumentClosed $removed): void - { - $this->index->remove(TextDocumentUri::fromString($removed->identifier()->uri)); - } -} diff --git a/lib/Extension/Logger/Formatter/FormatterRegistry.php b/lib/Extension/Logger/Formatter/FormatterRegistry.php deleted file mode 100644 index efdcaeb265..0000000000 --- a/lib/Extension/Logger/Formatter/FormatterRegistry.php +++ /dev/null @@ -1,29 +0,0 @@ -serviceMap[$alias])) { - throw new RuntimeException(sprintf( - 'Could not find formatter with alias "%s", known formatters: "%s"', - $alias, - implode('", "', array_keys($this->serviceMap)) - )); - } - - return $this->container->get($this->serviceMap[$alias]); - } -} diff --git a/lib/Extension/Logger/Formatter/PrettyFormatter.php b/lib/Extension/Logger/Formatter/PrettyFormatter.php deleted file mode 100644 index c91f2bc450..0000000000 --- a/lib/Extension/Logger/Formatter/PrettyFormatter.php +++ /dev/null @@ -1,48 +0,0 @@ -color($record['level_name']) . substr($record['level_name'], 0, 4)."\e[0;0m", - "\e[1;37m".substr($record['datetime']->format('U.u'), 4)."\e[0;0m", - $record['message'] - ); - - return $message."\n"; - } - - - public function formatBatch(array $records): void - { - } - - private function color(string $level): string - { - switch (strtolower($level)) { - case LogLevel::EMERGENCY: - case LogLevel::CRITICAL: - case LogLevel::ERROR: - return "\e[0;31m"; - case LogLevel::WARNING: - return "\e[0;33m"; - case LogLevel::ALERT: - case LogLevel::NOTICE: - return "\e[0;36m"; - case LogLevel::INFO: - return "\e[0;32m"; - case LogLevel::DEBUG: - return "\e[0;37m"; - } - - return "\e[0;0m"; - } -} diff --git a/lib/Extension/Logger/Logger/ChannelLogger.php b/lib/Extension/Logger/Logger/ChannelLogger.php deleted file mode 100644 index c9a3a40167..0000000000 --- a/lib/Extension/Logger/Logger/ChannelLogger.php +++ /dev/null @@ -1,26 +0,0 @@ -innerLogger->log( - $level, - $message, - array_merge([ - 'channel' => $this->name, - ], $context), - ); - } -} diff --git a/lib/Extension/Logger/LoggerFactory.php b/lib/Extension/Logger/LoggerFactory.php deleted file mode 100644 index 0f81249b4b..0000000000 --- a/lib/Extension/Logger/LoggerFactory.php +++ /dev/null @@ -1,18 +0,0 @@ -mainLogger); - } -} diff --git a/lib/Extension/Logger/LoggingExtension.php b/lib/Extension/Logger/LoggingExtension.php deleted file mode 100644 index 4f24301f92..0000000000 --- a/lib/Extension/Logger/LoggingExtension.php +++ /dev/null @@ -1,151 +0,0 @@ -setDefaults([ - self::PARAM_ENABLED => false, - self::PARAM_FINGERS_CROSSED => false, - self::PARAM_PATH => 'application.log', - self::PARAM_LEVEL => LogLevel::WARNING, - self::PARAM_NAME => 'logger', - self::PARAM_FORMATTER => null, - ]); - - $schema->setTypes([ - self::PARAM_ENABLED => 'boolean', - self::PARAM_FINGERS_CROSSED => 'boolean', - self::PARAM_PATH => 'string', - self::PARAM_LEVEL => 'string', - self::PARAM_NAME => 'string', - ]); - - $schema->setEnums([ - self::PARAM_LEVEL => [ - LogLevel::EMERGENCY, - LogLevel::ALERT, - LogLevel::CRITICAL, - LogLevel::ERROR, - LogLevel::WARNING, - LogLevel::NOTICE, - LogLevel::INFO, - LogLevel::DEBUG, - ], - ]); - } - - public function load(ContainerBuilder $container): void - { - $this->registerInfrastructure($container); - $this->registerFormatters($container); - } - - public static function channelLogger(Container $container, string $name): LoggerInterface - { - return (new LoggerFactory($container->get(self::SERVICE_LOGGER)))->get($name); - } - - private function registerInfrastructure(ContainerBuilder $container): void - { - $container->register(self::SERVICE_LOGGER, function (Container $container) { - $logger = new Logger('phpactor'); - - if (false === $container->parameter(self::PARAM_ENABLED)->bool()) { - $logger->pushHandler(new NullHandler()); - return $logger; - } - - $handler = new StreamHandler( - $container->parameter(self::PARAM_PATH)->string(), - $container->parameter(self::PARAM_LEVEL)->string(), - ); - - if ($formatter = $container->parameter(self::PARAM_FORMATTER)->stringOrNull()) { - $handler->setFormatter( - $container->expect( - self::SERVICE_FORMATTER_REGISTRY, - FormatterRegistry::class - )->get($formatter) - ); - } - - if ($container->parameter(self::PARAM_FINGERS_CROSSED)->bool()) { - $handler = new FingersCrossedHandler($handler); - } - - $logger->pushHandler($handler); - - - return $logger; - }); - - $container->register(self::SERVICE_FORMATTER_REGISTRY, function (Container $container) { - $serviceMap = []; - foreach ($container->getServiceIdsForTag(self::TAG_FORMATTER) as $serviceId => $attrs) { - if (!isset($attrs['alias'])) { - throw new RuntimeException(sprintf( - 'Logging service "%s" must provide an `alias` attribute', - $serviceId - )); - } - $serviceMap[$attrs['alias']] = $serviceId; - } - - return new FormatterRegistry($container, $serviceMap); - }); - } - - private function registerFormatters(ContainerBuilder $container): void - { - $container->register(PrettyFormatter::class, function () { - return new PrettyFormatter(); - }, [ - LoggingExtension::TAG_FORMATTER => [ - 'alias' => 'pretty', - ], - ]); - $container->register(LineFormatter::class, function () { - return new LineFormatter(); - }, [ - LoggingExtension::TAG_FORMATTER => [ - 'alias' => 'line', - ], - ]); - $container->register(JsonFormatter::class, function () { - return new JsonFormatter(); - }, [ - LoggingExtension::TAG_FORMATTER => [ - 'alias' => 'json', - ], - ]); - } -} diff --git a/lib/Extension/Logger/Tests/Unit/Formatter/FormatterRegistryTest.php b/lib/Extension/Logger/Tests/Unit/Formatter/FormatterRegistryTest.php deleted file mode 100644 index db7e872682..0000000000 --- a/lib/Extension/Logger/Tests/Unit/Formatter/FormatterRegistryTest.php +++ /dev/null @@ -1,40 +0,0 @@ -expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not find formatter'); - $container = $this->prophesize(ContainerInterface::class); - $registry = new FormatterRegistry($container->reveal(), [ - 'foo' => 'bar' - ]); - - $registry->get('zed'); - } - - public function testReturnsFormatter(): void - { - $container = $this->prophesize(ContainerInterface::class); - $formatter = $this->prophesize(FormatterInterface::class); - $registry = new FormatterRegistry($container->reveal(), [ - 'foo' => 'bar' - ]); - - $container->get('bar')->willReturn($formatter->reveal()); - - $this->assertSame($formatter->reveal(), $registry->get('foo')); - } -} diff --git a/lib/Extension/Logger/Tests/Unit/Formatter/PrettyFormatterTest.php b/lib/Extension/Logger/Tests/Unit/Formatter/PrettyFormatterTest.php deleted file mode 100644 index f8ee107618..0000000000 --- a/lib/Extension/Logger/Tests/Unit/Formatter/PrettyFormatterTest.php +++ /dev/null @@ -1,35 +0,0 @@ - 'info', - 'context' => [], - 'message' => 'hello', - 'datetime' => new DateTime(), - ]); - $formatter = new PrettyFormatter(); - $string = $formatter->format($record); - self::assertIsString($string); - } - - public static function provideFormat() - { - yield [ - ['level_name' => 'critical'], - ]; - yield [ - ['level_name' => 'unknown'], - ]; - } -} diff --git a/lib/Extension/Logger/Tests/Unit/LoggingExtensionTest.php b/lib/Extension/Logger/Tests/Unit/LoggingExtensionTest.php deleted file mode 100644 index 5892db1270..0000000000 --- a/lib/Extension/Logger/Tests/Unit/LoggingExtensionTest.php +++ /dev/null @@ -1,113 +0,0 @@ -create([ - LoggingExtension::PARAM_ENABLED => false, - ]); - $logger = $container->get('logging.logger'); - assert($logger instanceof Logger); - $handlers = $logger->getHandlers(); - $this->assertCount(1, $handlers); - $this->assertInstanceOf(NullHandler::class, $handlers[0]); - } - - #[DataProvider('provideLoggingFormatters')] - public function testLoggingFormatters(string $formatter): void - { - $container = $this->create([ - LoggingExtension::PARAM_ENABLED => true, - ]); - $logger = $container->get('logging.logger'); - assert($logger instanceof Logger); - $handlers = $logger->getHandlers(); - $this->assertCount(1, $handlers); - $this->assertInstanceOf(StreamHandler::class, $handlers[0]); - } - - public static function provideLoggingFormatters() - { - yield [ - 'line' - ]; - yield [ - 'json' - ]; - yield [ - 'pretty' - ]; - } - - public function testFingersCrossed(): void - { - $container = $this->create([ - LoggingExtension::PARAM_ENABLED => true, - LoggingExtension::PARAM_FINGERS_CROSSED => true, - ]); - $logger = $container->get('logging.logger'); - assert($logger instanceof Logger); - $handlers = $logger->getHandlers(); - $this->assertCount(1, $handlers); - $this->assertInstanceOf(FingersCrossedHandler::class, $handlers[0]); - } - - public function testCustomFormatter(): void - { - $fname = tempnam(sys_get_temp_dir(), 'phpactor_test'); - $container = $this->create([ - LoggingExtension::PARAM_FORMATTER => 'json', - LoggingExtension::PARAM_ENABLED => true, - LoggingExtension::PARAM_PATH => $fname, - LoggingExtension::PARAM_LEVEL => 'debug', - ]); - $logger = $container->get('logging.logger'); - assert($logger instanceof Logger); - $logger->info('asd'); - $result = json_decode(file_get_contents($fname)); - $this->assertNotNull($result, 'Decoded JSON'); - unlink($fname); - } - - private function create(array $options): Container - { - $container = PhpactorContainer::fromExtensions([ - LoggingExtension::class, - ExampleExtension::class, - ], $options); - - return $container; - } -} - -class ExampleExtension implements Extension -{ - public function load(ContainerBuilder $container): void - { - $container->register('json_formatter', function (Container $container) { - return new JsonFormatter(); - }, [ LoggingExtension::TAG_FORMATTER => ['alias'=> 'json2']]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/Navigation/Application/Navigator.php b/lib/Extension/Navigation/Application/Navigator.php deleted file mode 100644 index 9dd73c0aed..0000000000 --- a/lib/Extension/Navigation/Application/Navigator.php +++ /dev/null @@ -1,71 +0,0 @@ - $autoCreateConfig */ - public function __construct( - private NavigatorInterface $navigator, - private ClassNew $classNew, - private array $autoCreateConfig, - private string $absolutePath - ) { - } - - /** @return array */ - public function destinationsFor(string $path): array - { - return $this->navigator->destinationsFor($path); - } - - public function canCreateNew(string $path, string $destinationName): bool - { - $destination = $this->destination($path, $destinationName); - - if (file_exists($destination)) { - return false; - } - - return isset($this->autoCreateConfig[$destinationName]); - } - - public function createNew(string $path, string $destinationName): void - { - $destination = $this->destination($path, $destinationName); - $variant = $this->variant($destinationName); - $this->classNew->generate($destination, $variant); - } - - private function destination(string $path, string $destinationName): string - { - $destinations = $this->destinationsFor($path); - - if (false === isset($destinations[$destinationName])) { - throw new RuntimeException(sprintf( - 'Destination "%s" does not exist, known destinations: "%s"', - $destinationName, - implode('", "', array_keys($destinations)) - )); - } - - return Path::makeAbsolute($destinations[$destinationName], $this->absolutePath); - } - - private function variant(string $destinationName): string - { - if (!isset($this->autoCreateConfig[$destinationName])) { - throw new RuntimeException(sprintf( - 'Destination "%s" has no new class variant set', - $destinationName - )); - } - - return $this->autoCreateConfig[$destinationName]; - } -} diff --git a/lib/Extension/Navigation/Handler/NavigateHandler.php b/lib/Extension/Navigation/Handler/NavigateHandler.php deleted file mode 100644 index 749f406865..0000000000 --- a/lib/Extension/Navigation/Handler/NavigateHandler.php +++ /dev/null @@ -1,86 +0,0 @@ -setDefaults([ - self::PARAM_SOURCE_PATH => null, - self::PARAM_DESTINATION => null, - self::PARAM_CONFIRM_CREATE => null, - ]); - } - - public function handle(array $arguments) - { - if (null === $arguments[self::PARAM_SOURCE_PATH]) { - throw new RuntimeException(sprintf( - 'Param %s is required', - self::PARAM_SOURCE_PATH - )); - } - - if (false === $arguments[self::PARAM_CONFIRM_CREATE]) { - return EchoResponse::fromMessage('Cancelled'); - } - - $destinations = $this->navigator->destinationsFor($arguments[self::PARAM_SOURCE_PATH]); - $this->requireInput(ChoiceInput::fromNameLabelChoices( - self::PARAM_DESTINATION, - 'Destination:', - array_combine(array_keys($destinations), array_keys($destinations)) - )); - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - $path = $destinations[$arguments[self::PARAM_DESTINATION]]; - $canCreate = $this->navigator->canCreateNew($arguments[self::PARAM_SOURCE_PATH], $arguments[self::PARAM_DESTINATION]); - - if ($canCreate) { - $this->requireInput(ConfirmInput::fromNameAndLabel( - self::PARAM_CONFIRM_CREATE, - sprintf( - 'File "%s" does not exist, generate new?: ', - $path - ) - )); - } - - if ($this->hasMissingArguments($arguments)) { - return $this->createInputCallback($arguments); - } - - if ($canCreate) { - $this->navigator->createNew($arguments[self::PARAM_SOURCE_PATH], $arguments[self::PARAM_DESTINATION]); - } - - return OpenFileResponse::fromPath($path); - } -} diff --git a/lib/Extension/Navigation/NavigationExtension.php b/lib/Extension/Navigation/NavigationExtension.php deleted file mode 100644 index 49270a1b50..0000000000 --- a/lib/Extension/Navigation/NavigationExtension.php +++ /dev/null @@ -1,88 +0,0 @@ -registerPathFinder($container); - $this->registerNavigators($container); - $this->registerRpc($container); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PATH_FINDER_DESTINATIONS => [], - self::NAVIGATOR_AUTOCREATE => [], - ]); - } - - private function registerPathFinder(ContainerBuilder $container): void - { - $container->register( - self::SERVICE_PATH_FINDER, - fn (Container $container) => PathFinder::fromAbsoluteDestinations( - $container->parameter(FilePathResolverExtension::PARAM_PROJECT_ROOT)->string(), - $container->parameter(self::PATH_FINDER_DESTINATIONS)->value() // @phpstan-ignore argument.type - ) - ); - - $container->register('application.navigator', function (Container $container) { - return new Navigator( - $container->get('navigation.navigator.chain'), - $container->get('application.class_new'), - $container->parameter(self::NAVIGATOR_AUTOCREATE)->value(), // @phpstan-ignore argument.type - $container->parameter(FilePathResolverExtension::PARAM_PROJECT_ROOT)->string() - ); - }); - } - - private function registerRpc(ContainerBuilder $container): void - { - $container->register('rpc.handler.navigate', function (Container $container) { - return new NavigateHandler( - $container->get('application.navigator') - ); - }, [ 'rpc.handler' => ['name' => NavigateHandler::NAME] ]); - } - - private function registerNavigators(ContainerBuilder $container): void - { - $container->register('navigation.navigator.chain', function (Container $container) { - $navigators = []; - foreach ($container->getServiceIdsForTag('navigation.navigator') as $serviceId => $attrs) { - $navigators[] = $container->get($serviceId); - } - - return new ChainNavigator($navigators); - }); - $container->register('navigation.navigator.path_finder', function (Container $container) { - return new PathFinderNavigator($container->get(self::SERVICE_PATH_FINDER)); - }, [ 'navigation.navigator' => [] ]); - - $container->register('navigation.navigator.worse_reflection', function (Container $container) { - return new WorseReflectionNavigator($container->get(WorseReflectionExtension::SERVICE_REFLECTOR)); - }, [ 'navigation.navigator' => [] ]); - } -} diff --git a/lib/Extension/Navigation/Navigator/ChainNavigator.php b/lib/Extension/Navigation/Navigator/ChainNavigator.php deleted file mode 100644 index e083d8e8b4..0000000000 --- a/lib/Extension/Navigation/Navigator/ChainNavigator.php +++ /dev/null @@ -1,23 +0,0 @@ -navigators as $navigator) { - $destinations = array_merge($destinations, $navigator->destinationsFor($path)); - } - - return $destinations; - } -} diff --git a/lib/Extension/Navigation/Navigator/Navigator.php b/lib/Extension/Navigation/Navigator/Navigator.php deleted file mode 100644 index 5e6532c0ee..0000000000 --- a/lib/Extension/Navigation/Navigator/Navigator.php +++ /dev/null @@ -1,8 +0,0 @@ -pathFinder->destinationsFor($path); - } catch (NoMatchingSourceException) { - return []; - } - } -} diff --git a/lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php b/lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php deleted file mode 100644 index 77f69cc650..0000000000 --- a/lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php +++ /dev/null @@ -1,57 +0,0 @@ -build(); - $classes = $this->reflector->reflectClassesIn($source); - - foreach ($classes as $class) { - if ($class instanceof ReflectionClass) { - $destinations = $this->forReflectionClass($destinations, $class); - } - - if ($class instanceof ReflectionInterface) { - $destinations = $this->forReflectionInterface($destinations, $class); - } - } - - return $destinations; - } - - private function forReflectionClass(array $destinations, ReflectionClass $class) - { - $parentClass = $class->parent(); - if ($parentClass instanceof ReflectionClass) { - $destinations['parent'] = $parentClass->sourceCode()->uri()?->path(); - } - - foreach ($class->interfaces() as $interface) { - $destinations['interface:'.$interface->name()->short()] = $interface->sourceCode()->uri()?->path(); - } - - return $destinations; - } - - private function forReflectionInterface($destinations, ReflectionInterface $class) - { - foreach ($class->parents() as $interface) { - $destinations['interface:'.$interface->name()->short()] = $interface->sourceCode()->uri()?->path(); - } - - return $destinations; - } -} diff --git a/lib/Extension/Navigation/Tests/Application/NavigatorTest.php b/lib/Extension/Navigation/Tests/Application/NavigatorTest.php deleted file mode 100644 index 08097c2fd3..0000000000 --- a/lib/Extension/Navigation/Tests/Application/NavigatorTest.php +++ /dev/null @@ -1,59 +0,0 @@ -navigator(); - - $this->workspace->put('src/Kernel.php', 'destinationsFor($this->workspace->path('src/Kernel.php')); - - self::assertSame(['unit_test' => 'tests/Unit/KernelFoo.php'], $result); - } - - public function testCanCreate(): void - { - $navigator = $this->navigator(); - - $this->workspace->put('src/Kernel.php', 'canCreateNew($this->workspace->path('src/Kernel.php'), 'unit_test'); - - self::assertTrue($result); - } - - public function testNoNeedToCreate(): void - { - $navigator = $this->navigator(); - - $this->workspace->put('src/Kernel.php', 'workspace->put('tests/Unit/KernelFoo.php', 'canCreateNew($this->workspace->path('src/Kernel.php'), 'unit_test'); - - self::assertFalse($result); - } - - /** - * @param array $destinations - * @param array $autocreate - */ - private function navigator( - array $destinations = ['source' => 'src/.php', 'unit_test' => 'tests/Unit/Foo.php'], - array $autocreate = ['source' => 'source', 'unit_test' => 'unit_test'], - ): Navigator { - $container = $this->container([ - NavigationExtension::PATH_FINDER_DESTINATIONS => $destinations, - NavigationExtension::NAVIGATOR_AUTOCREATE => $autocreate - ]); - /** @var Navigator */ - return $container->get('application.navigator'); - } -} diff --git a/lib/Extension/Navigation/Tests/IntegrationTestCase.php b/lib/Extension/Navigation/Tests/IntegrationTestCase.php deleted file mode 100644 index 6342dc0275..0000000000 --- a/lib/Extension/Navigation/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,75 +0,0 @@ -workspace = $this->workspace(); - $this->workspace->reset(); - } - - /** - * @param array{ - * 'navigator.destinations': array, - * 'navigator.autocreate': array, - * } $config - */ - protected function container(array $config): Container - { - $key = serialize($config); - static $container = []; - - if (isset($container[$key])) { - return $container[$key]; - } - - $container[$key] = PhpactorContainer::fromExtensions([ - CodeTransformExtension::class, - CodeTransformExtraExtension::class, - PhpExtension::class, - CoreExtension::class, - NavigationExtension::class, - LoggingExtension::class, - SourceCodeFilesystemExtension::class, - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - FilePathResolverExtension::class, - WorseReflectionExtension::class, - ], array_merge([ - LoggingExtension::PARAM_ENABLED=> true, - LoggingExtension::PARAM_PATH=> 'php://stderr', - WorseReflectionExtension::PARAM_ENABLE_CACHE=> false, - ComposerAutoloaderExtension::PARAM_COMPOSER_ENABLE => false, - WorseReflectionExtension::PARAM_STUB_DIR => $this->workspace()->path(), - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ . '/../', - FilePathResolverExtension::PARAM_PROJECT_ROOT => $this->workspace()->path(), - ], $config)); - - return $container[$key]; - } - - protected function workspace(): Workspace - { - return Workspace::create(__DIR__ . '/Workspace'); - } -} diff --git a/lib/Extension/Navigation/Tests/Navigator/PathFinderNavigatorTest.php b/lib/Extension/Navigation/Tests/Navigator/PathFinderNavigatorTest.php deleted file mode 100644 index 0227fdb4a8..0000000000 --- a/lib/Extension/Navigation/Tests/Navigator/PathFinderNavigatorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -pathFinder = PathFinder::fromDestinations([ - 'source' => 'src/.php', - 'unit_test' => 'tests/Unit/Test.php' - ]); - } - - public function testSomething(): void - { - $navigator = new PathFinderNavigator($this->pathFinder); - $result = $navigator->destinationsFor('src/Kernel.php'); - - self::assertSame(['unit_test' => 'tests/Unit/KernelTest.php'], $result); - } - -} diff --git a/lib/Extension/ObjectRenderer/Extension/ObjectRendererTwigExtension.php b/lib/Extension/ObjectRenderer/Extension/ObjectRendererTwigExtension.php deleted file mode 100644 index c17bfd5573..0000000000 --- a/lib/Extension/ObjectRenderer/Extension/ObjectRendererTwigExtension.php +++ /dev/null @@ -1,10 +0,0 @@ - - */ - private array $templatePaths = []; - - private string $suffix = '.twig'; - - private bool $renderEmptyOnNotFound = false; - - private LoggerInterface $logger; - - /** - * @var bool|string|callable - */ - private mixed $escaping = false; - - private bool $enableAncestoralCandidates = false; - - private bool $enableInterfaceCandidates = false; - - /** - * @var ?Closure(Environment): Environment - */ - private ?Closure $twigConfigurator; - - private function __construct() - { - $this->logger = new NullLogger(); - } - - /** - * Create a new instance of the builder. - * Call build() to create a new ObejctRenderer. - */ - public static function create(): self - { - return new self(); - } - - /** - * When renderEmptyOnNotFound() is set, use this - * logger to log template not found messages. - */ - public function setLogger(LoggerInterface $logger): self - { - $new = clone $this; - $new->logger = $logger; - - return $new; - } - - /** - * Suffix of the twig files, `.twig` by default - */ - public function setTemplateSuffix(string $suffix): self - { - $new = clone $this; - $new->suffix = $suffix; - - return $new; - } - - /** - * Add a template path. Can be called multiple times. - */ - public function addTemplatePath(string $path): self - { - $new = clone $this; - $new->templatePaths[] = $path; - - return $new; - } - - /** - * Set the Twig escaping strategy: - * - * - false: disable auto-escaping - * - html, js: set the autoescaping to one of the supported strategies - * - name: set the autoescaping strategy based on the template name extension - * - PHP callback: a PHP callback that returns an escaping strategy based on the template "name" - * - * @param bool|string|callable $escaping - */ - public function setEscaping($escaping): self - { - $new = clone $this; - $new->escaping = $escaping; - - return $new; - } - - /** - * Instead of throwing an exception when a template is not found, return - * empty. If a logger is provided, via. setLogger, log the exception - * message. - */ - public function renderEmptyOnNotFound(): self - { - $new = clone $this; - $new->renderEmptyOnNotFound = true; - - return $new; - } - - /** - * Determine templates from the class of the current object and then the - * class of each of its ancestors. - */ - public function enableAncestoralCandidates(): self - { - $new = clone $this; - $new->enableAncestoralCandidates = true; - - return $new; - } - - /** - * Determine templates from the class of the current object and then the - * class of each of its ancestors. - */ - public function enableInterfaceCandidates(): self - { - $new = clone $this; - $new->enableInterfaceCandidates = true; - - return $new; - } - - /** - * Build the object renderer - */ - public function build(): ObjectRenderer - { - return $this->buildRenderer(); - } - - /** - * @param null|Closure(Environment): Environment $configurator - */ - public function configureTwig(?Closure $configurator): ObjectRendererBuilder - { - $this->twigConfigurator = $configurator; - - return $this; - } - - private function buildRenderer(): ObjectRenderer - { - $renderer = new TwigObjectRenderer( - $this->buildTwig(), - $this->buildTemplateProvider() - ); - - if ($this->renderEmptyOnNotFound) { - $renderer = new TolerantObjectRenderer($renderer, $this->logger); - } - - return $renderer; - } - - private function buildTwig(): Environment - { - $env = new Environment( - new FilesystemLoader($this->templatePaths), - [ - 'autoescape' => $this->escaping, - 'strict_variables' => true, - ] - ); - - if ($this->twigConfigurator) { - return $this->twigConfigurator->__invoke($env); - } - - return $env; - } - - private function buildTemplateProvider(): TemplateCandidateProvider - { - $provider = new ClassNameTemplateProvider(); - - if ($this->enableAncestoralCandidates) { - $provider = new AncestoralClassTemplateProvider($provider); - } - - if ($this->enableInterfaceCandidates) { - $provider = new InterfaceTemplateProvider($provider); - } - - $provider = new SuffixAppendingTemplateProvider($provider, $this->suffix); - - return $provider; - } -} diff --git a/lib/Extension/ObjectRenderer/ObjectRendererExtension.php b/lib/Extension/ObjectRenderer/ObjectRendererExtension.php deleted file mode 100644 index 1789423171..0000000000 --- a/lib/Extension/ObjectRenderer/ObjectRendererExtension.php +++ /dev/null @@ -1,78 +0,0 @@ -setDefaults([ - self::PARAM_TEMPLATE_PATHS => [ - '%project_config%/templates/markdown', - '%config%/templates/markdown', - ] - ]); - - $schema->setDescriptions([ - self::PARAM_TEMPLATE_PATHS => 'Paths in which to look for templates for hover information.' - ]); - } - - - public function load(ContainerBuilder $container): void - { - $container->register(self::SERVICE_MARKDOWN_RENDERER, function (Container $container) { - $resolver = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER); - - /** @var array $templatePaths */ - $templatePaths = $container->getParameter(self::PARAM_TEMPLATE_PATHS); - $templatePaths[] = __DIR__ . '/../../../templates/help/markdown'; - - $resolvedTemplatePaths = array_map(fn (string $path) => $resolver->resolve($path), $templatePaths); - - $phpVersion = $container->get(PhpVersionResolver::class)->resolve(); - $paths = (new PhpVersionPathResolver($phpVersion))->resolve($resolvedTemplatePaths); - - $builder = ObjectRendererBuilder::create() - ->setLogger(LoggingExtension::channelLogger($container, 'LSP-HOVER')) - ->enableInterfaceCandidates() - ->enableAncestoralCandidates() - ->configureTwig(function (Environment $env) use ($container) { - foreach ($container->getServiceIdsForTag(self::TAG_TWIG_EXTENSION) as $serviceId => $_) { - $service = $container->get($serviceId); - if (!$service instanceof ObjectRendererTwigExtension) { - throw new RuntimeException(sprintf( - 'Expected service to be a "%s"', - ObjectRendererTwigExtension::class - )); - } - $service->configure($env); - } - return $env; - }) - ->renderEmptyOnNotFound(); - - foreach ($paths as $path) { - $builder = $builder->addTemplatePath($path); - } - - return $builder->build(); - }); - } -} diff --git a/lib/Extension/OpenTelemetry/Model/ClassHook.php b/lib/Extension/OpenTelemetry/Model/ClassHook.php deleted file mode 100644 index ff7137137e..0000000000 --- a/lib/Extension/OpenTelemetry/Model/ClassHook.php +++ /dev/null @@ -1,21 +0,0 @@ -tracer(); - - foreach ($this->providers as $provider) { - foreach ($provider->hooks() as $hook) { - hook($hook->class, $hook->function, function ( - object $object, - array $params, - string $class, - string $function, - ?string $filename, - ?int $lineno, - ) use ($tracer, $hook): void { - $callContext = new PreContext( - $object, - $params, - $class, - $function, - $filename, - $lineno, - ); - $tracerContext = new TracerContext($tracer, Context::getCurrent(), Context::storage()); - $span = ($hook->pre)($tracerContext, $callContext); - Context::storage()->attach($span->storeInContext($tracerContext->currentContext())); - }, function (object $object, array $params, mixed $returnValue, ?Throwable $exception) use ($tracer, $hook) { - $tracerContext = new TracerContext($tracer, Context::getCurrent(), Context::storage()); - $postContext = new PostContext($object, $params, $returnValue, $exception); - if ($hook->post !== null) { - return ($hook->post)($tracerContext, $postContext); - } - $scope = Context::storage()->scope(); - if (null === $scope) { - throw new RuntimeException( - 'Expected scope from context storage, but got NULL' - ); - } - $scope->detach(); - $span = $scope->context()->get(ContextKeys::span()); - if (!$span instanceof SpanInterface) { - throw new RuntimeException(sprintf( - 'Expected Span from context , but got %s', - get_debug_type($span) - )); - } - $span->end(); - return $returnValue; - }); - } - } - - $this->initialized = true; - } -} diff --git a/lib/Extension/OpenTelemetry/Model/HookProvider.php b/lib/Extension/OpenTelemetry/Model/HookProvider.php deleted file mode 100644 index adacd104cc..0000000000 --- a/lib/Extension/OpenTelemetry/Model/HookProvider.php +++ /dev/null @@ -1,16 +0,0 @@ - - */ - public function hooks(): Generator; -} diff --git a/lib/Extension/OpenTelemetry/Model/PostContext.php b/lib/Extension/OpenTelemetry/Model/PostContext.php deleted file mode 100644 index f825dbb92d..0000000000 --- a/lib/Extension/OpenTelemetry/Model/PostContext.php +++ /dev/null @@ -1,20 +0,0 @@ - $params - */ - public function __construct( - public object $object, - public array $params, - public mixed $returnValue, - public ?Throwable $exception - ) { - } - -} diff --git a/lib/Extension/OpenTelemetry/Model/PreContext.php b/lib/Extension/OpenTelemetry/Model/PreContext.php deleted file mode 100644 index f3b906c504..0000000000 --- a/lib/Extension/OpenTelemetry/Model/PreContext.php +++ /dev/null @@ -1,41 +0,0 @@ - $params - */ - public function __construct( - public object $object, - public array $params, - public string $class, - public string $function, - public ?string $filename, - public ?int $lineno - ) { - } - - public function context(): ContextInterface - { - return Context::getCurrent(); - } - - public function param(int $offset): mixed - { - if (!isset($this->params[$offset])) { - throw new RuntimeException(sprintf( - 'No parameter at offset %d, there are %d parameters', - $offset, - count($this->params), - )); - } - - return $this->params[$offset]; - } -} diff --git a/lib/Extension/OpenTelemetry/Model/TracerContext.php b/lib/Extension/OpenTelemetry/Model/TracerContext.php deleted file mode 100644 index b20f4beded..0000000000 --- a/lib/Extension/OpenTelemetry/Model/TracerContext.php +++ /dev/null @@ -1,43 +0,0 @@ -tracer->spanBuilder($spanName) - ->setSpanKind(SpanKind::KIND_SERVER) - ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $callContext->class, $callContext->function)) - ->setAttribute(CodeAttributes::CODE_FILE_PATH, $callContext->filename) - ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $callContext->function); - } - - public function storage(): ContextStorageInterface - { - return $this->storage; - } - - public function currentContext(): ContextInterface - { - return $this->context; - } -} diff --git a/lib/Extension/OpenTelemetry/OpenTelemetryExtension.php b/lib/Extension/OpenTelemetry/OpenTelemetryExtension.php deleted file mode 100644 index 4339800404..0000000000 --- a/lib/Extension/OpenTelemetry/OpenTelemetryExtension.php +++ /dev/null @@ -1,44 +0,0 @@ -register(HookBootstrap::class, function (Container $container) { - $providers = []; - foreach ($container->getServiceIdsForTag(self::TAG_HOOK_PROVIDER) as $serviceId => $_) { - $providers[] = $container->expect($serviceId, HookProvider::class); - } - return new HookBootstrap($providers); - }); - - } - - public function configure(Resolver $schema): void - { - } - - public function boot(Container $container): void - { - $container->get(HookBootstrap::class)->bootstrap(); - } - - public function name(): string - { - return 'open_telemetry'; - } -} diff --git a/lib/Extension/OpenTelemetry/Tests/Unit/Model/HookBootstrapTest.php b/lib/Extension/OpenTelemetry/Tests/Unit/Model/HookBootstrapTest.php deleted file mode 100644 index ee9496616f..0000000000 --- a/lib/Extension/OpenTelemetry/Tests/Unit/Model/HookBootstrapTest.php +++ /dev/null @@ -1,51 +0,0 @@ -markTestSkipped('Requires opentelemetry extension'); - } - $bootstrap = new HookBootstrap([new TestProvider()]); - ($bootstrap)->bootstrap(); - self::assertTrue($bootstrap->initialized); - $class = new ExampleClass(); - $class->foo(); - } - - public function hookTest(): void - { - } -} - -class ExampleClass -{ - public function foo(): void - { - } -} - -class TestProvider implements HookProvider -{ - public function hooks(): Generator - { - yield new ClassHook( - ExampleClass::class, - 'foo', - function (TracerContext $tracer, PreContext $context) { - return $tracer->spanBuilder($context, 'test')->startSpan(); - }, - ); - } -} diff --git a/lib/Extension/PHPUnit/CodeTransform/GenerateTestMethods.php b/lib/Extension/PHPUnit/CodeTransform/GenerateTestMethods.php deleted file mode 100644 index b212edf06d..0000000000 --- a/lib/Extension/PHPUnit/CodeTransform/GenerateTestMethods.php +++ /dev/null @@ -1,77 +0,0 @@ - */ - public function getGeneratableTestMethods(SourceCode $source): Generator - { - $classes = $this->reflector->reflectClassesIn($source); - if (count($classes->classes()) !== 1) { - return; - } - - $class = $classes->classes()->first(); - if (!$class instanceof ReflectionClass) { - return; - } - - if (!$class->isInstanceOf(ClassName::fromString('\PHPUnit\Framework\TestCase'))) { - return; - } - - foreach (self::METHODS_TO_GENERATE as $methodName) { - if ($class->ownMembers()->methods()->byName($methodName)->count() === 0) { - yield $methodName; - } - } - - return; - } - - public function generateMethod(TextDocument $document, string $methodName): TextEdits - { - Assert::inArray( - $methodName, - self::METHODS_TO_GENERATE, - sprintf('%s can not generate "%s" with class', __CLASS__, $methodName), - ); - - $class = $this->reflector->reflectClassesIn($document)->classes()->first(); - - $builder = SourceCodeBuilder::create(); - $builder->namespace($class->name()->namespace()); - $classBuilder = $builder->class($class->name()->short()); - - if ($class->methods()->has($methodName)) { - return TextEdits::none(); - } - - $classBuilder ->method($methodName) ->visibility('public') ->returnType('void') ; - - return $this->updater->textEditsFor($builder->build(), $document); - } -} diff --git a/lib/Extension/PHPUnit/CodeTransform/TestGenerator.php b/lib/Extension/PHPUnit/CodeTransform/TestGenerator.php deleted file mode 100644 index 31b107e2e0..0000000000 --- a/lib/Extension/PHPUnit/CodeTransform/TestGenerator.php +++ /dev/null @@ -1,30 +0,0 @@ -namespace(); - $name = $targetName->short(); - $sourceCode = <<canWalk($node)) { - return $frame; - } - - $callExpression = $node->parent; - if (!$callExpression instanceof CallExpression) { - return $frame; - } - - $args = FunctionArguments::fromList( - $resolver->resolver(), - $frame, - $callExpression->argumentExpressionList - ); - - if (count($args) < 2) { - return $frame; - } - - $type = $args->at(0)->type(); - - if ($type instanceof StringLiteralType) { - $type = TypeFactory::reflectedClass($resolver->reflector(), $type->value()); - } - - if ($type instanceof ClassStringType) { - $type = TypeFactory::reflectedClass($resolver->reflector(), $type->className()?->__toString()); - } - - if (!$type instanceof ClassType) { - return $frame; - } - - $var = $args->at(1); - - $frame->locals()->set(Variable::fromSymbolContext($var->withType($type))); - - return $frame; - } - - private function canWalk(Node $node): bool - { - if ($node instanceof ScopedPropertyAccessExpression) { - $scopeResolutionQualifier = $node->scopeResolutionQualifier; - - if (!$scopeResolutionQualifier instanceof QualifiedName) { - return false; - } - - $resolvedName = $scopeResolutionQualifier->getResolvedName(); - if ((string) $resolvedName !== 'PHPUnit\Framework\Assert') { - return false; - } - - return true; - } - - if ($node instanceof MemberAccessExpression) { - $memberName = $node->memberName; - - if (!$memberName instanceof Token) { - return false; - } - - // we havn't got the facility to check if we are extending the TestCase - // here, so just assume that any method named this way is belonging to - // PHPUnit - if ('assertInstanceOf' === $memberName->getText($node->getFileContents())) { - return true; - } - } - - return false; - } -} diff --git a/lib/Extension/PHPUnit/LspCommand/GenerateTestMethodCommand.php b/lib/Extension/PHPUnit/LspCommand/GenerateTestMethodCommand.php deleted file mode 100644 index bab63d110c..0000000000 --- a/lib/Extension/PHPUnit/LspCommand/GenerateTestMethodCommand.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ - public function __invoke(string $uri, string $method): Promise - { - $textDocument = $this->workspace->get($uri); - $source = SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri); - - $textEdits = $this->generateTestMethods->generateMethod($source, $method); - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => TextEditConverter::toLspTextEdits($textEdits, $textDocument->text) - ]), 'Generate decoration'); - } -} diff --git a/lib/Extension/PHPUnit/PHPUnitExtension.php b/lib/Extension/PHPUnit/PHPUnitExtension.php deleted file mode 100644 index 52d852c2e3..0000000000 --- a/lib/Extension/PHPUnit/PHPUnitExtension.php +++ /dev/null @@ -1,92 +0,0 @@ -registerCommands($container); - $this->registerServices($container); - $this->registerWorseReflection($container); - $this->registerCodeTransform($container); - } - - - public function configure(Resolver $schema): void - { - } - - public function name(): string - { - return 'phpunit'; - } - - public function registerCommands(ContainerBuilder $container): void - { - $container->register( - GenerateTestMethodCommand::class, - function (Container $container) { - return new GenerateTestMethodCommand( - $container->get(ClientApi::class), - $container->expect(LanguageServerExtension::SERVICE_SESSION_WORKSPACE, Workspace::class), - $container->get(GenerateTestMethods::class) - ); - }, - [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => GenerateTestMethodCommand::NAME - ], - ] - ); - } - - private function registerServices(ContainerBuilder $container): void - { - $container->register(GenerateTestMethodProvider::class, function (Container $container) { - return new GenerateTestMethodProvider( - $container->get(GenerateTestMethods::class), - ); - }, [ - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(GenerateTestMethods::class, function (Container $container) { - return new GenerateTestMethods( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(Updater::class), - ); - }); - } - - private function registerWorseReflection(ContainerBuilder $container): void - { - $container->register('phpunit.frame_walker.assert_instance_of', function (Container $container) { - return new AssertInstanceOfWalker(); - }, [ WorseReflectionExtension::TAG_FRAME_WALKER => [] ]); - } - - private function registerCodeTransform(ContainerBuilder $container): void - { - $container->register('phpunit.code_transform.test_generator', function (Container $container) { - return new TestGenerator(); - }, [CodeTransformExtension::TAG_NEW_CLASS_GENERATOR => ['name' => 'phpunit']]); - } -} diff --git a/lib/Extension/PHPUnit/Provider/GenerateTestMethodProvider.php b/lib/Extension/PHPUnit/Provider/GenerateTestMethodProvider.php deleted file mode 100644 index a0982c8f80..0000000000 --- a/lib/Extension/PHPUnit/Provider/GenerateTestMethodProvider.php +++ /dev/null @@ -1,62 +0,0 @@ -generateTestMethods->getGeneratableTestMethods( - SourceCode::fromStringAndPath($textDocument->text, $textDocument->uri) - ); - - $availableCodeActions = []; - foreach ($methodsThatCanBeGenerated as $methodName) { - $availableCodeActions[] = new CodeAction( - title: 'Generate method ' . $methodName, - kind: $this->kinds()[0], - diagnostics: [], - isPreferred: false, - command: new Command( - title: 'Test Methods', - command: GenerateTestMethodCommand::NAME, - arguments: [ - $textDocument->uri, - $methodName, - ] - ) - ); - } - - return new Success($availableCodeActions); - } - - public function describe(): string - { - return 'Generate setUp and or tearDown in PhpUnit test cases'; - } - - public function kinds(): array - { - return [ - CodeActionKind::REFACTOR - ]; - } -} diff --git a/lib/Extension/PHPUnit/Tests/Unit/CodeTransform/GenerateTestMethodsTest.php b/lib/Extension/PHPUnit/Tests/Unit/CodeTransform/GenerateTestMethodsTest.php deleted file mode 100644 index 41cfe871a4..0000000000 --- a/lib/Extension/PHPUnit/Tests/Unit/CodeTransform/GenerateTestMethodsTest.php +++ /dev/null @@ -1,122 +0,0 @@ -workspace()->put( - 'TestCase.php', - << $expected - */ - #[DataProvider('dataCanMethodBeGenerated')] - public function testCanMethodBeGenerated(string $source, array $expected): void - { - $sourceCode = SourceCode::fromStringAndPath('createTestMethodGenerator($source)->getGeneratableTestMethods($sourceCode); - - self::assertEquals($expected, iterator_to_array($methodNames)); - } - - /** - * @return Generator}> - */ - public static function dataCanMethodBeGenerated(): Generator - { - yield 'no classes' => ['echo "Hello"', []]; - - yield 'not a phpunit class' => [ - << [ - << [ - <<sourceExpectedAndOffset(__DIR__ . '/fixtures/' . $test); - $sourceCode = SourceCode::fromStringAndPath($source, 'file:///source'); - - $textDocumentEdits = $this->createTestMethodGenerator($source)->generateMethod($sourceCode, 'setUp'); - - $transformed = SourceCode::fromStringAndPath( - (string) $textDocumentEdits->apply($sourceCode), - 'file:///source' - ); - - $this->assertEquals(trim($expected), trim($transformed)); - } - - /** - * @return Generator - */ - public static function provideGenerateTestMethods(): Generator - { - yield 'generating a method that already exists' => [ 'generateTestMethods_existing.test']; - yield 'generating a new setUp method' => [ 'generateTestMethods_generate.test']; - } - - public function testGeneratingOtherMethods(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'Phpactor\Extension\PHPUnit\CodeTransform\GenerateTestMethods can not generate "someRandomMethod" with class' - ); - - $sourceCode = SourceCode::fromStringAndPath('', 'file:///source'); - $this->createTestMethodGenerator('')->generateMethod($sourceCode, 'someRandomMethod'); - } - - private function createTestMethodGenerator(string $source): GenerateTestMethods - { - return new GenerateTestMethods($this->reflectorForWorkspace($source), $this->updater()); - } -} diff --git a/lib/Extension/PHPUnit/Tests/Unit/CodeTransform/fixtures/generateTestMethods_existing.test b/lib/Extension/PHPUnit/Tests/Unit/CodeTransform/fixtures/generateTestMethods_existing.test deleted file mode 100644 index 0e6a17f4dc..0000000000 --- a/lib/Extension/PHPUnit/Tests/Unit/CodeTransform/fixtures/generateTestMethods_existing.test +++ /dev/null @@ -1,19 +0,0 @@ -// File: source - -} - -// File: expected -resolve(<<<'EOT' - resolve(<<<'EOT' - resolve(<<<'EOT' - resolve(<<<'EOT' - bar()); - } - EOT); - } - - public function testInstanceCall(): void - { - $this->resolve( - <<<'EOT' - addFrameWalker(new TestAssertWalker($this)) - ->addFrameWalker(new AssertInstanceOfWalker()) - ->addSource($sourceCode) - ->build(); - - $reflector->reflectOffset($sourceCode, mb_strlen($sourceCode)); - } -} diff --git a/lib/Extension/PHPUnit/Tests/Unit/PHPUnitExtensionTest.php b/lib/Extension/PHPUnit/Tests/Unit/PHPUnitExtensionTest.php deleted file mode 100644 index 0fabf3492a..0000000000 --- a/lib/Extension/PHPUnit/Tests/Unit/PHPUnitExtensionTest.php +++ /dev/null @@ -1,33 +0,0 @@ - __DIR__ - ]); - - $reflector = $container->get(WorseReflectionExtension::SERVICE_REFLECTOR); - $this->assertInstanceOf(Reflector::class, $reflector); - } -} diff --git a/lib/Extension/Php/Model/ChainResolver.php b/lib/Extension/Php/Model/ChainResolver.php deleted file mode 100644 index 58284133ba..0000000000 --- a/lib/Extension/Php/Model/ChainResolver.php +++ /dev/null @@ -1,53 +0,0 @@ -versionResolvers = $versionResolvers; - } - - - public function resolve(): ?string - { - foreach ($this->versionResolvers as $versionResolver) { - if (!$version = $versionResolver->resolve()) { - continue; - } - - return $version; - } - - throw new RuntimeException(sprintf( - '%s resolvers could not resolve PHP version', - count($this->versionResolvers) - )); - } - - public function source(): string - { - foreach ($this->versionResolvers as $versionResolver) { - if (!$version = $versionResolver->resolve()) { - continue; - } - - return $versionResolver->name(); - } - - return 'unknown'; - } - - public function name(): string - { - return 'chain'; - } -} diff --git a/lib/Extension/Php/Model/ComposerPhpVersionResolver.php b/lib/Extension/Php/Model/ComposerPhpVersionResolver.php deleted file mode 100644 index 0d794eb76c..0000000000 --- a/lib/Extension/Php/Model/ComposerPhpVersionResolver.php +++ /dev/null @@ -1,58 +0,0 @@ -composerJsonPath)) { - return null; - } - - if (!$contents = file_get_contents($this->composerJsonPath)) { - return null; - } - - $json = json_decode($contents, true); - if (!$json || !is_array($json)) { - return null; - } - - if (isset($json['config']['platform']['php'])) { - return $json['config']['platform']['php']; - } - - if (isset($json['require']['php'])) { - return $this->resolveLowestVersion($json['require']['php']); - } - - return null; - } - - public function name(): string - { - return 'composer'; - } - - private function resolveLowestVersion(string $versionString): ?string - { - /** @phpstan-ignore-next-line */ - $versions = array_map(function (string $versionString) { - return preg_replace('/[^0-9.]/', '', trim($versionString)); - }, (array)preg_split('{\|\|?}', $versionString)); - - sort($versions); - - if (false === $version = reset($versions)) { - return $versionString; - } - - return $version; - } -} diff --git a/lib/Extension/Php/Model/ConstantPhpVersionResolver.php b/lib/Extension/Php/Model/ConstantPhpVersionResolver.php deleted file mode 100644 index 699daa18af..0000000000 --- a/lib/Extension/Php/Model/ConstantPhpVersionResolver.php +++ /dev/null @@ -1,21 +0,0 @@ -version; - } - - public function name(): string - { - return 'user configured'; - } -} diff --git a/lib/Extension/Php/Model/PhpVersionResolver.php b/lib/Extension/Php/Model/PhpVersionResolver.php deleted file mode 100644 index dc2eac7384..0000000000 --- a/lib/Extension/Php/Model/PhpVersionResolver.php +++ /dev/null @@ -1,9 +0,0 @@ -register(ChainResolver::class, function (Container $container) { - $pathResolver = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER); - $composerPath = $pathResolver->resolve('%project_root%/composer.json'); - - return new ChainResolver( - new ConstantPhpVersionResolver($container->getParameter(self::PARAM_VERSION)), - new ComposerPhpVersionResolver($composerPath), - new RuntimePhpVersionResolver() - ); - }); - $container->register(PhpVersionResolver::class, function (Container $container) { - return $container->get(ChainResolver::class); - }); - - $container->register(PhpStatusProvider::class, function (Container $container) { - return new PhpStatusProvider($container->get(ChainResolver::class)); - }, []); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_VERSION => null - ]); - $schema->setDescriptions([ - self::PARAM_VERSION => <<<'EOT' - Consider this value to be the project\'s version of PHP (e.g. `7.4`). If omitted - it will check `composer.json` (by the configured platform then the PHP requirement) before - falling back to the PHP version of the current process. - EOT - ]); - } -} diff --git a/lib/Extension/Php/Status/PhpStatusProvider.php b/lib/Extension/Php/Status/PhpStatusProvider.php deleted file mode 100644 index f51d7bf892..0000000000 --- a/lib/Extension/Php/Status/PhpStatusProvider.php +++ /dev/null @@ -1,28 +0,0 @@ - (string)$this->chainResolver->resolve(), - 'source' => $this->chainResolver->source(), - 'runtime' => phpversion(), - ]; - } -} diff --git a/lib/Extension/Php/Tests/IntegrationTestCase.php b/lib/Extension/Php/Tests/IntegrationTestCase.php deleted file mode 100644 index cf985fe182..0000000000 --- a/lib/Extension/Php/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,14 +0,0 @@ -get(PhpVersionResolver::class)->resolve(); - self::assertNotNull($version); - } -} diff --git a/lib/Extension/Php/Tests/Unit/Model/ChainResolverTest.php b/lib/Extension/Php/Tests/Unit/Model/ChainResolverTest.php deleted file mode 100644 index 4c5aedd45a..0000000000 --- a/lib/Extension/Php/Tests/Unit/Model/ChainResolverTest.php +++ /dev/null @@ -1,26 +0,0 @@ -expectException(RuntimeException::class); - (new ChainResolver())->resolve(); - } - - public function testResolvesVersion(): void - { - $resolver = $this->prophesize(PhpVersionResolver::class); - $resolver->resolve()->willReturn('7.1'); - self::assertEquals('7.1', (new ChainResolver($resolver->reveal()))->resolve()); - } -} diff --git a/lib/Extension/Php/Tests/Unit/Model/ComposerPhpVersionResolverTest.php b/lib/Extension/Php/Tests/Unit/Model/ComposerPhpVersionResolverTest.php deleted file mode 100644 index 6318941f0e..0000000000 --- a/lib/Extension/Php/Tests/Unit/Model/ComposerPhpVersionResolverTest.php +++ /dev/null @@ -1,62 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest( - <<workspace()->path('/composer.json')); - self::assertEquals($expected, $resolver->resolve()); - } - - public static function provideRequireVersion(): Generator - { - yield [ '^7.0', '7.0' ]; - yield [ '7.0', '7.0' ]; - yield [ '^7.0 || ^8.0', '7.0' ]; - yield [ '^8.0 || ^7.0', '7.0' ]; - yield [ '^8.0 || 5.3 || ^7.0', '5.3' ]; - yield [ '~8.0', '8.0' ]; - yield [ '8.0 | 7.1 | 1 | 2', '1' ]; - } - - public function testReturnsPlatformWithHigherPrio(): void - { - $this->workspace()->reset(); - $this->workspace()->loadManifest( - <<<'EOT' - // File: composer.json - { - "require": { - "php": "^7.1" - }, - "config": { - "platform": { - "php": "7.3" - } - } - } - EOT - ); - $resolver = new ComposerPhpVersionResolver($this->workspace()->path('/composer.json')); - self::assertEquals('7.3', $resolver->resolve()); - } -} diff --git a/lib/Extension/PhpCodeSniffer/Formatter/PhpCodeSnifferFormatter.php b/lib/Extension/PhpCodeSniffer/Formatter/PhpCodeSnifferFormatter.php deleted file mode 100644 index 519e4a88c4..0000000000 --- a/lib/Extension/PhpCodeSniffer/Formatter/PhpCodeSnifferFormatter.php +++ /dev/null @@ -1,28 +0,0 @@ -phpCodeSniffer->produceFixesDiff($textDocument); - - $diffToTextEdits = new DiffToTextEditsConverter(); - return $diffToTextEdits->toTextEdits($diff); - }); - } -} diff --git a/lib/Extension/PhpCodeSniffer/LspCommand/FormatCommand.php b/lib/Extension/PhpCodeSniffer/LspCommand/FormatCommand.php deleted file mode 100644 index 7ec473d96c..0000000000 --- a/lib/Extension/PhpCodeSniffer/LspCommand/FormatCommand.php +++ /dev/null @@ -1,48 +0,0 @@ - - */ - public function __invoke(string $uri): Promise - { - return call(function () use ($uri) { - $path = TextDocumentUri::fromString($uri)->path(); - $textDocument = $this->workspace->get($uri); - - $diff = yield $this->phpCodeSniffer->produceFixesDiff($textDocument); - - $diffToTextEdits = new DiffToTextEditsConverter(); - $textEdits = $diffToTextEdits->toTextEdits($diff); - - $this->logger->debug(sprintf('PHP Code Sniffer produced %s text edits', count($textEdits))); - - return $this->clientApi->workspace()->applyEdit(new WorkspaceEdit([ - $uri => $textEdits - ]), 'Fix with PHP Code_Sniffer'); - }); - } -} diff --git a/lib/Extension/PhpCodeSniffer/Model/PhpCodeSnifferProcess.php b/lib/Extension/PhpCodeSniffer/Model/PhpCodeSnifferProcess.php deleted file mode 100644 index c3e2c7088c..0000000000 --- a/lib/Extension/PhpCodeSniffer/Model/PhpCodeSnifferProcess.php +++ /dev/null @@ -1,187 +0,0 @@ - $env - * @param list $additionalArgs - */ - public function __construct( - private string $binPath, - private LoggerInterface $logger, - private array $env = [], - private array $additionalArgs = [], - private ?string $cwd = null - ) { - } - - /** - * @return Promise - */ - public function run(string ...$args): Promise - { - $args = array_merge($args, $this->additionalArgs); - return call(function () use ($args) { - $process = ProcessBuilder::create([ - PHP_BINARY, - '-d', - 'display_errors=stderr', - '-d', - 'error_reporting=24575', - $this->binPath, - ...$args - ])->mergeParentEnv()->env($this->env); - if ($this->cwd !== null) { - $process->cwd($this->cwd); - } - $process = $process->build(); - yield $process->start(); - - $process->join() - ->onResolve(function (?Throwable $error, $data) use ($process): void { - $this->logger->log( - $error ? 'warning' : 'debug', - sprintf( - 'Executed %s, which exited with %s', - $process->getCommand(), - $data - ) - ); - }); - - return $process; - }); - } - - /** - * Producing diffs for phpcs fixes requires a temporary - * file. Otherwise any changes in current buffer which are not saved - * are included in resulted diff and interpreted as diagnostics with - * misleading ranges. - * - * It is because phpcs simply calls system's `diff` with the file - * passed by `--stdin-path` option. - * - * @param string[] $sniffs Phpcs sniffs to include. - * - * @return Promise - */ - public function produceFixesDiff(TextDocumentItem $textDocument, array $sniffs = []): Promise - { - return call(function () use ($textDocument, $sniffs) { - $tmpFilePath = $this->createTempFile($textDocument->text); - if (null === $tmpFilePath) { - $this->logger->error( - 'Failed to create temporary file for phpcs diagnostics. Without this results would be unreliable.' - ); - return '[]'; - } - $diagnostics = yield $this->runDiagnosticts( - $tmpFilePath, - $textDocument->text, - [ - '--report=diff', - '--no-cache', - empty($sniffs) ? '' : sprintf('--sniffs=%s', implode(',', $sniffs)) - ] - ); - unlink($tmpFilePath); - return $diagnostics; - }); - } - - /** - * @param string[] $options - * - * @return Promise - */ - public function diagnose(TextDocumentItem $textDocument, array $options = []): Promise - { - return $this->runDiagnosticts( - TextDocumentUri::fromString($textDocument->uri)->path(), - $textDocument->text, - [ '--report=json', ...$options ] - ); - } - - /** - * @param string[] $options - * - * @return Promise - */ - private function runDiagnosticts(string $url, string $text, array $options = []): Promise - { - return call(function () use ($url, $text, $options) { - /** @var Process */ - $process = yield $this->run( - ...[ - ...$options, - '-q', - '--no-colors', - sprintf('--stdin-path=%s', $url), - '-' - ] - ); - - $stdin = $process->getStdin(); - $stdin->write($text); - $stdin->end(); - - $stdout = yield buffer($process->getStdout()); - $exitCode = yield $process->join(); - - if ($exitCode !== 0 - && $exitCode !== self::EXIT_FOUND_NON_FIXABLE_ERRORS - && $exitCode !== self::EXIT_FILES_NEEDS_FIXING - ) { - throw new RuntimeException( - sprintf( - "phpcs exited with code '%s'; cmd: %s; stderr: '%s'; stdout: '%s'", - $exitCode, - $process->getCommand(), - yield buffer($process->getStderr()), - $stdout - ) - ); - } - - return $stdout; - }); - } - - /** - * Filename MUST include PHP extension, otherwise phpcs will not - * process it. - */ - private function createTempFile(string $text): ?string - { - $tmpName = tempnam(sys_get_temp_dir(), 'phpcsls'); - $name = sprintf('%s.php', $tmpName); - if (false === rename($tmpName, $name)) { - throw new RuntimeException('Could not rename file'); - } - $written = file_put_contents($name, $text); - - if (false === $written) { - return null; - } - - return $name; - } -} diff --git a/lib/Extension/PhpCodeSniffer/PhpCodeSnifferExtension.php b/lib/Extension/PhpCodeSniffer/PhpCodeSnifferExtension.php deleted file mode 100644 index 556772359d..0000000000 --- a/lib/Extension/PhpCodeSniffer/PhpCodeSnifferExtension.php +++ /dev/null @@ -1,114 +0,0 @@ -register( - PhpCodeSnifferProcess::class, - function (Container $container) { - $resolver = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - $path = $resolver->resolve($container->parameter(self::PARAM_PHP_CODE_SNIFFER_BIN)->string()); - $cwd = $container->parameter(self::PARAM_CWD)->value(); - if (is_string($cwd)) { - $cwd = $resolver->resolve($cwd); - } - - return new PhpCodeSnifferProcess( - $path, - LoggingExtension::channelLogger($container, 'phpcs'), - /** @phpstan-ignore-next-line */ - $container->parameter(self::PARAM_ENV)->value(), - /** @phpstan-ignore-next-line */ - $container->parameter(self::PARAM_ARGS)->value(), - /** @phpstan-ignore-next-line */ - $cwd, - ); - } - ); - - $container->register(PhpCodeSnifferFormatter::class, function (Container $container) { - return new PhpCodeSnifferFormatter( - $container->get(PhpCodeSnifferProcess::class) - ); - }, [ - LanguageServerExtension::TAG_FORMATTER => [] - ]); - - $container->register(PhpCodeSnifferDiagnosticsProvider::class, function (Container $container) { - return new PhpCodeSnifferDiagnosticsProvider( - $container->get(PhpCodeSnifferProcess::class), - $container->parameter(self::PARAM_SHOW_DIAGNOSTICS)->bool(), - new RangesForDiff(), - LoggingExtension::channelLogger($container, 'phpcs'), - ); - }, [ - LanguageServerExtension::TAG_DIAGNOSTICS_PROVIDER => DiagnosticProviderTag::create('phpcs'), - LanguageServerExtension::TAG_CODE_ACTION_PROVIDER => [] - ]); - - $container->register(FormatCommand::class, function (Container $container) { - return new FormatCommand( - $container->get(PhpCodeSnifferProcess::class), - $container->get(ClientApi::class), - $container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE), - LoggingExtension::channelLogger($container, 'phpcs') - ); - }, [ - LanguageServerExtension::TAG_COMMAND => [ - 'name' => 'php_code_sniffer.fix' - ], - ]); - } - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::PARAM_PHP_CODE_SNIFFER_BIN => '%project_root%/vendor/bin/phpcs', - self::PARAM_ENV => [ - 'XDEBUG_MODE' => 'off', - ], - self::PARAM_SHOW_DIAGNOSTICS => true, - self::PARAM_ARGS => [], - self::PARAM_CWD => null, - ]); - - $schema->setDescriptions([ - self::PARAM_PHP_CODE_SNIFFER_BIN => 'Path to the phpcs executable', - self::PARAM_ENV => 'Environment for PHP_CodeSniffer (e.g. to set XDEBUG_MODE)', - self::PARAM_SHOW_DIAGNOSTICS => 'Whether PHP_CodeSniffer diagnostics are shown', - self::PARAM_ARGS => 'Additional arguments to pass to the PHPCS process', - self::PARAM_CWD => 'Working directory for PHPCS', - ]); - } - - public function name(): string - { - return 'php_code_sniffer'; - } -} diff --git a/lib/Extension/PhpCodeSniffer/PhpCodeSnifferSuggestExtension.php b/lib/Extension/PhpCodeSniffer/PhpCodeSnifferSuggestExtension.php deleted file mode 100644 index 4d7b3d1357..0000000000 --- a/lib/Extension/PhpCodeSniffer/PhpCodeSnifferSuggestExtension.php +++ /dev/null @@ -1,50 +0,0 @@ -register('php_code_sniffer.suggest', function (Container $container) { - return new PhpactorComposerSuggestor( - $container->expect(ConfigurationExtension::SERVICE_PHPACTOR_CONFIG_LOCAL, JsonConfig::class), - $container->get(ComposerInspector::class), - function (JsonConfig $config, ComposerInspector $inspector) { - if ($config->has(PhpCodeSnifferExtension::PARAM_ENABLED)) { - return Changes::none(); - } - - if (!$inspector->package('squizlabs/php_codesniffer')) { - return Changes::none(); - } - - return Changes::from([ - new PhpactorConfigChange('PHP_CodeSniffer detected, enable the PHP_CodeSniffer extension?', function (bool $enable) { - return [ - PhpCodeSnifferExtension::PARAM_ENABLED => $enable, - ]; - }) - ]); - } - ); - }, [ - ConfigurationExtension::TAG_SUGGESTOR => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/PhpCodeSniffer/Provider/PhpCodeSnifferDiagnosticsProvider.php b/lib/Extension/PhpCodeSniffer/Provider/PhpCodeSnifferDiagnosticsProvider.php deleted file mode 100644 index a00f9bccdc..0000000000 --- a/lib/Extension/PhpCodeSniffer/Provider/PhpCodeSnifferDiagnosticsProvider.php +++ /dev/null @@ -1,243 +0,0 @@ -, - * totals: array{ - * errors: int<0, max>, - * warnings: int<0, max>, - * fixable: int<0, max> - * } - * } - * - * @phpstan-type PhpcsFileResult array{ - * errors: int<0, max>, - * warnings: int<0, max>, - * messages: PhpcsRule[] - * } - * - * @phpstan-type PhpcsRule array{ - * message: string, - * source: string, - * severity: int, - * fixable: bool, - * type: string, - * line: int, - * column: int - * } - */ -class PhpCodeSnifferDiagnosticsProvider implements DiagnosticsProvider, CodeActionProvider -{ - - public function __construct( - private PhpCodeSnifferProcess $phpCodeSniffer, - private bool $showDiagnostics, - private RangesForDiff $rangeForDiff, - private LoggerInterface $logger, - ) { - } - - /** - * @return Promise - */ - public function provideDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - if (!$this->showDiagnostics) { - return new Success([]); - } - - return call(function () use ($textDocument, $cancel) { - return yield $this->findDiagnostics($textDocument, $cancel); - }); - } - - public function provideActionsFor(TextDocumentItem $textDocument, Range $range, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument, $cancel) { - $isFixable = yield $this->hasFixableDiagnostics($textDocument); - if ($isFixable === false) { - return []; - } - - $diagnostics = yield $this->findDiagnostics($textDocument, $cancel); - - if ($diagnostics === []) { - return []; - } - - $title = 'Format with PHP Code Sniffer'; - - return [ - CodeAction::fromArray([ - 'title' => $title, - 'kind' => 'source.fixAll.phpactor.phpCodeSniffer', - 'diagnostics' => $diagnostics, - 'command' => new Command( - $title, - 'php_code_sniffer.fix', - [ - $textDocument->uri - ] - ) - ]) - ]; - }); - } - - public function kinds(): array - { - return ['source.fixAll.phpactor.phpCodeSniffer']; - } - - public function name(): string - { - return 'phpcs'; - } - - public function describe(): string - { - return 'phpcs'; - } - - /** - * @return Promise - */ - private function hasFixableDiagnostics(TextDocumentItem $textDocument): Promise - { - return call(function () use ($textDocument) { - /** @var string $outputJson */ - $outputJson = yield $this->phpCodeSniffer->diagnose($textDocument, [ '-m' ]); - - return $this->parseOutput($outputJson)['totals']['fixable'] > 0; - }); - } - - /** - * @return Promise - */ - private function findDiagnostics(TextDocumentItem $textDocument, CancellationToken $cancel): Promise - { - return call(function () use ($textDocument) { - /** @var string $outputJson */ - $outputJson = yield $this->phpCodeSniffer->diagnose($textDocument); - - $files = $this->parseOutput($outputJson)['files']; - if (empty($files)) { - return []; - } - - // phpcs return array indexed by file name, - // but we only deal with one file, thus don't care about - // actual key - $rules = current($files)['messages']; - - $diagnostics = []; - - $diffParser = new Parser(); - - foreach ($rules as $rule) { - // We treat non-fixable rules as 1 char range. - if ($rule['fixable'] === false) { - $lineNo = $rule['line'] - 1; - $range = new Range( - new Position($lineNo, $rule['column']), - new Position($lineNo, $rule['column'] + 1) - ); - $diagnostics[] = $this->createRuleDiagnostics($rule, $range); - continue; - } - - $sniffWithoutSuffix = $this->getSniffGroup($rule['source']); - if ($sniffWithoutSuffix === null) { - continue; - } - - $fileDiffText = yield $this->phpCodeSniffer->produceFixesDiff($textDocument, [$sniffWithoutSuffix]); - $fileDiff = $diffParser->parse($fileDiffText); - - // one file input is passed and one file expected - if (count($fileDiff) !== 1) { - $this->logger->warning( - sprintf("Expected phpcs to provide 1 diff, got %s. Skipping diagnostics for file '%s'", count($fileDiff), $textDocument->uri) - ); - - continue; - } - - $ranges = $this->rangeForDiff->createRangesForDiff($fileDiff[0]); - - foreach ($ranges as $range) { - $diagnostics[] = $this->createRuleDiagnostics($rule, $range); - } - } - - return $diagnostics; - }); - } - - /** - * @param PhpcsRule $rule - */ - private function createRuleDiagnostics(array $rule, Range $range): Diagnostic - { - return new Diagnostic( - message: $rule['message'], - range: $range, - severity: DiagnosticSeverity::WARNING, - source: $this->name(), - code: $rule['source'] - ); - } - - /** - * When trying to apply a fix, we need to know the name of the sniff - * group, not the exact sniff name. - * - * @return string|null Sniff with stripped last identifier. - */ - private function getSniffGroup(string $source): ?string - { - $matches = []; - preg_match("/(.*)\.\w+/", $source, $matches); - if (! isset($matches[1])) { - return null; - } - $sniffWithoutSuffix = $matches[1]; - return $sniffWithoutSuffix; - } - - /** @return PhpcsResult */ - private function parseOutput(string $rawOutput): array - { - try { - /** @var PhpcsResult $output */ - $output = json_decode($rawOutput, associative: true, flags: JSON_THROW_ON_ERROR); - } catch (JsonException $error) { - throw new RuntimeException(sprintf('Could not decode JSON: %s', $rawOutput)); - } - - return $output; - } -} diff --git a/lib/Extension/PhpCodeSniffer/Tests/Model/PhpCodeSnifferProcessTest.php b/lib/Extension/PhpCodeSniffer/Tests/Model/PhpCodeSnifferProcessTest.php deleted file mode 100644 index 93290c96ea..0000000000 --- a/lib/Extension/PhpCodeSniffer/Tests/Model/PhpCodeSnifferProcessTest.php +++ /dev/null @@ -1,26 +0,0 @@ -getPhpCodeSniffer(); - - $process = call(function () use ($phpCodeSniffer) { - $process = yield $phpCodeSniffer->run('--version'); - $stdout = yield buffer($process->getStdout()); - - self::assertStringContainsString('PHP_CodeSniffer ', $stdout, sprintf("Expected phpcs --version to return it's name followed with version, got: %s", $stdout)); - }); - - wait($process); - } - -} diff --git a/lib/Extension/PhpCodeSniffer/Tests/PhpCodeSnifferTestCase.php b/lib/Extension/PhpCodeSniffer/Tests/PhpCodeSnifferTestCase.php deleted file mode 100644 index 646902983b..0000000000 --- a/lib/Extension/PhpCodeSniffer/Tests/PhpCodeSnifferTestCase.php +++ /dev/null @@ -1,21 +0,0 @@ - 'off' - ], - ); - } -} diff --git a/lib/Extension/PhpCodeSniffer/Tests/Provider/PhpCodeSnifferDiagnosticsProviderTest.php b/lib/Extension/PhpCodeSniffer/Tests/Provider/PhpCodeSnifferDiagnosticsProviderTest.php deleted file mode 100644 index d15dcbe52c..0000000000 --- a/lib/Extension/PhpCodeSniffer/Tests/Provider/PhpCodeSnifferDiagnosticsProviderTest.php +++ /dev/null @@ -1,160 +0,0 @@ -getPhpCodeSnifferDiagnosticsProvider(true); - - $cancel = new NullCancellationToken(); - $document = ProtocolFactory::textDocumentItem('/tmp/test.php', $fileContent); - - $diagnostics = wait($provider->provideDiagnostics($document, $cancel)); - self::assertIsArray($diagnostics); - foreach ($diagnostics as $diagnostic) { - self::assertInstanceOf(Diagnostic::class, $diagnostic); - } - self::assertCount($expectedDiagnostics, $diagnostics); - } - - #[DataProvider('fileProvider')] - public function testProvideDiagnosticsHidden(string $fileContent): void - { - $provider = $this->getPhpCodeSnifferDiagnosticsProvider(false); - - $cancel = new NullCancellationToken(); - $document = ProtocolFactory::textDocumentItem('/tmp/test.php', $fileContent); - - $diagnostics = wait($provider->provideDiagnostics($document, $cancel)); - self::assertIsArray($diagnostics); - self::assertCount(0, $diagnostics); - } - - #[DataProvider('fileProvider')] - public function testProvideActionsForVisibleDiagnostics(string $fileContent, int $expectedDiagnostics): void - { - $provider = $this->getPhpCodeSnifferDiagnosticsProvider(true); - - $cancel = new NullCancellationToken(); - $document = ProtocolFactory::textDocumentItem('/tmp/test.php', $fileContent); - - $actions = wait( - $provider->provideActionsFor( - $document, - new Range( - new Position(0, 0), - new Position(PHP_INT_MAX, PHP_INT_MAX) - ), - $cancel - ) - ); - - self::assertIsArray($actions); - if ($expectedDiagnostics > 0) { - self::assertTrue(count($actions) > 0, 'Expected at least one action if file has diagnostics'); - } - foreach ($actions as $action) { - self::assertInstanceOf(CodeAction::class, $action); - } - } - - #[DataProvider('fileProvider')] - public function testProvideActionsForHiddenDiagnostics(string $fileContent, int $expectedDiagnostics): void - { - $provider = $this->getPhpCodeSnifferDiagnosticsProvider(false); - - $cancel = new NullCancellationToken(); - $document = ProtocolFactory::textDocumentItem('/tmp/test.php', $fileContent); - - $actions = wait( - $provider->provideActionsFor( - $document, - new Range( - new Position(0, 0), - new Position(PHP_INT_MAX, PHP_INT_MAX) - ), - $cancel - ) - ); - - self::assertIsArray($actions); - if ($expectedDiagnostics > 0) { - self::assertTrue(count($actions) > 0, 'Expected at least one action if file has diagnostics'); - } - foreach ($actions as $action) { - self::assertInstanceOf(CodeAction::class, $action); - } - } - - public function getPhpCodeSnifferDiagnosticsProvider(bool $showDiagnostics): PhpCodeSnifferDiagnosticsProvider - { - $phpCodeSniffer = $this->getPhpCodeSniffer(); - - return new PhpCodeSnifferDiagnosticsProvider( - $phpCodeSniffer, - $showDiagnostics, - new RangesForDiff(), - new NullLogger() - ); - } - - /** - * @return Generator - */ - public static function fileProvider(): Generator - { - yield 'PEAR: tab indentation' => [ - << [ - << - * @license https://mit-license.org/ MIT - * @link Link - **/ - - \$foo = 'bar'; - - EOF, - 0 - ]; - } -} diff --git a/lib/Extension/Prophecy/ProphecyExtension.php b/lib/Extension/Prophecy/ProphecyExtension.php deleted file mode 100644 index e993db0bef..0000000000 --- a/lib/Extension/Prophecy/ProphecyExtension.php +++ /dev/null @@ -1,39 +0,0 @@ -register(ProphecyMemberContextResolver::class, function (Container $container) { - return new ProphecyMemberContextResolver(); - }, [ WorseReflectionExtension::TAG_MEMBER_TYPE_RESOLVER => []]); - - $container->register(SourceCodeLocator::class, function (Container $container) { - return new ProphecyStubLocator(); - }, [ WorseReflectionExtension::TAG_SOURCE_LOCATOR => [ - 'priority' => 290 - ]]); - } - - public function configure(Resolver $schema): void - { - } - - public function name(): string - { - return 'prophecy'; - } -} diff --git a/lib/Extension/Prophecy/ProphecySuggestExtension.php b/lib/Extension/Prophecy/ProphecySuggestExtension.php deleted file mode 100644 index 5acd5d8626..0000000000 --- a/lib/Extension/Prophecy/ProphecySuggestExtension.php +++ /dev/null @@ -1,50 +0,0 @@ -register('prophecy.suggest', function (Container $container) { - return new PhpactorComposerSuggestor( - $container->expect(ConfigurationExtension::SERVICE_PHPACTOR_CONFIG_LOCAL, JsonConfig::class), - $container->get(ComposerInspector::class), - function (JsonConfig $config, ComposerInspector $inspector) { - if ($config->has(ProphecyExtension::PARAM_ENABLED)) { - return Changes::none(); - } - - if (!$inspector->package('phpspec/prophecy')) { - return Changes::none(); - } - - return Changes::from([ - new PhpactorConfigChange('Prophecy mocking framework detected, enable Prophecy extension?', function (bool $enable) { - return [ - ProphecyExtension::PARAM_ENABLED => $enable, - ]; - }) - ]); - } - ); - }, [ - ConfigurationExtension::TAG_SUGGESTOR => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/Prophecy/Tests/Integration/WorseReflection/ProphecyMemberContextResolverTest.php b/lib/Extension/Prophecy/Tests/Integration/WorseReflection/ProphecyMemberContextResolverTest.php deleted file mode 100644 index b00723f05e..0000000000 --- a/lib/Extension/Prophecy/Tests/Integration/WorseReflection/ProphecyMemberContextResolverTest.php +++ /dev/null @@ -1,170 +0,0 @@ -resolve( - <<<'EOT' - prophesize(Hello::class); - - wrAssertType('Prophecy\Prophecy\ObjectProphecy', $prophet); - wrAssertType('Prophecy\Prophecy\MethodProphecy', $prophet->bar()); - wrAssertType('Prophecy\Prophecy\MethodProphecy', $prophet->bar()->will()); - wrAssertType('Prophecy\Prophecy\ObjectProphecy', $prophet->bar()->getObjectProphecy()); - wrAssertType('Prophecy\Prophecy\MethodProphecy', $prophet->bar()->getObjectProphecy()->bar()); - wrAssertType('Prophecy\Prophecy\MethodProphecy', $prophet->bar()->willReturn('')->getObjectProphecy()->bar()); - wrAssertType('string', $prophet->bar()->getMethodName()); - wrAssertType('Hello', $prophet->reveal()); - EOT - , - ); - } - public function testMethodProphecy(): void - { - $this->resolve( - <<<'EOT' - prophesize(Hello::class); - - wrAssertType('Prophecy\Prophecy\MethodProphecy', $prophet->bar()->willReturn('')); - wrAssertType('Prophecy\Prophecy\ObjectProphecy', $prophet->bar()->willReturn('')->getObjectProphecy()); - wrAssertType('Prophecy\Prophecy\MethodProphecy', $prophet->bar()->willReturn('')->getObjectProphecy()->bar()); - EOT - , - ); - } - - public function testProphesizeFromProperty(): void - { - $this->resolve( - <<<'EOT' - - */ - private $hello; - - public function hello(): void - { - wrAssertType('Prophecy\Prophecy\ObjectProphecy', $this->hello); - wrAssertType('Prophecy\Prophecy\MethodProphecy', $this->hello->bar()); - } - } - EOT - , - ); - } - - public function testProphesizeFromMethod(): void - { - $this->resolve( - <<<'EOT' - - */ - public function foobar(): ObjectProphecy - - public function hello(): void - { - wrAssertType('Prophecy\Prophecy\ObjectProphecy', $this->foobar()); - } - } - EOT - , - ); - } - - public function testProphesizeSelfInATrait(): void - { - $this->resolve( - <<<'EOT' - prophesize(self::class); - } - } - - class TestCase { - use StorageManagerHelperTrait; - /** - * @return ObjectProphecy - */ - public function foobar(): ObjectProphecy - - public function hello(): void - { - wrAssertType('ObjectProphecy', $this->getStorageManager()); - } - } - EOT - , - ); - } - - public function resolve(string $sourceCode): void - { - $sourceCode = TextDocumentBuilder::fromUnknown($sourceCode); - $reflector = ReflectorBuilder::create() - ->addFrameWalker(new TestAssertWalker($this)) - ->addLocator(new ProphecyStubLocator()) - ->addSource($sourceCode) - ->addMemberContextResolver(new ProphecyMemberContextResolver()) - ->build(); - - $reflector->reflectOffset($sourceCode, mb_strlen($sourceCode)); - } -} diff --git a/lib/Extension/Prophecy/WorseReflection/ProphecyMemberContextResolver.php b/lib/Extension/Prophecy/WorseReflection/ProphecyMemberContextResolver.php deleted file mode 100644 index 4a1cbd0f23..0000000000 --- a/lib/Extension/Prophecy/WorseReflection/ProphecyMemberContextResolver.php +++ /dev/null @@ -1,105 +0,0 @@ -class() instanceof ReflectionClass) { - return null; - } - - if ($type instanceof GenericClassType && $type->instanceof(TypeFactory::reflectedClass($reflector, 'Prophecy\Prophecy\ObjectProphecy'))->isTrue()) { - return $this->fromGeneric($reflector, $type); - } - - return $this->fromProphesize($reflector, $member, $arguments); - } - - private function fromProphesize( - Reflector $reflector, - ReflectionMember $member, - ?FunctionArguments $arguments - ): ?Type { - if (!$member instanceof ReflectionMethod) { - return null; - } - - if ($member->name() !== 'prophesize') { - return null; - } - - if (null === $arguments) { - return null; - } - - if ($arguments->count() !== 1) { - return null; - } - - $arg = $arguments->at(0)->type(); - - if (!$arg instanceof ClassStringType) { - return null; - } - - $className = $arg->className(); - - if (null === $className) { - return null; - } - - $innerType = TypeFactory::class($className); - - $type = new GenericClassType($reflector, ClassName::fromString('Prophecy\Prophecy\ObjectProphecy'), [$innerType]); - - return $this->fromGeneric($reflector, $type); - } - - private function fromGeneric(Reflector $reflector, GenericClassType $type): Type - { - $innerType = $type->arguments()[0]; - if (!$innerType instanceof ClassType) { - return TypeFactory::undefined(); - } - - try { - $innerReflection = $reflector->reflectClassLike($innerType->name()); - } catch (NotFound) { - return TypeFactory::unknown(); - } - return $type->mergeMembers($innerReflection->members()->map(function (ReflectionMember $member) use ($reflector, $innerType) { - if (!$member instanceof ReflectionMethod) { - return $member; - } - return VirtualReflectionMethod::fromReflectionMethod($member)->withInferredType( - new GenericClassType($reflector, ClassName::fromString('Prophecy\Prophecy\MethodProphecy'), [ - $innerType - ]) - ); - })); - } -} diff --git a/lib/Extension/Prophecy/WorseReflection/ProphecyStubLocator.php b/lib/Extension/Prophecy/WorseReflection/ProphecyStubLocator.php deleted file mode 100644 index 5e59de99c6..0000000000 --- a/lib/Extension/Prophecy/WorseReflection/ProphecyStubLocator.php +++ /dev/null @@ -1,26 +0,0 @@ -locator = new InternalLocator([ - 'Prophecy\Prophecy\ObjectProphecy' => __DIR__ . '/../stubs/Prophecy.stub', - 'Prophecy\Prophecy\MethodProphecy' => __DIR__ . '/../stubs/Prophecy.stub' - ]); - } - - public function locate(Name $name): TextDocument - { - return $this->locator->locate($name); - } -} diff --git a/lib/Extension/Prophecy/stubs/Prophecy.stub b/lib/Extension/Prophecy/stubs/Prophecy.stub deleted file mode 100644 index 268f9d8de9..0000000000 --- a/lib/Extension/Prophecy/stubs/Prophecy.stub +++ /dev/null @@ -1,310 +0,0 @@ - - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function withArguments($arguments) - { - } - - /** - * Sets custom promise to the prophecy. - * - * @param callable|Promise\PromiseInterface $promise - * - * @return MethodProphecy - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function will($promise) - { - } - - /** - * Sets return promise to the prophecy. - * - * @see \Prophecy\Promise\ReturnPromise - * - * @return MethodProphecy - */ - public function willReturn() - { - } - - /** - * @param array $items - * @param mixed $return - * - * @return MethodProphecy - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function willYield($items, $return = null) - { - } - - /** - * Sets return argument promise to the prophecy. - * - * @param int $index The zero-indexed number of the argument to return - * - * @see \Prophecy\Promise\ReturnArgumentPromise - * - * @return MethodProphecy - */ - public function willReturnArgument($index = 0) - { - } - - /** - * Sets throw promise to the prophecy. - * - * @see \Prophecy\Promise\ThrowPromise - * - * @param string|\Exception $exception Exception class or instance - * - * @return MethodProphecy - */ - public function willThrow($exception) - { - } - - /** - * Sets custom prediction to the prophecy. - * - * @param callable|Prediction\PredictionInterface $prediction - * - * @return MethodProphecy - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function should($prediction) - { - } - - /** - * Sets call prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallPrediction - * - * @return MethodProphecy - */ - public function shouldBeCalled() - { - } - - /** - * Sets no calls prediction to the prophecy. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * - * @return MethodProphecy - */ - public function shouldNotBeCalled() - { - } - - /** - * Sets call times prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @param $count - * - * @return MethodProphecy - */ - public function shouldBeCalledTimes($count) - { - } - - /** - * Sets call times prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @return MethodProphecy - */ - public function shouldBeCalledOnce() - { - } - - /** - * Checks provided prediction immediately. - * - * @param callable|Prediction\PredictionInterface $prediction - * - * @return MethodProphecy - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function shouldHave($prediction) - { - } - - /** - * Checks call prediction. - * - * @see \Prophecy\Prediction\CallPrediction - * - * @return MethodProphecy - */ - public function shouldHaveBeenCalled() - { - return MethodProphecy->shouldHave(new Prediction\CallPrediction); - } - - /** - * Checks no calls prediction. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * - * @return MethodProphecy - */ - public function shouldNotHaveBeenCalled() - { - return MethodProphecy->shouldHave(new Prediction\NoCallsPrediction); - } - - /** - * Checks no calls prediction. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * @deprecated - * - * @return MethodProphecy - */ - public function shouldNotBeenCalled() - { - return MethodProphecy->shouldNotHaveBeenCalled(); - } - - /** - * Checks call times prediction. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @param int $count - * - * @return MethodProphecy - */ - public function shouldHaveBeenCalledTimes($count) - { - return MethodProphecy->shouldHave(new Prediction\CallTimesPrediction($count)); - } - - /** - * Checks call times prediction. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @return MethodProphecy - */ - public function shouldHaveBeenCalledOnce() - { - } - - /** - * Checks currently registered [with should(...)] prediction. - */ - public function checkPrediction() - { - } - - /** - * Returns currently registered promise. - * - * @return null|Promise\PromiseInterface - */ - public function getPromise() - { - } - - /** - * Returns currently registered prediction. - * - * @return null|Prediction\PredictionInterface - */ - public function getPrediction() - { - } - - /** - * Returns predictions that were checked on this object. - * - * @return Prediction\PredictionInterface[] - */ - public function getCheckedPredictions() - { - } - - /** - * Returns object prophecy this method prophecy is tied to. - * - * @return ObjectProphecy - */ - public function getObjectProphecy() - { - } - - /** - * Returns method name. - * - * @return string - */ - public function getMethodName() - { - } - - /** - * Returns arguments wildcard. - * - * @return Argument\ArgumentsWildcard - */ - public function getArgumentsWildcard() - { - } - - /** - * @return bool - */ - public function hasReturnVoid() - { - } -} diff --git a/lib/Extension/ReferenceFinder/ReferenceFinderExtension.php b/lib/Extension/ReferenceFinder/ReferenceFinderExtension.php deleted file mode 100644 index 9ffe2ebe8f..0000000000 --- a/lib/Extension/ReferenceFinder/ReferenceFinderExtension.php +++ /dev/null @@ -1,87 +0,0 @@ -register(self::SERVICE_DEFINITION_LOCATOR, function (Container $container) { - $locators = []; - foreach (array_keys($container->getServiceIdsForTag(self::TAG_DEFINITION_LOCATOR)) as $serviceId) { - $locator = $container->get($serviceId); - if (null === $locator) { - continue; - } - $locators[] = $locator; - } - - return new ChainDefinitionLocationProvider($locators, LoggingExtension::channelLogger($container, 'LSP-REF')); - }); - - $container->register(self::SERVICE_TYPE_LOCATOR, function (Container $container) { - /** @var list $locators */ - $locators = []; - foreach (array_keys($container->getServiceIdsForTag(self::TAG_TYPE_LOCATOR)) as $serviceId) { - $locators[] = $container->expect($serviceId, TypeLocator::class); - } - - return new ChainTypeLocator($locators, LoggingExtension::channelLogger($container, 'LSP-REF')); - }); - - $container->register(self::SERVICE_IMPLEMENTATION_FINDER, function (Container $container) { - $finders = []; - foreach (array_keys($container->getServiceIdsForTag(self::TAG_IMPLEMENTATION_FINDER)) as $serviceId) { - $finders[] = $container->get($serviceId); - } - - return new ChainImplementationFinder($finders); - }); - - $container->register(ReferenceFinder::class, function (Container $container) { - $finders = []; - foreach (array_keys($container->getServiceIdsForTag(self::TAG_REFERENCE_FINDER)) as $serviceId) { - $finders[] = $container->get($serviceId); - } - - return new ChainReferenceFinder($finders); - }); - - $container->register(NameSearcher::class, function (Container $container) { - foreach (array_keys($container->getServiceIdsForTag(self::TAG_NAME_SEARCHER)) as $serviceId) { - return $container->get($serviceId); - } - - return new NullNameSearcher(); - }); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/ReferenceFinder/Tests/Example/SomeDefinitionLocator.php b/lib/Extension/ReferenceFinder/Tests/Example/SomeDefinitionLocator.php deleted file mode 100644 index 8c093aaccc..0000000000 --- a/lib/Extension/ReferenceFinder/Tests/Example/SomeDefinitionLocator.php +++ /dev/null @@ -1,28 +0,0 @@ -register('some_definition_locator', function (Container $container) { - return new SomeDefinitionLocator(); - }, [ ReferenceFinderExtension::TAG_DEFINITION_LOCATOR => []]); - $container->register('some_type_locator', function (Container $container) { - return new SomeTypeLocator(); - }, [ ReferenceFinderExtension::TAG_TYPE_LOCATOR => []]); - - $container->register('some_implementation_finder', function (Container $container) { - return new SomeImplementationFinder(); - }, [ ReferenceFinderExtension::TAG_IMPLEMENTATION_FINDER=> []]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/ReferenceFinder/Tests/Example/SomeImplementationFinder.php b/lib/Extension/ReferenceFinder/Tests/Example/SomeImplementationFinder.php deleted file mode 100644 index d716d553ae..0000000000 --- a/lib/Extension/ReferenceFinder/Tests/Example/SomeImplementationFinder.php +++ /dev/null @@ -1,16 +0,0 @@ -get(ReferenceFinderExtension::SERVICE_DEFINITION_LOCATOR); - $this->assertInstanceOf(ChainDefinitionLocationProvider::class, $locator); - } - - public function testEmptyChainTypeLocator(): void - { - $container = PhpactorContainer::fromExtensions([ - ReferenceFinderExtension::class, - LoggingExtension::class, - ]); - - $locator = $container->get(ReferenceFinderExtension::SERVICE_TYPE_LOCATOR); - $this->assertInstanceOf(ChainTypeLocator::class, $locator); - } - - public function testChainDefinitionLocatorLocatorWithRegisteredLocators(): void - { - $container = PhpactorContainer::fromExtensions([ - ReferenceFinderExtension::class, - SomeExtension::class, - LoggingExtension::class, - ]); - - $locator = $container->get(ReferenceFinderExtension::SERVICE_DEFINITION_LOCATOR); - assert($locator instanceof DefinitionLocator); - $this->assertInstanceOf(ChainDefinitionLocationProvider::class, $locator); - - $location = $locator->locateDefinition(TextDocumentBuilder::create('asd')->build(), ByteOffset::fromInt(1)); - - $this->assertEquals( - $location->first()->location(), - Location::fromPathAndOffsets( - SomeDefinitionLocator::EXAMPLE_PATH, - SomeDefinitionLocator::EXAMPLE_OFFSET, - SomeDefinitionLocator::EXAMPLE_OFFSET_END - ) - ); - } - - public function testChainLocatorLocatorWithRegisteredLocators(): void - { - $container = PhpactorContainer::fromExtensions([ - ReferenceFinderExtension::class, - SomeExtension::class, - LoggingExtension::class, - ]); - - $locator = $container->get(ReferenceFinderExtension::SERVICE_TYPE_LOCATOR); - $this->assertInstanceOf(ChainTypeLocator::class, $locator); - - $location = $locator->locateTypes(TextDocumentBuilder::create('asd')->build(), ByteOffset::fromInt(1)); - - $this->assertEquals( - $location->first()->location(), - Location::fromPathAndOffsets( - SomeTypeLocator::EXAMPLE_PATH, - SomeTypeLocator::EXAMPLE_OFFSET, - SomeTypeLocator::EXAMPLE_OFFSET_END - ) - ); - } - - public function testReturnsImplementationFinder(): void - { - $container = PhpactorContainer::fromExtensions([ - ReferenceFinderExtension::class, - SomeExtension::class, - LoggingExtension::class, - ]); - - $finder = $container->get(ReferenceFinderExtension::SERVICE_IMPLEMENTATION_FINDER); - $this->assertInstanceOf(ClassImplementationFinder::class, $finder); - } - - public function testReturnsReferenceFinder(): void - { - $container = PhpactorContainer::fromExtensions([ - ReferenceFinderExtension::class, - SomeExtension::class, - LoggingExtension::class, - ]); - - $finder = $container->get(ReferenceFinder::class); - $this->assertInstanceOf(ReferenceFinder::class, $finder); - } -} diff --git a/lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php b/lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php deleted file mode 100644 index 12fbbe4a20..0000000000 --- a/lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php +++ /dev/null @@ -1,72 +0,0 @@ -setDefaults([ - self::PARAM_LANGUAGE => 'php', - self::PARAM_TARGET => OpenFileResponse::TARGET_FOCUSED_WINDOW - ]); - $resolver->setRequired([ - self::PARAM_OFFSET, - self::PARAM_SOURCE, - self::PARAM_PATH, - ]); - $resolver->setEnums([ - self::PARAM_TARGET => OpenFileResponse::VALID_TARGETS, - ]); - $resolver->setTypes([ - self::PARAM_OFFSET => 'integer', - self::PARAM_LANGUAGE => 'string', - self::PARAM_TARGET => 'string', - ]); - $resolver->setDescriptions([ - self::PARAM_OFFSET => 'Number of character into the buffer', - self::PARAM_SOURCE => 'Content of the current file', - self::PARAM_PATH => 'Path of the current file', - self::PARAM_LANGUAGE => 'Language of the current file', - self::PARAM_TARGET => 'Where should the reference be opened', - ]); - } - - public function handle(array $arguments) - { - $document = TextDocumentBuilder::create($arguments[self::PARAM_SOURCE]) - ->uri($arguments[self::PARAM_PATH]) - ->language($arguments[self::PARAM_LANGUAGE])->build(); - - $offset = ByteOffset::fromInt($arguments[self::PARAM_OFFSET]); - $location = $this->locator->locateDefinition($document, $offset)->first()->location(); - - return OpenFileResponse::fromPathAndOffset( - $location->uri()->path(), - $location->range()->start()->toInt() - )->withTarget($arguments[self::PARAM_TARGET]); - } -} diff --git a/lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php b/lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php deleted file mode 100644 index 6173a1b8d0..0000000000 --- a/lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php +++ /dev/null @@ -1,130 +0,0 @@ -setDefaults([ - self::PARAM_LANGUAGE => 'php', - self::PARAM_TARGET => OpenFileResponse::TARGET_FOCUSED_WINDOW - ]); - $resolver->setRequired([ - self::PARAM_OFFSET, - self::PARAM_SOURCE, - self::PARAM_PATH, - ]); - $resolver->setEnums([ - self::PARAM_TARGET => OpenFileResponse::VALID_TARGETS, - ]); - $resolver->setTypes([ - self::PARAM_OFFSET => 'integer', - self::PARAM_LANGUAGE => 'string', - self::PARAM_TARGET => 'string', - ]); - $resolver->setDescriptions([ - self::PARAM_OFFSET => 'Number of character into the buffer', - self::PARAM_SOURCE => 'Content of the current file', - self::PARAM_PATH => 'Path of the current file', - self::PARAM_LANGUAGE => 'Language of the current file', - self::PARAM_TARGET => 'Where should the reference be opened', - ]); - } - - public function handle(array $arguments) - { - $document = TextDocumentBuilder::create($arguments[self::PARAM_SOURCE]) - ->uri($arguments[self::PARAM_PATH]) - ->language($arguments[self::PARAM_LANGUAGE])->build(); - - $offset = ByteOffset::fromInt($arguments[self::PARAM_OFFSET]); - $locations = $this->finder->findImplementations($document, $offset); - - if (1 !== $locations->count()) { - return new FileReferencesResponse($this->locationsToReferences($locations)); - } - - $location = $locations->first(); - return OpenFileResponse::fromPathAndOffset( - $location->uri()->path(), - $location->range()->start()->toInt() - )->withTarget($arguments[self::PARAM_TARGET]); - } - - /** - * @return array - */ - private function locationsToReferences(Locations $locations): array - { - $references = []; - foreach ($locations as $location) { - assert($location instanceof Location); - $contents = $this->fileContents($location); - - // Opening at the start of the reference - $start = $location->range()->start(); - $lineCol = LineCol::fromByteOffset($contents, $start); - $line = (new LineAtOffset())->__invoke($contents, $start->toInt()); - - $references[] = FileReferences::fromPathAndReferences( - $location->uri()->path(), - [ - Reference::fromStartEndLineNumberLineAndCol( - $location->range()->start()->toInt(), - $location->range()->end()->toInt(), - $lineCol->line(), - $line, - $lineCol->col() - ) - ] - ); - } - - return $references; - } - - private function fileContents(Location $location): string - { - $contents = file_get_contents($location->uri()->path()); - if ($contents === false) { - throw new RuntimeException(sprintf( - 'Could not open file "%s"', - $location->uri()->path() - )); - } - - return $contents; - } -} diff --git a/lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php b/lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php deleted file mode 100644 index 11907f7f7f..0000000000 --- a/lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php +++ /dev/null @@ -1,72 +0,0 @@ -setDefaults([ - self::PARAM_LANGUAGE => 'php', - self::PARAM_TARGET => OpenFileResponse::TARGET_FOCUSED_WINDOW - ]); - $resolver->setRequired([ - self::PARAM_OFFSET, - self::PARAM_SOURCE, - self::PARAM_PATH, - ]); - $resolver->setEnums([ - self::PARAM_TARGET => OpenFileResponse::VALID_TARGETS, - ]); - $resolver->setTypes([ - self::PARAM_OFFSET => 'integer', - self::PARAM_LANGUAGE => 'string', - self::PARAM_TARGET => 'string', - ]); - $resolver->setDescriptions([ - self::PARAM_OFFSET => 'Number of character into the buffer', - self::PARAM_SOURCE => 'Content of the current file', - self::PARAM_PATH => 'Path of the current file', - self::PARAM_LANGUAGE => 'Language of the current file', - self::PARAM_TARGET => 'Where should the reference be opened', - ]); - } - - public function handle(array $arguments) - { - $document = TextDocumentBuilder::create($arguments[self::PARAM_SOURCE]) - ->uri($arguments[self::PARAM_PATH]) - ->language($arguments[self::PARAM_LANGUAGE])->build(); - - $offset = ByteOffset::fromInt($arguments[self::PARAM_OFFSET]); - $location = $this->locator->locateTypes($document, $offset)->first(); - - return OpenFileResponse::fromPathAndOffset( - $location->location()->uri()->path(), - $location->location()->range()->start()->toInt() - )->withTarget($arguments[self::PARAM_TARGET]); - } -} diff --git a/lib/Extension/ReferenceFinderRpc/ReferenceFinderRpcExtension.php b/lib/Extension/ReferenceFinderRpc/ReferenceFinderRpcExtension.php deleted file mode 100644 index d7f43ee6f0..0000000000 --- a/lib/Extension/ReferenceFinderRpc/ReferenceFinderRpcExtension.php +++ /dev/null @@ -1,36 +0,0 @@ -register('reference_finder_rpc.handler.goto_definition', function (Container $container) { - return new GotoDefinitionHandler($container->get(ReferenceFinderExtension::SERVICE_DEFINITION_LOCATOR)); - }, [ RpcExtension::TAG_RPC_HANDLER => [ 'name' => 'goto_definition' ]]); - - $container->register('reference_finder_rpc.handler.goto_type', function (Container $container) { - return new GotoTypeHandler($container->get(ReferenceFinderExtension::SERVICE_TYPE_LOCATOR)); - }, [ RpcExtension::TAG_RPC_HANDLER => [ 'name' => 'goto_type' ]]); - - $container->register('reference_finder_rpc.handler.goto_implementation', function (Container $container) { - return new GotoImplementationHandler($container->get(ReferenceFinderExtension::SERVICE_IMPLEMENTATION_FINDER)); - }, [ RpcExtension::TAG_RPC_HANDLER => [ 'name' => 'goto_implementation' ]]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoDefinitionHandlerTest.php b/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoDefinitionHandlerTest.php deleted file mode 100644 index 3a9537e20b..0000000000 --- a/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoDefinitionHandlerTest.php +++ /dev/null @@ -1,43 +0,0 @@ -create()->handle('goto_definition', [ - 'source' => self::EXAMPLE_SOURCE, - 'offset' => self::EXAMPLE_OFFSET, - 'path' => self::EXAMPLE_PATH, - 'target' => OpenFileResponse::TARGET_HORIZONTAL_SPLIT, - ]); - - $this->assertInstanceOf(OpenFileResponse::class, $location); - $this->assertEquals(self::EXAMPLE_PATH, $location->path()); - $this->assertEquals(OpenFileResponse::TARGET_HORIZONTAL_SPLIT, $location->target()); - - } - - public function create(): HandlerTester - { - $location = Location::fromPathAndOffsets(self::EXAMPLE_PATH, self::EXAMPLE_OFFSET, self::EXAMPLE_OFFSET); - - return new HandlerTester( - new GotoDefinitionHandler( - TestDefinitionLocator::fromSingleLocation(TypeFactory::unknown(), $location) - ) - ); - } -} diff --git a/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoImplementationHandlerTest.php b/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoImplementationHandlerTest.php deleted file mode 100644 index dfb7c320bf..0000000000 --- a/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoImplementationHandlerTest.php +++ /dev/null @@ -1,73 +0,0 @@ -create([ - Location::fromPathAndOffsets(self::EXAMPLE_PATH, 10, 10) - ])->handle('goto_implementation', [ - 'source' => self::EXAMPLE_SOURCE, - 'offset' => self::EXAMPLE_OFFSET, - 'path' => self::EXAMPLE_PATH, - 'target' => OpenFileResponse::TARGET_HORIZONTAL_SPLIT, - ]); - - $this->assertInstanceOf(OpenFileResponse::class, $location); - $this->assertEquals(self::EXAMPLE_PATH, $location->path()); - $this->assertEquals(OpenFileResponse::TARGET_HORIZONTAL_SPLIT, $location->target()); - } - - public function testSelectFromMultiple(): void - { - $response = $this->create([ - Location::fromPathAndOffsets(__FILE__, 20, 20), - Location::fromPathAndOffsets(__FILE__, 40, 40) - ])->handle('goto_implementation', [ - 'source' => self::EXAMPLE_SOURCE, - 'offset' => self::EXAMPLE_OFFSET, - 'path' => self::EXAMPLE_PATH, - 'target' => OpenFileResponse::TARGET_HORIZONTAL_SPLIT, - ]); - - $this->assertInstanceOf(FileReferencesResponse::class, $response); - } - - /** - * @param Location[] $locations - */ - public function create(array $locations): HandlerTester - { - $locator = new class($locations) implements ClassImplementationFinder { - /** - * @param Location[] $locations - */ - public function __construct(private array $locations) - { - } - - public function findImplementations(TextDocument $document, ByteOffset $byteOffset, bool $includeDefinition = false): Locations - { - return new Locations($this->locations); - } - }; - - return new HandlerTester(new GotoImplementationHandler($locator)); - } -} diff --git a/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoTypeHandlerTest.php b/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoTypeHandlerTest.php deleted file mode 100644 index ede6c17846..0000000000 --- a/lib/Extension/ReferenceFinderRpc/Tests/Unit/Handler/GotoTypeHandlerTest.php +++ /dev/null @@ -1,56 +0,0 @@ -create()->handle('goto_type', [ - 'source' => self::EXAMPLE_SOURCE, - 'offset' => self::EXAMPLE_OFFSET, - 'path' => self::EXAMPLE_PATH, - 'target' => OpenFileResponse::TARGET_HORIZONTAL_SPLIT, - ]); - - $this->assertInstanceOf(OpenFileResponse::class, $location); - $this->assertEquals(self::EXAMPLE_PATH, $location->path()); - $this->assertEquals(OpenFileResponse::TARGET_HORIZONTAL_SPLIT, $location->target()); - } - - public function create(): HandlerTester - { - $locator = new class() implements TypeLocator { - public function locateTypes(TextDocument $document, ByteOffset $byteOffset): TypeLocations - { - return - new TypeLocations([ - new TypeLocation( - new MixedType(), - new Location( - $document->uriOrThrow(), - ByteOffsetRange::fromByteOffsets($byteOffset, $byteOffset) - ), - ) - ]); - } - }; - return new HandlerTester(new GotoTypeHandler($locator)); - } -} diff --git a/lib/Extension/ReferenceFinderRpc/Tests/Unit/ReferenceFinderRpcExtensionTest.php b/lib/Extension/ReferenceFinderRpc/Tests/Unit/ReferenceFinderRpcExtensionTest.php deleted file mode 100644 index 905e5038f5..0000000000 --- a/lib/Extension/ReferenceFinderRpc/Tests/Unit/ReferenceFinderRpcExtensionTest.php +++ /dev/null @@ -1,60 +0,0 @@ -createContainer(); - $handler = $container->get(RpcExtension::SERVICE_REQUEST_HANDLER); - - $this->assertInstanceOf(RequestHandler::class, $handler); - $response = $handler->handle(Request::fromNameAndParameters('goto_definition', [ - 'offset' => 10, - 'source' => ' __FILE__, - ])); - - $this->assertInstanceOf(ErrorResponse::class, $response); - $this->assertStringContainsString('No definition locators', $response->message()); - } - - public function testGotoType(): void - { - $container = $this->createContainer(); - $handler = $container->get(RpcExtension::SERVICE_REQUEST_HANDLER); - - $this->assertInstanceOf(RequestHandler::class, $handler); - $response = $handler->handle(Request::fromNameAndParameters('goto_type', [ - 'offset' => 10, - 'source' => ' __FILE__, - ])); - - $this->assertInstanceOf(ErrorResponse::class, $response); - $this->assertStringContainsString('No type locators', $response->message()); - } - - private function createContainer(): Container - { - $container = PhpactorContainer::fromExtensions([ - ReferenceFinderExtension::class, - ReferenceFinderRpcExtension::class, - RpcExtension::class, - LoggingExtension::class, - ]); - return $container; - } -} diff --git a/lib/Extension/Rpc/Command/RpcCommand.php b/lib/Extension/Rpc/Command/RpcCommand.php deleted file mode 100644 index dd071616a4..0000000000 --- a/lib/Extension/Rpc/Command/RpcCommand.php +++ /dev/null @@ -1,121 +0,0 @@ -setDescription('Execute one or many actions from stdin and receive an imperative response'); - $this->addOption('replay', null, InputOption::VALUE_NONE, 'Replay the last request'); - $this->addOption('pretty', null, InputOption::VALUE_NONE, 'Pretty print JSON'); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $stdin = $this->resolveInput((bool) $input->getOption('replay')); - $request = json_decode($stdin, true); - - if (null === $request) { - throw new InvalidArgumentException(sprintf( - 'Could not decode JSON: %s', - $stdin - )); - } - - $response = $this->processRequest($request); - $flags = 0; - - if ($input->getOption('pretty')) { - $flags = JSON_PRETTY_PRINT; - } - - $output->write((string) json_encode([ - 'version' => RpcVersion::asString(), - 'action' => $response->name(), - 'parameters' => $response->parameters(), - ], $flags), false, OutputInterface::OUTPUT_RAW); - - return 0; - } - - private function processRequest(array $request) - { - $request = Request::fromArray($request); - - return $this->handler->handle($request); - } - - private function resolveInput(bool $replay): string - { - if ($replay) { - if (false === $this->storeReplay) { - throw new RuntimeException( - 'You must explicitly enable replay, set `rpc.store_replay` to `true` in your config.' - ); - } - return $this->lastRequest(); - } - - return $this->stdin(); - } - - private function stdin(): string - { - $in = ''; - - while ($line = fgets($this->inputStream)) { - $in .= $line; - } - - if ($this->storeReplay) { - $this->storeReplay($in); - } - - return $in; - } - - private function lastRequest() - { - $path = $this->replayPath; - if (false === file_exists($path)) { - throw new RuntimeException(sprintf( - 'Replace file does not exist at "%s"', - $path - )); - } - - return file_get_contents($path); - } - - private function storeReplay(string $in): void - { - $path = $this->replayPath; - - if (false === file_exists(dirname($this->replayPath))) { - mkdir(dirname($path), 0700, true); - } - - file_put_contents($path, $in); - chmod($path, 0700); - } -} diff --git a/lib/Extension/Rpc/Diff/TextEditBuilder.php b/lib/Extension/Rpc/Diff/TextEditBuilder.php deleted file mode 100644 index 794b5da29a..0000000000 --- a/lib/Extension/Rpc/Diff/TextEditBuilder.php +++ /dev/null @@ -1,73 +0,0 @@ -differ->diffToArray($original, $new); - $lineNumber = -1; - - foreach ($diff as $line) { - $lineNumber++; - $token = $line[0]; - switch ($line[1]) { - // Nothing - case 0: - break; - - // Added - case 1: - $edits[] = [ - 'start' => [ - 'line' => $lineNumber, - 'character' => 0, - ], - 'end' => [ - 'line' => $lineNumber, - 'character' => 0, - ], - 'text' => $token, - ]; - break; - - // Removed - case 2: - $edits[] = [ - 'start' => [ - 'line' => $lineNumber, - 'character' => 0, - ], - 'end' => [ - 'line' => $lineNumber + 1, - 'character' => 0, - ], - 'text' => '', - ]; - $lineNumber--; - break; - } - } - - return $edits; - } -} diff --git a/lib/Extension/Rpc/Exception/HandlerNotFound.php b/lib/Extension/Rpc/Exception/HandlerNotFound.php deleted file mode 100644 index 6d3c357ec4..0000000000 --- a/lib/Extension/Rpc/Exception/HandlerNotFound.php +++ /dev/null @@ -1,9 +0,0 @@ - */ - private array $requiredArguments = []; - - protected function requireInput(Input $input): void - { - $this->requiredArguments[$input->name()] = $input; - } - - /** @param array $arguments */ - protected function hasMissingArguments(array $arguments): bool - { - if (count($this->missingArguments($arguments)) > 0) { - return true; - } - - return false; - } - - /** @param array $arguments */ - protected function createInputCallback(array $arguments): InputCallbackResponse - { - return InputCallbackResponse::fromCallbackAndInputs( - Request::fromNameAndParameters( - $this->name(), - $arguments - ), - $this->inputsFromMissingArguments($arguments) - ); - } - - /** - * @param array $arguments - * - * @return array - */ - private function missingArguments(array $arguments): array - { - return array_keys(array_filter($arguments, function (mixed $argument, string|int $key) { - if (false === isset($this->requiredArguments[$key])) { - return false; - } - - return empty($argument); - }, ARRAY_FILTER_USE_BOTH)); - } - - /** - * @param array $arguments - * - * @return array - */ - private function inputsFromMissingArguments(array $arguments): array - { - $inputs = []; - foreach ($this->missingArguments($arguments) as $argumentName) { - if (false === isset($this->requiredArguments[$argumentName])) { - throw new InvalidArgumentException(sprintf( - 'Parameter "%s" is not set and no interactive input was made available for it', - $argumentName - )); - } - - $inputs[] = $this->requiredArguments[$argumentName]; - } - - return array_reverse($inputs); - } -} diff --git a/lib/Extension/Rpc/Handler/EchoHandler.php b/lib/Extension/Rpc/Handler/EchoHandler.php deleted file mode 100644 index 3791d205c6..0000000000 --- a/lib/Extension/Rpc/Handler/EchoHandler.php +++ /dev/null @@ -1,27 +0,0 @@ -setRequired([ - 'message', - ]); - } - - public function handle(array $arguments) - { - return EchoResponse::fromMessage($arguments['message']); - } -} diff --git a/lib/Extension/Rpc/HandlerRegistry.php b/lib/Extension/Rpc/HandlerRegistry.php deleted file mode 100644 index 724ea47155..0000000000 --- a/lib/Extension/Rpc/HandlerRegistry.php +++ /dev/null @@ -1,11 +0,0 @@ - */ - public function all(): array; -} diff --git a/lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php b/lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php deleted file mode 100644 index cf2f223fc4..0000000000 --- a/lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php +++ /dev/null @@ -1,42 +0,0 @@ -register($handler); - } - } - - public function get($handlerName): Handler - { - if (false === isset($this->handlers[$handlerName])) { - throw new HandlerNotFound(sprintf( - 'No handler "%s", available handlers: "%s"', - $handlerName, - implode('", "', array_keys($this->handlers)) - )); - } - - return $this->handlers[$handlerName]; - } - - public function all(): array - { - return $this->handlers; - } - - private function register(Handler $handler): void - { - $this->handlers[$handler->name()] = $handler; - } -} diff --git a/lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php b/lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php deleted file mode 100644 index a43b9d26e1..0000000000 --- a/lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php +++ /dev/null @@ -1,42 +0,0 @@ -serviceMap[$handlerName])) { - if (false === isset($this->serviceMap[$handlerName])) { - throw new HandlerNotFound(sprintf( - 'No handler "%s", available handlers: "%s"', - $handlerName, - implode('", "', array_keys($this->serviceMap)) - )); - } - } - - return $this->container->get($this->serviceMap[$handlerName]); - } - - public function all(): array - { - return array_map( - function (string $serviceId) { - return $this->container->get($serviceId); - }, - $this->serviceMap - ); - } -} diff --git a/lib/Extension/Rpc/Request.php b/lib/Extension/Rpc/Request.php deleted file mode 100644 index 42ad0b989d..0000000000 --- a/lib/Extension/Rpc/Request.php +++ /dev/null @@ -1,64 +0,0 @@ - $this->name, - self::KEY_PARAMETERS => $this->parameters, - ]; - } - - public function name(): string - { - return $this->name; - } - - public function parameters(): array - { - return $this->parameters; - } -} diff --git a/lib/Extension/Rpc/RequestHandler.php b/lib/Extension/Rpc/RequestHandler.php deleted file mode 100644 index b245af5f42..0000000000 --- a/lib/Extension/Rpc/RequestHandler.php +++ /dev/null @@ -1,8 +0,0 @@ -innerHandler->handle($request); - } catch (Exception $exception) { - return ErrorResponse::fromException($exception); - } - } -} diff --git a/lib/Extension/Rpc/RequestHandler/LoggingHandler.php b/lib/Extension/Rpc/RequestHandler/LoggingHandler.php deleted file mode 100644 index 664ad4a332..0000000000 --- a/lib/Extension/Rpc/RequestHandler/LoggingHandler.php +++ /dev/null @@ -1,41 +0,0 @@ -logger->debug('REQUEST', [ - 'action' => $request->name(), - 'parameters' => $request->parameters() - ]); - - $response = $this->requestHandler->handle($request); - - $level = LogLevel::DEBUG; - if ($response instanceof ErrorResponse) { - $level = LogLevel::ERROR; - } - - $this->logger->log($level, 'RESPONSE', [ - 'action' => $response->name(), - 'parameters' => $response->parameters(), - ]); - - return $response; - } -} diff --git a/lib/Extension/Rpc/RequestHandler/RequestHandler.php b/lib/Extension/Rpc/RequestHandler/RequestHandler.php deleted file mode 100644 index 8c9400e759..0000000000 --- a/lib/Extension/Rpc/RequestHandler/RequestHandler.php +++ /dev/null @@ -1,29 +0,0 @@ -registry->get($request->name()); - - $resolver = new Resolver(); - $parameters = $request->parameters(); - $defaults = $handler->configure($resolver); - $arguments = $resolver->resolve($parameters); - - return $handler->handle($arguments); - } -} diff --git a/lib/Extension/Rpc/Response.php b/lib/Extension/Rpc/Response.php deleted file mode 100644 index c156fc34fd..0000000000 --- a/lib/Extension/Rpc/Response.php +++ /dev/null @@ -1,10 +0,0 @@ - $this->path, - ]; - } - - public function path(): string - { - return $this->path; - } -} diff --git a/lib/Extension/Rpc/Response/CollectionResponse.php b/lib/Extension/Rpc/Response/CollectionResponse.php deleted file mode 100644 index 745aba378f..0000000000 --- a/lib/Extension/Rpc/Response/CollectionResponse.php +++ /dev/null @@ -1,60 +0,0 @@ -add($action); - } - } - - public static function fromActions(array $actions): self - { - return new self($actions); - } - - public function name(): string - { - return 'collection'; - } - - public function parameters(): array - { - $actions = []; - - foreach ($this->actions as $action) { - $actions[] = [ - 'name' => $action->name(), - 'parameters' => $action->parameters() - ]; - } - - return [ - 'actions' => $actions - ]; - } - - public function actions(): array - { - return $this->actions; - } - - private function add(Response $action): void - { - $this->actions[] = $action; - } -} diff --git a/lib/Extension/Rpc/Response/EchoResponse.php b/lib/Extension/Rpc/Response/EchoResponse.php deleted file mode 100644 index e9499f1122..0000000000 --- a/lib/Extension/Rpc/Response/EchoResponse.php +++ /dev/null @@ -1,34 +0,0 @@ - $this->message - ]; - } - - public function message(): string - { - return $this->message; - } -} diff --git a/lib/Extension/Rpc/Response/ErrorResponse.php b/lib/Extension/Rpc/Response/ErrorResponse.php deleted file mode 100644 index 5fd92be00f..0000000000 --- a/lib/Extension/Rpc/Response/ErrorResponse.php +++ /dev/null @@ -1,72 +0,0 @@ -getMessage(), self::exceptionDetails($exception)); - } - - public function name(): string - { - return 'error'; - } - - public function parameters(): array - { - return [ - 'message' => $this->message, - 'details' => $this->details - ]; - } - - public function message(): string - { - return $this->message; - } - - public function details(): string - { - return $this->details; - } - - private static function exceptionDetails(Exception $exception): string - { - $exceptions = [ $exception ]; - - while ($previous = $exception->getPrevious()) { - $exceptions[] = $previous; - $exception = $previous; - } - - $exceptions = array_reverse($exceptions); - - $details = []; - foreach ($exceptions as $index => $exception) { - $details[] = sprintf( - "%s: %s\n%s", - $index, - $exception->getMessage(), - $exception->getTraceAsString() - ); - } - - return implode("\n" . "\n", $details); - } -} diff --git a/lib/Extension/Rpc/Response/FileReferencesResponse.php b/lib/Extension/Rpc/Response/FileReferencesResponse.php deleted file mode 100644 index 2b92ac0640..0000000000 --- a/lib/Extension/Rpc/Response/FileReferencesResponse.php +++ /dev/null @@ -1,51 +0,0 @@ - $references - */ - public function __construct(private array $references) - { - } - - public static function fromArray(array $array) - { - $references = []; - foreach ($array as $fileAndReferences) { - $references[] = FileReferences::fromPathAndReferences( - $fileAndReferences['file'], - array_map(function (array $reference) { - return Reference::fromStartEndLineNumberLineAndCol($reference['start'], $reference['end'], $reference['line_no'], $reference['line'] ?? '', $reference['col_no']); - }, $fileAndReferences['references']) - ); - } - - return new self($references); - } - - public function name(): string - { - return 'file_references'; - } - - public function parameters(): array - { - return [ - 'file_references' => array_map(function (FileReferences $fileReferences) { - return $fileReferences->toArray(); - }, $this->references) - ]; - } - - public function references(): array - { - return $this->references; - } -} diff --git a/lib/Extension/Rpc/Response/InformationResponse.php b/lib/Extension/Rpc/Response/InformationResponse.php deleted file mode 100644 index 39013139c8..0000000000 --- a/lib/Extension/Rpc/Response/InformationResponse.php +++ /dev/null @@ -1,34 +0,0 @@ -information; - } - - public function name(): string - { - return 'information'; - } - - public function parameters(): array - { - return [ - 'information' => $this->information, - ]; - } -} diff --git a/lib/Extension/Rpc/Response/Input/ChoiceInput.php b/lib/Extension/Rpc/Response/Input/ChoiceInput.php deleted file mode 100644 index c5267c9de3..0000000000 --- a/lib/Extension/Rpc/Response/Input/ChoiceInput.php +++ /dev/null @@ -1,65 +0,0 @@ -name, $this->label, $this->choices, $this->default, $keyMap); - } - - public function type(): string - { - return 'choice'; - } - - public function name(): string - { - return $this->name; - } - - public function label(): string - { - return $this->label; - } - - public function default(): ?string - { - return $this->default; - } - - public function choices(): array - { - return $this->choices; - } - - public function parameters(): array - { - return [ - 'default' => $this->default, - 'label' => $this->label, - 'choices' => $this->choices, - 'keyMap' => $this->keyMap, - ]; - } -} diff --git a/lib/Extension/Rpc/Response/Input/ConfirmInput.php b/lib/Extension/Rpc/Response/Input/ConfirmInput.php deleted file mode 100644 index 6b7ea6c7cf..0000000000 --- a/lib/Extension/Rpc/Response/Input/ConfirmInput.php +++ /dev/null @@ -1,34 +0,0 @@ -name; - } - - public function parameters(): array - { - return [ - 'label' => $this->label - ]; - } -} diff --git a/lib/Extension/Rpc/Response/Input/Input.php b/lib/Extension/Rpc/Response/Input/Input.php deleted file mode 100644 index 50191be6b8..0000000000 --- a/lib/Extension/Rpc/Response/Input/Input.php +++ /dev/null @@ -1,12 +0,0 @@ -allowMultipleResults = $allowMultipleResults; - - return $new; - } - - public function parameters(): array - { - return array_merge(parent::parameters(), [ - 'multi' => $this->allowMultipleResults, - ]); - } -} diff --git a/lib/Extension/Rpc/Response/Input/TextInput.php b/lib/Extension/Rpc/Response/Input/TextInput.php deleted file mode 100644 index 74a8f35e17..0000000000 --- a/lib/Extension/Rpc/Response/Input/TextInput.php +++ /dev/null @@ -1,48 +0,0 @@ -name; - } - - public function label(): string - { - return $this->label; - } - - public function default(): ?string - { - return $this->default; - } - - public function parameters(): array - { - return [ - 'default' => $this->default, - 'label' => $this->label, - 'type' => $this->type, - ]; - } -} diff --git a/lib/Extension/Rpc/Response/InputCallbackResponse.php b/lib/Extension/Rpc/Response/InputCallbackResponse.php deleted file mode 100644 index a976f65a7c..0000000000 --- a/lib/Extension/Rpc/Response/InputCallbackResponse.php +++ /dev/null @@ -1,63 +0,0 @@ -add($input); - } - } - - public static function fromCallbackAndInputs(Request $callbackAction, array $inputs) - { - return new self($callbackAction, $inputs); - } - - public function name(): string - { - return 'input_callback'; - } - - public function inputs(): array - { - return $this->inputs; - } - - public function parameters(): array - { - return [ - 'inputs' => array_map(function (Input $input) { - return [ - 'name' => $input->name(), - 'type' => $input->type(), - 'parameters' => $input->parameters() - ]; - }, $this->inputs), - 'callback' => [ - 'action' => $this->callbackAction->name(), - 'parameters' => $this->callbackAction->parameters() - ], - ]; - } - - public function callbackAction(): Request - { - return $this->callbackAction; - } - - private function add(Input $input): void - { - $this->inputs[] = $input; - } -} diff --git a/lib/Extension/Rpc/Response/OpenFileResponse.php b/lib/Extension/Rpc/Response/OpenFileResponse.php deleted file mode 100644 index 5521791fd5..0000000000 --- a/lib/Extension/Rpc/Response/OpenFileResponse.php +++ /dev/null @@ -1,86 +0,0 @@ - $this->path, - 'offset' => $this->offset, - 'force_reload' => $this->forceReload, - 'target' => $this->target, - ]; - } - - public function path(): string - { - return $this->path; - } - - public function target(): string - { - return $this->target; - } - - public function withForcedReload(bool $bool): OpenFileResponse - { - $new = clone $this; - $new->forceReload = $bool; - - return $new; - } - - public function withTarget(string $target): OpenFileResponse - { - if (!in_array($target, self::VALID_TARGETS)) { - throw new RuntimeException(sprintf( - 'Unknown target "%s", known targets "%s"', - $target, - implode('", "', self::VALID_TARGETS) - )); - } - $new = clone $this; - $new->target = $target; - - return $new; - } -} diff --git a/lib/Extension/Rpc/Response/Reference/FileReferences.php b/lib/Extension/Rpc/Response/Reference/FileReferences.php deleted file mode 100644 index 4cfb00d384..0000000000 --- a/lib/Extension/Rpc/Response/Reference/FileReferences.php +++ /dev/null @@ -1,37 +0,0 @@ -addReference($reference); - } - } - - public static function fromPathAndReferences($filePath, array $references) - { - return new self($filePath, $references); - } - - public function toArray() - { - return [ - 'file' => $this->filePath, - 'references' => array_map(function (Reference $reference) { - return $reference->toArray(); - }, $this->references) - ]; - } - - private function addReference(Reference $reference): void - { - $this->references[] = $reference; - } -} diff --git a/lib/Extension/Rpc/Response/Reference/Reference.php b/lib/Extension/Rpc/Response/Reference/Reference.php deleted file mode 100644 index d747fab765..0000000000 --- a/lib/Extension/Rpc/Response/Reference/Reference.php +++ /dev/null @@ -1,31 +0,0 @@ - $this->start, - 'end' => $this->end, - 'line' => $this->line, - 'line_no' => $this->lineNumber, - 'col_no' => $this->colNo - ]; - } -} diff --git a/lib/Extension/Rpc/Response/ReplaceFileSourceResponse.php b/lib/Extension/Rpc/Response/ReplaceFileSourceResponse.php deleted file mode 100644 index b1fb653f58..0000000000 --- a/lib/Extension/Rpc/Response/ReplaceFileSourceResponse.php +++ /dev/null @@ -1,37 +0,0 @@ - $this->path, - 'source' => $this->replacementSource, - ]; - } - - public function path(): string - { - return $this->path; - } -} diff --git a/lib/Extension/Rpc/Response/ReturnChoiceResponse.php b/lib/Extension/Rpc/Response/ReturnChoiceResponse.php deleted file mode 100644 index 0e4e503b73..0000000000 --- a/lib/Extension/Rpc/Response/ReturnChoiceResponse.php +++ /dev/null @@ -1,57 +0,0 @@ -add($option); - } - } - - public function name(): string - { - return 'return_choice'; - } - - public function parameters(): array - { - $options = []; - foreach ($this->options as $option) { - $options[] = [ - 'name' => $option->name(), - 'value' => $option->value(), - ]; - } - - return [ - 'choices' => $options - ]; - } - - public static function fromOptions(array $options): ReturnChoiceResponse - { - return new self($options); - } - - public function options() - { - return $this->options; - } - - private function add(ReturnOption $option): void - { - $this->options[] = $option; - } -} diff --git a/lib/Extension/Rpc/Response/ReturnOption.php b/lib/Extension/Rpc/Response/ReturnOption.php deleted file mode 100644 index 8276d695af..0000000000 --- a/lib/Extension/Rpc/Response/ReturnOption.php +++ /dev/null @@ -1,27 +0,0 @@ -name; - } - - public function value() - { - return $this->value; - } -} diff --git a/lib/Extension/Rpc/Response/ReturnResponse.php b/lib/Extension/Rpc/Response/ReturnResponse.php deleted file mode 100644 index c8976c2e62..0000000000 --- a/lib/Extension/Rpc/Response/ReturnResponse.php +++ /dev/null @@ -1,39 +0,0 @@ - $this->value - ]; - } - - public static function fromValue($value): ReturnResponse - { - return new self($value); - } - - public function value() - { - return $this->value; - } -} diff --git a/lib/Extension/Rpc/Response/UpdateFileSourceResponse.php b/lib/Extension/Rpc/Response/UpdateFileSourceResponse.php deleted file mode 100644 index 3d910b4833..0000000000 --- a/lib/Extension/Rpc/Response/UpdateFileSourceResponse.php +++ /dev/null @@ -1,54 +0,0 @@ -textEditBuilder = new TextEditBuilder(); - } - - public static function fromPathOldAndNewSource(string $path, string $oldSource, string $newSource) - { - return new self($path, $oldSource, $newSource); - } - - public function name(): string - { - return 'update_file_source'; - } - - public function parameters(): array - { - return [ - 'path' => $this->path, - 'source' => $this->newSource, - 'edits' => $this->textEditBuilder->calculateTextEdits($this->oldSource, $this->newSource), - ]; - } - - public function path(): string - { - return $this->path; - } - - public function oldSource(): string - { - return $this->oldSource; - } - - public function newSource(): string - { - return $this->newSource; - } -} diff --git a/lib/Extension/Rpc/RpcCommandDocumentor.php b/lib/Extension/Rpc/RpcCommandDocumentor.php deleted file mode 100644 index f868926ad5..0000000000 --- a/lib/Extension/Rpc/RpcCommandDocumentor.php +++ /dev/null @@ -1,79 +0,0 @@ -handlerRegistry->all() as $serviceId => $handler) { - $documentation = $this->documentHandler($serviceId, $handler); - if (null === $documentation) { - continue; - } - $docs[] = $documentation; - } - return implode("\n", $docs); - } - - private function documentHandler(string $serviceId, Handler $handler): ?string - { - $handlerClass = get_class($handler); - $parts = explode('\\', $handlerClass); - $documentedName = '_RpcHandler_'.$serviceId; - - /** @phpstan-ignore-next-line */ - if (false === $documentedName) { - throw new RuntimeException(sprintf( - 'Invalid extension class name "%s"', - $handlerClass - )); - } - - $help = [ - '.. ' . $documentedName . ':', - "\n", - $documentedName, - str_repeat('-', mb_strlen($documentedName)), - "\n", - ]; - - $resolver = new Resolver(); - $handler->configure($resolver); - - $hasDocumentation = false; - foreach ($resolver->definitions() as $definition) { - $help[] = $this->definitionDocumentor->document('RpcCommand_'.$handler->name(), $definition); - $hasDocumentation = true; - } - - if (!$hasDocumentation) { - return null; - } - - return implode("\n", $help); - } -} diff --git a/lib/Extension/Rpc/RpcExtension.php b/lib/Extension/Rpc/RpcExtension.php deleted file mode 100644 index 6c913364d0..0000000000 --- a/lib/Extension/Rpc/RpcExtension.php +++ /dev/null @@ -1,97 +0,0 @@ -register('rpc.command.rpc', function (Container $container) { - return new RpcCommand( - $container->get('rpc.request_handler'), - $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER)->resolve($container->getParameter('rpc.replay_path')), - $container->getParameter('rpc.store_replay') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'rpc' ] ]); - - $container->register(self::SERVICE_REQUEST_HANDLER, function (Container $container) { - return new LoggingHandler( - new ExceptionCatchingHandler( - new RequestHandler($container->get('rpc.handler_registry')) - ), - LoggingExtension::channelLogger($container, 'rpc'), - ); - }); - - $container->register('rpc.handler_registry', function (Container $container) { - $handlers = []; - foreach ($container->getServiceIdsForTag(self::TAG_RPC_HANDLER) as $serviceId => $attrs) { - if (!isset($attrs['name'])) { - throw new RuntimeException(sprintf( - 'Handler "%s" must be provided with a "name" ' . - 'attribute when it is registered', - $serviceId - )); - } - - $handlers[$attrs['name']] = $serviceId; - } - - return new LazyContainerHandlerRegistry($container, $handlers); - }); - - $container->register(RpcCommandDocumentor::class, function ($container) { - return new RpcCommandDocumentor( - $container->get('rpc.handler_registry'), - $container->get(DefinitionDocumentor::class) - ); - }, [ DebugExtension::TAG_DOCUMENTOR => [ - 'name' => self::RPC_DOCUMENTOR_NAME - ] ]); - - $this->registerHandlers($container); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::STORE_REPLAY => false, - self::REPLAY_PATH => '%cache%/replay.json', - ]); - $schema->setDescriptions([ - self::STORE_REPLAY => 'Should replays be stored?', - self::REPLAY_PATH => 'Path where the replays should be stored', - ]); - } - - private function registerHandlers(ContainerBuilder $container): void - { - $container->register('rpc.handler.echo', function (Container $container) { - return new EchoHandler(); - }, [ self::TAG_RPC_HANDLER => [ 'name' => 'echo' ] ]); - } -} diff --git a/lib/Extension/Rpc/RpcVersion.php b/lib/Extension/Rpc/RpcVersion.php deleted file mode 100644 index 1c97f74b0a..0000000000 --- a/lib/Extension/Rpc/RpcVersion.php +++ /dev/null @@ -1,19 +0,0 @@ -handler - ]); - $requestHandler = new RequestHandler($registry); - $request = Request::fromNameAndParameters($actionName, $parameters); - - return $requestHandler->handle($request); - } -} diff --git a/lib/Extension/Rpc/Tests/Integration/Command/RpcCommandTest.php b/lib/Extension/Rpc/Tests/Integration/Command/RpcCommandTest.php deleted file mode 100644 index 22cf8ab424..0000000000 --- a/lib/Extension/Rpc/Tests/Integration/Command/RpcCommandTest.php +++ /dev/null @@ -1,120 +0,0 @@ -workspace = Workspace::create(__DIR__ . '/../../Workspace'); - $this->workspace->reset(); - } - - /** - * It should execute a command from stdin - */ - public function testReadsFromStdin(): void - { - $stdin = json_encode([ - 'action' => 'echo', - 'parameters' => [ - 'message' => 'Hello World', - ], - ]); - - $tester = $this->execute((string)$stdin); - $this->assertEquals(0, $tester->getStatusCode()); - $response = json_decode($tester->getDisplay(), true); - - $this->assertEquals([ - 'action' => 'echo', - 'parameters' => [ - 'message' => 'Hello World', - ], - 'version' => RpcVersion::asString(), - ], $response); - } - - public function testPrettyPrintsOutput(): void - { - $stdin = json_encode([ - 'action' => 'echo', - 'parameters' => [ - 'message' => 'Hello World', - ], - ]); - - $tester = $this->execute((string)$stdin, [ '--pretty' => true ]); - $this->assertEquals(0, $tester->getStatusCode()); - } - - public function testReplaysLastRequest(): void - { - $randomString = md5((string)rand(0, 100000)); - $stdin = json_encode([ - 'action' => 'echo', - 'parameters' => [ - 'message' => $randomString, - ], - ]); - - $tester = $this->execute((string)$stdin); - $this->assertEquals(0, $tester->getStatusCode()); - - $tester = $this->execute('', [ '--replay' => true ]); - $this->assertEquals(0, $tester->getStatusCode()); - $response = json_decode($tester->getDisplay(), true); - - $this->assertEquals([ - 'action' => 'echo', - 'parameters' => [ - 'message' => $randomString, - ], - 'version' => RpcVersion::asString(), - ], $response); - } - - private function execute(string $stdin, array $input = []): CommandTester - { - $container = PhpactorContainer::fromExtensions([ - LoggingExtension::class, - RpcExtension::class - ], []); - - $stream = fopen('php://temp', 'r+'); - if (false === $stream) { - throw new RuntimeException('Could not open stream'); - } - fwrite($stream, $stdin); - rewind($stream); - $tester = new CommandTester( - new RpcCommand( - $container->get('rpc.request_handler'), - $this->workspace()->path('/replay.json'), - true, - $stream - ) - ); - $tester->execute($input); - fclose($stream); - - return $tester; - } - - private function workspace(): Workspace - { - return $this->workspace; - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/Diff/TextEditBuilderTest.php b/lib/Extension/Rpc/Tests/Unit/Diff/TextEditBuilderTest.php deleted file mode 100644 index ef7863844a..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/Diff/TextEditBuilderTest.php +++ /dev/null @@ -1,118 +0,0 @@ -calculateTextEdits($one, $two); - $this->assertEquals($expected, $chunks); - } - - /** - * @return Generator>}> - */ - public static function provideDiff(): Generator - { - yield 'no edits' => [ - <<<'EOT' - original - original - original - EOT - , - <<<'EOT' - original - original - original - EOT - , - [ ] - ]; - - yield 'addition at start of file' => [ - <<<'EOT' - original - original - original - EOT - , - <<<'EOT' - new - original - original - original - EOT - , - [ - [ - 'start' => [ 'line' => 0, 'character' => 0 ], - 'end' => [ 'line' => 0, 'character' => 0 ], - 'text' => 'new' . "\n", - ], - ], - ]; - - yield 'first line changed' => [ - <<<'EOT' - original - original - original - EOT - , - <<<'EOT' - neworiginal - original - original - EOT - , - [ - [ - 'start' => [ 'line' => 0, 'character' => 0 ], - 'end' => [ 'line' => 1, 'character' => 0 ], - 'text' => '', - ], - [ - 'start' => [ 'line' => 0, 'character' => 0 ], - 'end' => [ 'line' => 0, 'character' => 0 ], - 'text' => 'neworiginal' . "\n", - ], - ], - ]; - - yield 'last line changed' => [ - <<<'EOT' - original - original - middle - EOT - , - <<<'EOT' - original - original - original - EOT - , - [ - [ - 'start' => [ 'line' => 2, 'character' => 0 ], - 'end' => [ 'line' => 3, 'character' => 0 ], - 'text' => '', - ], - [ - 'start' => [ 'line' => 2, 'character' => 0 ], - 'end' => [ 'line' => 2, 'character' => 0 ], - 'text' => 'original', - ], - ], - ]; - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/Editor/ReturnChoiceActionTest.php b/lib/Extension/Rpc/Tests/Unit/Editor/ReturnChoiceActionTest.php deleted file mode 100644 index f0b0b0d2b7..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/Editor/ReturnChoiceActionTest.php +++ /dev/null @@ -1,29 +0,0 @@ -assertEquals([ - 'choices' => [ - [ - 'name' => 'one', - 'value' => 1000, - ], - ], - ], $returnChoice->parameters()); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/Editor/StackActionTest.php b/lib/Extension/Rpc/Tests/Unit/Editor/StackActionTest.php deleted file mode 100644 index 86450e015a..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/Editor/StackActionTest.php +++ /dev/null @@ -1,42 +0,0 @@ -prophesize(Response::class); - $action2 = $this->prophesize(Response::class); - - $action1->name()->willReturn('a1'); - $action2->name()->willReturn('a2'); - $action1->parameters()->willReturn([ 'p1' => 'v1' ]); - $action2->parameters()->willReturn([ 'p2' => 'v2' ]); - - - $action = CollectionResponse::fromActions([ - $action1->reveal(), $action2->reveal() - ]); - - $this->assertEquals([ - 'actions' => [ - [ - 'name' => 'a1', - 'parameters' => [ 'p1' => 'v1' ], - ], - [ - 'name' => 'a2', - 'parameters' => [ 'p2' => 'v2' ], - ], - ] - ], $action->parameters()); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/Registry/ActiveHandlerRegistryTest.php b/lib/Extension/Rpc/Tests/Unit/Registry/ActiveHandlerRegistryTest.php deleted file mode 100644 index 38ce2721b4..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/Registry/ActiveHandlerRegistryTest.php +++ /dev/null @@ -1,36 +0,0 @@ -expectException(HandlerNotFound::class); - $this->expectExceptionMessage('No handler "aaa"'); - - $action = new EchoHandler(); - $registry = $this->create([ $action ]); - - $registry->get('aaa'); - } - - public function testGetAction(): void - { - $action = new EchoHandler(); - $registry = $this->create([ $action ]); - - $this->assertSame($action, $registry->get('echo')); - } - - public function create(array $actions = []) - { - return new ActiveHandlerRegistry($actions); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/RequestHandler/ExceptionCatchingHandlerTest.php b/lib/Extension/Rpc/Tests/Unit/RequestHandler/ExceptionCatchingHandlerTest.php deleted file mode 100644 index 03d6c6f9d3..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/RequestHandler/ExceptionCatchingHandlerTest.php +++ /dev/null @@ -1,61 +0,0 @@ - */ - private ObjectProphecy $innerHandler; - - private ExceptionCatchingHandler $exceptionHandler; - - /** @var ObjectProphecy */ - private ObjectProphecy $response; - - /** @var ObjectProphecy */ - private ObjectProphecy $request; - - public function setUp(): void - { - $this->innerHandler = $this->prophesize(RequestHandler::class); - $this->exceptionHandler = new ExceptionCatchingHandler($this->innerHandler->reveal()); - $this->request = $this->prophesize(Request::class); - $this->response = $this->prophesize(Response::class); - } - - public function testDelegate(): void - { - $this->innerHandler->handle($this->request->reveal())->willReturn($this->response->reveal()); - - $response = $this->exceptionHandler->handle($this->request->reveal()); - - $this->assertSame( - $this->response->reveal(), - $response - ); - } - - public function testCatchExceptions(): void - { - $this->innerHandler->handle( - $this->request->reveal() - )->willThrow(new Exception('Test!')); - - $response = $this->exceptionHandler->handle($this->request->reveal()); - - $this->assertInstanceOf(ErrorResponse::class, $response); - $this->assertEquals('Test!', $response->message()); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/RequestHandler/LoggingHandlerTest.php b/lib/Extension/Rpc/Tests/Unit/RequestHandler/LoggingHandlerTest.php deleted file mode 100644 index 19a4593313..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/RequestHandler/LoggingHandlerTest.php +++ /dev/null @@ -1,93 +0,0 @@ - 'req-name', - 'parameters' => [ 'p1' => 'v1' ] - ]; - private const EXPECTED_RESPONSE_DATA = [ - 'action' => 'res-name', - 'parameters' => [ 'p1' => 'v1' ] - ]; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $innerHandler; - - private LoggingHandler $loggingHandler; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $response; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $request; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $logger; - - public function setUp(): void - { - $this->logger = $this->prophesize(LoggerInterface::class); - $this->innerHandler = $this->prophesize(RequestHandler::class); - - $this->loggingHandler = new LoggingHandler($this->innerHandler->reveal(), $this->logger->reveal()); - - $this->response = $this->prophesize(Response::class); - $this->request = $this->prophesize(Request::class); - $this->request->name()->willReturn('req-name'); - $this->request->parameters()->willReturn(['p1' => 'v1' ]); - $this->response->name()->willReturn('res-name'); - $this->response->parameters()->willReturn(['p1' => 'v1' ]); - } - - public function testLogging(): void - { - $this->innerHandler->handle($this->request->reveal())->willReturn($this->response->reveal()); - - $response = $this->loggingHandler->handle($this->request->reveal()); - - $this->assertSame( - $this->response->reveal(), - $response - ); - - $this->logger->debug('REQUEST', self::EXPECTED_REQUEST_DATA)->shouldHaveBeenCalled(); - $this->logger->log(LogLevel::DEBUG, 'RESPONSE', self::EXPECTED_RESPONSE_DATA)->shouldHaveBeenCalled(); - } - - public function testLoggingWithError(): void - { - $response = ErrorResponse::fromMessageAndDetails('foobar', 'barfoo'); - $expected = [ - 'action' => $response->name(), - 'parameters' => $response->parameters() - ]; - - $this->innerHandler->handle($this->request->reveal())->willReturn($response); - - $response = $this->loggingHandler->handle($this->request->reveal()); - $this->logger->log(LogLevel::ERROR, 'RESPONSE', $expected)->shouldHaveBeenCalled(); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/RequestHandlerTest.php b/lib/Extension/Rpc/Tests/Unit/RequestHandlerTest.php deleted file mode 100644 index 9fa0098249..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/RequestHandlerTest.php +++ /dev/null @@ -1,58 +0,0 @@ -handlerRegistry = $this->prophesize(HandlerRegistry::class); - $this->handler = $this->prophesize(Handler::class); - - $this->requestHandler = new RequestHandler( - $this->handlerRegistry->reveal() - ); - } - - public function testHandle(): void - { - $expectedResponse = $this->prophesize(Response::class); - - $this->handlerRegistry->get('aaa')->willReturn($this->handler->reveal()); - $this->handler->configure(Argument::type(Resolver::class))->will(function ($args): void { - $args[0]->setDefaults([ - 'one' => null, - ]); - }); - ; - - $request = Request::fromNameAndParameters('aaa', [ - 'one' => 'bar', - ]); - - $this->handler->handle(['one' => 'bar'])->willReturn($expectedResponse->reveal()); - - $response = $this->requestHandler->handle($request); - - $this->assertEquals($expectedResponse->reveal(), $response); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/RequestTest.php b/lib/Extension/Rpc/Tests/Unit/RequestTest.php deleted file mode 100644 index bf76b9e2c6..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/RequestTest.php +++ /dev/null @@ -1,41 +0,0 @@ -expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Missing "action" key'); - Request::fromArray([]); - } - - public function testCanBeCreatedFromArrayParametersAreOptional(): void - { - $request = Request::fromArray([ - 'action' => 'foobar', - ]); - $result = $request->toArray(); - - $this->assertEquals([ - 'action' => 'foobar', - 'parameters' => [] - ], $result); - } - - public function testThrowsExceptionIfInvalidKeysGiven(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid request keys "foobar"'); - - Request::fromArray([ - 'action' => 'fo', - 'foobar' => 'foobar', - ]); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/Response/ErrorResponseTest.php b/lib/Extension/Rpc/Tests/Unit/Response/ErrorResponseTest.php deleted file mode 100644 index 1d147eda30..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/Response/ErrorResponseTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertEquals('Hello', $response->message()); - } - - public function testFromExceptionWithPrevious(): void - { - $exception1 = new Exception('One'); - $exception2 = new Exception('Two', 0, $exception1); - $exception3 = new Exception('Three', 0, $exception2); - $response = ErrorResponse::fromException($exception3); - - $this->assertEquals('Three', $response->message()); - $this->assertStringContainsString('One', $response->details()); - $this->assertStringContainsString('Two', $response->details()); - $this->assertStringContainsString('Three', $response->details()); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/Response/Input/ChoiceInputTest.php b/lib/Extension/Rpc/Tests/Unit/Response/Input/ChoiceInputTest.php deleted file mode 100644 index 58faa384d2..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/Response/Input/ChoiceInputTest.php +++ /dev/null @@ -1,32 +0,0 @@ -withKeys([ - 'one' => 'o', - 'two' => 't', - ]); - self::assertEquals([ - 'label' => 'foobar', - 'choices' => [ - 0 => 'one', - 1 => 'two', - ], - 'default' => null, - 'keyMap' => [ - 'one' => 'o', - 'two' => 't', - ], - ], $choice->parameters()); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/Response/OpenFileResponseTest.php b/lib/Extension/Rpc/Tests/Unit/Response/OpenFileResponseTest.php deleted file mode 100644 index 805fb1bd51..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/Response/OpenFileResponseTest.php +++ /dev/null @@ -1,44 +0,0 @@ -assertEquals(OpenFileResponse::TARGET_FOCUSED_WINDOW, $response->target()); - } - - public function testExceptionOnInvalidTarget(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Unknown target "nope"'); - OpenFileResponse::fromPath(__FILE__) - ->withTarget('nope'); - } - - public function testReturnsConfiguredTarget(): void - { - $response = OpenFileResponse::fromPath(__FILE__) - ->withTarget(OpenFileResponse::TARGET_HORIZONTAL_SPLIT); - $this->assertEquals(OpenFileResponse::TARGET_HORIZONTAL_SPLIT, $response->target()); - } - - public function testReturnsParameters(): void - { - $response = OpenFileResponse::fromPathAndOffset(__FILE__, 6) - ->withTarget(OpenFileResponse::TARGET_HORIZONTAL_SPLIT) - ->withForcedReload(true); - $this->assertEquals([ - 'path' => __FILE__, - 'offset' => 6, - 'force_reload' => true, - 'target' => OpenFileResponse::TARGET_HORIZONTAL_SPLIT - ], $response->parameters()); - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/RpcExtensionTest.php b/lib/Extension/Rpc/Tests/Unit/RpcExtensionTest.php deleted file mode 100644 index cdbcfa80e2..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/RpcExtensionTest.php +++ /dev/null @@ -1,51 +0,0 @@ -createContainer(); - $loader = $container->get(ConsoleExtension::SERVICE_COMMAND_LOADER); - $this->assertInstanceOf(RpcCommand::class, $loader->get('rpc')); - } - - public function testHandler(): void - { - $container = $this->createContainer(); - $handler = $this->getHandler($container); - $response = $handler->handle(Request::fromNameAndParameters('echo', [ - 'message' => 'world', - ])); - $this->assertInstanceOf(Response::class, $response); - } - - private function getHandler(Container $container): RequestHandler - { - return $container->get(RpcExtension::SERVICE_REQUEST_HANDLER); - } - - private function createContainer(): Container - { - $container = PhpactorContainer::fromExtensions([ - LoggingExtension::class, - RpcExtension::class, - ConsoleExtension::class, - FilePathResolverExtension::class, - ], []); - return $container; - } -} diff --git a/lib/Extension/Rpc/Tests/Unit/Test/HandlerTesterTest.php b/lib/Extension/Rpc/Tests/Unit/Test/HandlerTesterTest.php deleted file mode 100644 index b809d38657..0000000000 --- a/lib/Extension/Rpc/Tests/Unit/Test/HandlerTesterTest.php +++ /dev/null @@ -1,34 +0,0 @@ -handler = new EchoHandler(); - } - - public function testTester(): void - { - $tester = new HandlerTester($this->handler); - - $response = $tester->handle('echo', [ 'message' => 'bar' ]); - $this->assertInstanceOf(EchoResponse::class, $response); - $this->assertEquals('bar', $response->message()); - } -} diff --git a/lib/Extension/SourceCodeFilesystem/SourceCodeFilesystemExtension.php b/lib/Extension/SourceCodeFilesystem/SourceCodeFilesystemExtension.php deleted file mode 100644 index ffb4ce7160..0000000000 --- a/lib/Extension/SourceCodeFilesystem/SourceCodeFilesystemExtension.php +++ /dev/null @@ -1,100 +0,0 @@ -setDefaults([ - self::PARAM_PROJECT_ROOT => '%project_root%', - ]); - } - - public function load(ContainerBuilder $container): void - { - $this->registerFilesystems($container); - } - - private function registerFilesystems(ContainerBuilder $container): void - { - $container->register(self::SERVICE_REGISTRY, function (Container $container) { - $filesystems = []; - /** @var array $fileSystemsByTag */ - $fileSystemsByTag = $container->getServiceIdsForTag('source_code_filesystem.filesystem'); - foreach ($fileSystemsByTag as $serviceId => $attributes) { - try { - /** @var Filesystem $filesystem */ - $filesystem =$container->get($serviceId); - $filesystems[$attributes['name']] = $filesystem; - } catch (NotSupported $exception) { - LoggingExtension::channelLogger($container, 'scf')->warning(sprintf( - 'Filesystem "%s" not supported: "%s"', - $attributes['name'], - $exception->getMessage() - )); - } - } - - return new FallbackFilesystemRegistry( - new MappedFilesystemRegistry($filesystems), - 'simple' - ); - }); - $container->register(self::SERVICE_FILESYSTEM_GIT, function (Container $container) { - return new GitFilesystem(FilePath::fromString($this->projectRoot($container))); - }, [ 'source_code_filesystem.filesystem' => [ 'name' => self::FILESYSTEM_GIT ]]); - - $container->register(self::SERVICE_FILESYSTEM_SIMPLE, function (Container $container) { - return new SimpleFilesystem(FilePath::fromString($this->projectRoot($container))); - }, [ 'source_code_filesystem.filesystem' => ['name' => self::FILESYSTEM_SIMPLE]]); - - $container->register(self::SERVICE_FILESYSTEM_COMPOSER, function (Container $container) { - $providers = []; - $cwd = FilePath::fromString($this->projectRoot($container)); - $classLoaders = $container->get(ComposerAutoloaderExtension::SERVICE_AUTOLOADERS); - - if (!$classLoaders) { - throw new NotSupported('No composer class loaders found/configured'); - } - - foreach ($classLoaders as $classLoader) { - $providers[] = new ComposerFileListProvider($cwd, $classLoader); - } - - return new SimpleFilesystem($cwd, new ChainFileListProvider($providers)); - }, [ 'source_code_filesystem.filesystem' => [ 'name' => self::FILESYSTEM_COMPOSER ]]); - } - - private function projectRoot(Container $container): string - { - return $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER)->resolve($container->parameter(self::PARAM_PROJECT_ROOT)->string()); - } -} diff --git a/lib/Extension/SourceCodeFilesystem/Tests/Unit/SourceCodeFilesystemExtensionTest.php b/lib/Extension/SourceCodeFilesystem/Tests/Unit/SourceCodeFilesystemExtensionTest.php deleted file mode 100644 index b33e608cab..0000000000 --- a/lib/Extension/SourceCodeFilesystem/Tests/Unit/SourceCodeFilesystemExtensionTest.php +++ /dev/null @@ -1,57 +0,0 @@ -createRegistry([ - ComposerAutoloaderExtension::PARAM_AUTOLOADER_PATH => __DIR__ . '/../../vendor/autoload.php', - ]); - $this->assertInstanceOf($expectedClass, $registry->get($filesystem)); - } - - public static function provideFilesystems() - { - // disable this as travis does not have git when tested via. the - // Phpactor test suite (this is there installed as a dependency). - // yield [ 'git', GitFilesystem::class ]; - yield [ 'simple', SimpleFilesystem::class ]; - yield [ 'composer', SimpleFilesystem::class ]; - } - - public function testComposerNotSupported(): void - { - $registry = $this->createRegistry([ - SourceCodeFilesystemExtension::PARAM_PROJECT_ROOT => __DIR__, - ComposerAutoloaderExtension::PARAM_AUTOLOADER_PATH => __DIR__ . '/no-autoload.php', - ]); - $composer = $registry->get('composer'); - $this->assertInstanceOf(SimpleFilesystem::class, $composer); - } - - public function createRegistry(array $config): FilesystemRegistry - { - $container = PhpactorContainer::fromExtensions([ - SourceCodeFilesystemExtension::class, - ComposerAutoloaderExtension::class, - LoggingExtension::class, - FilePathResolverExtension::class, - ], $config); - - return $container->get(SourceCodeFilesystemExtension::SERVICE_REGISTRY); - } -} diff --git a/lib/Extension/SourceCodeFilesystemExtra/Command/ClassSearchCommand.php b/lib/Extension/SourceCodeFilesystemExtra/Command/ClassSearchCommand.php deleted file mode 100644 index 2b6e44b8f3..0000000000 --- a/lib/Extension/SourceCodeFilesystemExtra/Command/ClassSearchCommand.php +++ /dev/null @@ -1,44 +0,0 @@ -setDescription('Search for class by (short) name and return informations on candidates'); - $this->addArgument('name', InputArgument::REQUIRED, 'Source path or FQN'); - FormatHandler::configure($this); - FilesystemHandler::configure($this, SourceCodeFilesystemExtension::FILESYSTEM_COMPOSER); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $results = $this->search->classSearch( - $input->getOption('filesystem'), - $input->getArgument('name') - ); - - $dumper = $this->dumperRegistry->get($input->getOption('format')); - $dumper->dump($output, $results); - - return 0; - } -} diff --git a/lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php b/lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php deleted file mode 100644 index 36a0fe1a56..0000000000 --- a/lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php +++ /dev/null @@ -1,63 +0,0 @@ -setRequired([ - self::SHORT_NAME, - ]); - } - - public function handle(array $arguments) - { - $results = $this->classSearch->classSearch( - $this->defaultFilesystem, - $arguments[self::SHORT_NAME] - ); - - if (count($results) === 0) { - return EchoResponse::fromMessage(sprintf('No classes found with short name "%s"', $arguments[self::SHORT_NAME])); - } - - if (count($results) === 1) { - $result = reset($results); - return ReturnResponse::fromValue($result); - } - - $options = []; - foreach ($results as $result) { - $options[] = ReturnOption::fromNameAndValue( - $result['class'], - $result - ); - } - - return ReturnChoiceResponse::fromOptions($options); - } -} diff --git a/lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php b/lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php deleted file mode 100644 index 6e010e7d99..0000000000 --- a/lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php +++ /dev/null @@ -1,116 +0,0 @@ -convertFqnToRelativePath($name); - $filesystem = $this->filesystemRegistry->get($filesystemName); - - /** @var FileList $files */ - $files = $filesystem->fileList('{' . $name . '}')->named($name . '.php'); - - $results = []; - $results = $this->builtInResults($results, $name); - - foreach ($files as $file) { - if (isset($results[(string) $file->path()])) { - continue; - } - - $result = [ - 'file_path' => (string) $file->path(), - 'class' => null, - 'class_name' => null, - 'class_namespace' => null, - ]; - - $candidates = $this->fileToClass->fileToClassCandidates(FilePath::fromString((string) $file->path())); - - if (false === $candidates->noneFound()) { - $result['class_name'] = (string) $candidates->best()->name(); - $result['class'] = (string) $candidates->best(); - $result['class_namespace'] = (string) $candidates->best()->namespace(); - } - - $results[(string) $file->path()] = $result; - } - - return array_values($results); - } - - private function tryAndReflect(string $name) - { - try { - $reflectionClass = $this->reflector->reflectClassLike($name); - } catch (NotFound) { - return; - } - - return [ - 'file_path' => (string) $reflectionClass->sourceCode()->uri()?->path(), - 'class' => (string) $reflectionClass->name(), - 'class_name' => $reflectionClass->name()->short(), - 'class_namespace' => (string) $reflectionClass->name()->namespace(), - ]; - } - - private function builtInResults(array $results, string $name) - { - $declared = array_merge( - get_declared_classes(), - get_declared_traits(), - get_declared_interfaces() - ); - - foreach ($declared as $declaredClass) { - $short = $this->resolveShortName($declaredClass); - - $namespace = substr($declaredClass, 0, intval(strrpos($declaredClass, '\\'))); - - if ($name !== $short) { - continue; - } - - if (!$this->tryAndReflect($name)) { - continue; - } - - $results[] = $this->tryAndReflect($name); - } - - return $results; - } - - private function resolveShortName($declaredClass): string - { - $offset = strrpos($declaredClass, '\\'); - - if (false === $offset) { - return $declaredClass; - } - - return substr($declaredClass, $offset + 1); - } - - private function convertFqnToRelativePath(string $name) - { - return str_replace('\\', '/', $name); - } -} diff --git a/lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php b/lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php deleted file mode 100644 index 80b66dc094..0000000000 --- a/lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php +++ /dev/null @@ -1,60 +0,0 @@ -registerCommands($container); - $this->registerApplicationServices($container); - $this->registerRpc($container); - } - - private function registerCommands(ContainerBuilder $container): void - { - $container->register('command.class_search', function (Container $container) { - return new ClassSearchCommand( - $container->get('application.class_search'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'class:search' ]]); - } - - private function registerApplicationServices(ContainerBuilder $container): void - { - $container->register('application.class_search', function (Container $container) { - return new ClassSearch( - $container->get('source_code_filesystem.registry'), - $container->get('class_to_file.converter'), - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR) - ); - }); - } - - private function registerRpc(ContainerBuilder $container): void - { - $container->register('source_code_filesystem.rpc.handler.class_search', function (Container $container) { - return new ClassSearchHandler( - $container->get('application.class_search'), - SourceCodeFilesystemExtension::FILESYSTEM_COMPOSER - ); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => ClassSearchHandler::NAME] ]); - } -} diff --git a/lib/Extension/Symfony/Adapter/Symfony/XmlSymfonyContainerInspector.php b/lib/Extension/Symfony/Adapter/Symfony/XmlSymfonyContainerInspector.php deleted file mode 100644 index 3dbbb1fd94..0000000000 --- a/lib/Extension/Symfony/Adapter/Symfony/XmlSymfonyContainerInspector.php +++ /dev/null @@ -1,137 +0,0 @@ -loadXPath(); - - if (null === $dom) { - return []; - } - - $services = []; - $serviceEls = $dom->query('//symfony:service'); - if (false === $serviceEls) { - return []; - } - foreach ($serviceEls as $serviceEl) { - $service = $this->serviceFromEl($serviceEl); - if (null === $service) { - continue; - } - $services[] = $service; - } - - return $services; - } - - public function parameters(): array - { - $dom = $this->loadXPath(); - - if (null === $dom) { - return []; - } - - $parameters = []; - $parameterEls = $dom->query('//symfony:parameter'); - if (false === $parameterEls) { - return []; - } - foreach ($parameterEls as $parameterEl) { - if (!$parameterEl instanceof DOMElement) { - continue; - } - $key = $parameterEl->getAttribute('key'); - $value = $parameterEl->nodeValue; - if (empty($key) || !is_string($value)) { - continue; - } - $parameters[] = new SymfonyContainerParameter( - $key, - TypeFactory::fromValue($value), - ); - } - - return $parameters; - } - - public function service(string $id): ?SymfonyContainerService - { - $xpath = $this->loadXPath(); - if (null === $xpath) { - return null; - } - $list = $xpath->query(sprintf("//symfony:service[@id='%s']", $id)); - if ($list === false) { - return null; - } - foreach ($list as $serviceEl) { - return $this->serviceFromEl($serviceEl); - } - return null; - } - - private function loadXPath(): ?DOMXPath - { - if (!file_exists($this->xmlPath)) { - return null; - } - if (null !== $this->cache) { - clearstatcache(); - $latest = filemtime($this->xmlPath) ?: 0; - if ($this->mtime === $latest) { - return $this->cache; - } - } - $dom = new DOMDocument(); - $dom->load($this->xmlPath); - $xpath = new DOMXPath($dom); - $xpath->registerNamespace('symfony', 'http://symfony.com/schema/dic/services'); - $this->cache = $xpath; - $this->mtime = filemtime($this->xmlPath) ?: 0; - return $xpath; - } - - private function serviceFromEl(DOMNode $serviceEl): ?SymfonyContainerService - { - if (!$serviceEl instanceof DOMElement) { - return null; - } - $id = $serviceEl->getAttribute('id'); - $class = $serviceEl->getAttribute('class'); - $public = $serviceEl->getAttribute('public'); - if (true === $this->publicOnly && 'true' !== $public) { - return null; - } - if (empty($id) || empty($class)) { - return null; - } - return new SymfonyContainerService( - $id, - TypeFactory::fromString($class), - ); - } -} diff --git a/lib/Extension/Symfony/Completor/SymfonyContainerCompletor.php b/lib/Extension/Symfony/Completor/SymfonyContainerCompletor.php deleted file mode 100644 index 72fe5bb40c..0000000000 --- a/lib/Extension/Symfony/Completor/SymfonyContainerCompletor.php +++ /dev/null @@ -1,101 +0,0 @@ -parent->parent) { - $inQuote = true; - $node = $node->getFirstAncestor(CallExpression::class); - } - if ($node instanceof QualifiedName) { - $node = $node->getFirstAncestor(CallExpression::class); - } - - if (!$node instanceof CallExpression) { - return; - } - - $memberAccess = $node->callableExpression; - - if (!$memberAccess instanceof MemberAccessExpression) { - return; - } - - $methodName = NodeUtil::nameFromTokenOrNode($node, $memberAccess->memberName); - - if ($methodName !== 'get') { - return; - } - - $expression = $memberAccess->dereferencableExpression; - $containerType = $this->reflector->reflectOffset($source, $expression->getEndPosition())->nodeContext()->type(); - - if ($containerType->instanceof(TypeFactory::class(self::CONTAINER_CLASS))->isFalseOrMaybe()) { - return; - } - - foreach ($this->inspector->services() as $service) { - $label = $service->id; - $suggestion = $inQuote ? $service->id : sprintf('\'%s\'', $service->id); - $import = null; - - if ($this->serviceIdIsFqn($service) && $inQuote) { - continue; - } - - if (false === $this->serviceIdIsFqn($service) && false === $inQuote) { - continue; - } - - if (false === $inQuote && $this->serviceIdIsFqn($service)) { - $suggestion = $service->type->short() . '::class'; - $label = $service->type->short() . '::class'; - $import = $service->type->__toString(); - } - - yield Suggestion::createWithOptions($suggestion, [ - 'label' => $label, - 'short_description' => $service->id, - 'documentation' => sprintf('**Symfony Service**: %s', $service->type->__toString()), - 'type' => Suggestion::TYPE_VALUE, - 'name_import' => $import, - 'priority' => 555, - ]); - } - - return true; - } - - private function serviceIdIsFqn(SymfonyContainerService $service): bool - { - return $service->type->isClass() && $service->id === $service->type->__toString(); - } -} diff --git a/lib/Extension/Symfony/Model/InMemorySymfonyContainerInspector.php b/lib/Extension/Symfony/Model/InMemorySymfonyContainerInspector.php deleted file mode 100644 index 2de06bdc4c..0000000000 --- a/lib/Extension/Symfony/Model/InMemorySymfonyContainerInspector.php +++ /dev/null @@ -1,37 +0,0 @@ -services; - } - - public function parameters(): array - { - return $this->parameters; - } - - public function service(string $id): ?SymfonyContainerService - { - foreach ($this->services as $service) { - if ($service->id === $id) { - return $service; - } - } - - return null; - } -} diff --git a/lib/Extension/Symfony/Model/SymfonyContainerInspector.php b/lib/Extension/Symfony/Model/SymfonyContainerInspector.php deleted file mode 100644 index 4366f5dc39..0000000000 --- a/lib/Extension/Symfony/Model/SymfonyContainerInspector.php +++ /dev/null @@ -1,18 +0,0 @@ -register(SymfonyContainerInspector::class, function (Container $container) { - $xmlPath = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class) - ->resolve($container->parameter(self::XML_PATH)->string()); - return new XmlSymfonyContainerInspector( - $xmlPath, - $container->parameter(self::PARAM_PUBLIC_SERVICES_ONLY)->bool() - ); - }); - $container->register(SymfonyContainerCompletor::class, function (Container $container) { - return new SymfonyContainerCompletor( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $container->get(SymfonyContainerInspector::class) - ); - }, [ - CompletionWorseExtension::TAG_TOLERANT_COMPLETOR => [ - 'name' => 'symfony', - ], - ]); - $container->register(SymfonyContainerContextResolver::class, function (Container $container) { - return new SymfonyContainerContextResolver( - $container->get(SymfonyContainerInspector::class) - ); - }, [ - WorseReflectionExtension::TAG_MEMBER_TYPE_RESOLVER => [ - ], - ]); - } - - - public function configure(Resolver $schema): void - { - $schema->setDefaults([ - self::XML_PATH => '%project_root%/var/cache/dev/App_KernelDevDebugContainer.xml', - self::PARAM_COMPLETOR_ENABLED => true, - self::PARAM_PUBLIC_SERVICES_ONLY => false, - ]); - $schema->setDescriptions([ - self::XML_PATH => 'Path to the Symfony container XML dump file', - self::PARAM_COMPLETOR_ENABLED => 'Enable/disable the Symfony completor - depends on Symfony extension being enabled', - self::PARAM_PUBLIC_SERVICES_ONLY => 'Only consider public services when providing analysis for the service locator', - ]); - } - - public function name(): string - { - return 'symfony'; - } -} diff --git a/lib/Extension/Symfony/SymfonySuggestExtension.php b/lib/Extension/Symfony/SymfonySuggestExtension.php deleted file mode 100644 index 003a2d026b..0000000000 --- a/lib/Extension/Symfony/SymfonySuggestExtension.php +++ /dev/null @@ -1,76 +0,0 @@ -register('symfony.suggest', function (Container $container) { - return new PhpactorComposerSuggestor( - $container->expect(ConfigurationExtension::SERVICE_PHPACTOR_CONFIG_LOCAL, JsonConfig::class), - $container->get(ComposerInspector::class), - function (JsonConfig $config, ComposerInspector $inspector) use ($container) { - if ($config->has(SymfonyExtension::PARAM_ENABLED)) { - return Changes::none(); - } - - $symfonyXML = $container->getParameter(SymfonyExtension::XML_PATH); - if (!is_string($symfonyXML)) { - return Changes::none(); - } - - $xmlPath = $container->expect( - FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, - PathResolver::class - )->resolve($symfonyXML); - - if (!file_exists($xmlPath)) { - return Changes::none(); - } - $changes = [ - new PhpactorConfigChange('Symfony framework detected, enable Symfony extension?', function (bool $enable) { - return [ - SymfonyExtension::PARAM_ENABLED => $enable, - ]; - }), - ]; - - if (!$config->has('indexer.exclude_patterns')) { - $changes[] = new PhpactorConfigChange('Add common Symfony exclude paths?', function (bool $enable) { - return [ - 'indexer.exclude_patterns' => [ - '/vendor/**/Tests/**/*', - '/vendor/**/tests/**/*', - '/var/cache/**/*', - '/vendor/composer/**/*' - ] - ]; - }); - } - - return Changes::from($changes); - } - ); - }, [ - ConfigurationExtension::TAG_SUGGESTOR => [], - ]); - } - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/Symfony/Tests/Integration/Completor/SymfonyContainerCompletorTest.php b/lib/Extension/Symfony/Tests/Integration/Completor/SymfonyContainerCompletorTest.php deleted file mode 100644 index 68b50b2b17..0000000000 --- a/lib/Extension/Symfony/Tests/Integration/Completor/SymfonyContainerCompletorTest.php +++ /dev/null @@ -1,326 +0,0 @@ -completor($source, $services, [])]); - $suggestions = iterator_to_array($completor->complete( - TextDocumentBuilder::create($source)->language('php')->build(), - ByteOffset::fromInt((int)$start) - )); - $assertion($suggestions); - } - - /** - * @return Generator,Closure(Suggestion[]):void}> - */ - public static function provideComplete(): Generator - { - yield 'not on symfony container, get method' => [ - <<<'EOT' - get(<>); - EOT - , - [ - new SymfonyContainerService('foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(0, $suggestions); - } - ]; - yield 'on container, not get method' => [ - <<<'EOT' - set(<>); - EOT - , - [ - new SymfonyContainerService('foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(0, $suggestions); - } - ]; - yield 'on container, no suggestions' => [ - <<<'EOT' - get(<>); - EOT - , - [ - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(0, $suggestions); - } - ]; - - yield 'on container with string literal ID suggestions' => [ - <<<'EOT' - get('<> - EOT - , - [ - new SymfonyContainerService('foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(2, $suggestions); - self::assertEquals('foobar', $suggestions[0]->name()); - } - ]; - - yield 'on container open quote' => [ - <<<'EOT' - get('<> - - EOT - , - [ - new SymfonyContainerService('foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(2, $suggestions); - } - ]; - - yield 'on container open quote with string' => [ - <<<'EOT' - get('foo<> - - EOT - , - [ - new SymfonyContainerService('foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(2, $suggestions); - } - ]; - - yield 'do not return classes in string literal' => [ - <<<'EOT' - get('<> - - EOT - , - [ - new SymfonyContainerService('Foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals('foobar.barfoo', $suggestions[0]->label()); - } - ]; - - yield 'do not return string literal service IDs without quote' => [ - <<<'EOT' - get(<> - - EOT - , - [ - new SymfonyContainerService('Foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals('Foobar::class', $suggestions[0]->label()); - } - ]; - - yield 'string literal on compound statement node' => [ - <<<'EOT' - get('<> - } - - EOT - , - [ - new SymfonyContainerService('Foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals('foobar.barfoo', $suggestions[0]->label()); - } - ]; - - yield 'on container property with class suggestions' => [ - <<<'EOT' - container()->get(<>); - } - } - EOT - , - [ - new SymfonyContainerService('Foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals('Foobar::class', $suggestions[0]->name()); - } - ]; - - yield 'on container property with class suggestions and partial' => [ - <<<'EOT' - container()->get(Foo<>); - } - } - EOT - , - [ - new SymfonyContainerService('Foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(1, $suggestions); - self::assertEquals('Foobar::class', $suggestions[0]->name()); - } - ]; - - yield 'on container property with string suggestions' => [ - <<<'EOT' - container()->get('foo<> - } - } - EOT - , - [ - new SymfonyContainerService('Foobar', TypeFactory::class('Foobar')), - new SymfonyContainerService('foobar.barfoo', TypeFactory::class('Foobar\\Barfoo')), - ] - , - /** @param Suggestion[] $suggestions */ - function (array $suggestions): void { - self::assertCount(1, $suggestions); - } - ]; - } - - /** - * @param SymfonyContainerService[] $services - * @param SymfonyContainerParameter[] $parameters - */ - private function completor(string $source, array $services, array $parameters): TolerantCompletor - { - $reflector = ReflectorBuilder::create()->addLocator( - new InternalLocator([ - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => __DIR__ . '/stub/symfony.stub', - 'Symfony\\Component\\DependencyInjection\\Container' => __DIR__ . '/stub/symfony.stub', - ]) - )->addSource($source)->build(); - return new SymfonyContainerCompletor($reflector, new InMemorySymfonyContainerInspector($services, $parameters)); - } -} diff --git a/lib/Extension/Symfony/Tests/Integration/Completor/stub/symfony.stub b/lib/Extension/Symfony/Tests/Integration/Completor/stub/symfony.stub deleted file mode 100644 index 35d8fecead..0000000000 --- a/lib/Extension/Symfony/Tests/Integration/Completor/stub/symfony.stub +++ /dev/null @@ -1,4 +0,0 @@ -resolve( - <<<'EOT' - get('foo.bar'); - wrAssertType('Foo\Bar', $foo); - } - EOT - , - [ - new SymfonyContainerService('foo.bar', TypeFactory::class('Foo\Bar')), - ] - ); - } - - public function testResolveStringLiteralIdNoMatches(): void - { - $this->resolve( - <<<'EOT' - get(Foo::class); - wrAssertType('Foo\Bar', $foo); - } - EOT - , - [ - new SymfonyContainerService('Foo', TypeFactory::class('Foo\Bar')), - ] - ); - } - - /** - * @param SymfonyContainerService[] $services - */ - public function resolve(string $sourceCode, array $services): void - { - $sourceCode = TextDocumentBuilder::fromUnknown($sourceCode); - $reflector = ReflectorBuilder::create() - ->addFrameWalker(new TestAssertWalker($this)) - ->addSource( - 'addMemberContextResolver(new SymfonyContainerContextResolver( - new InMemorySymfonyContainerInspector($services, []) - )) - ->build(); - - $reflector->reflectOffset($sourceCode, mb_strlen($sourceCode)); - } -} diff --git a/lib/Extension/Symfony/Tests/IntegrationTestCase.php b/lib/Extension/Symfony/Tests/IntegrationTestCase.php deleted file mode 100644 index 4718c6e006..0000000000 --- a/lib/Extension/Symfony/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,18 +0,0 @@ -workspace()->reset(); - } - protected function workspace(): Workspace - { - return Workspace::create(__DIR__ . '/Workspace'); - } -} diff --git a/lib/Extension/Symfony/Tests/Unit/Adapter/XmlSymfonyContainerInspectorTest.php b/lib/Extension/Symfony/Tests/Unit/Adapter/XmlSymfonyContainerInspectorTest.php deleted file mode 100644 index 2cfa1759c4..0000000000 --- a/lib/Extension/Symfony/Tests/Unit/Adapter/XmlSymfonyContainerInspectorTest.php +++ /dev/null @@ -1,224 +0,0 @@ -inspect($this->workspace()->path('services.xml'))->services()); - } - - public function testListsServicesFormValidXml(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - EOT - ); - self::assertEquals([ - new SymfonyContainerService('service_container', TypeFactory::class('Symfony\Component\DependencyInjection\ContainerInterface')), - ], $this->inspect($this->workspace()->path('services.xml'))->services()); - } - - public function testListsPublicServicesOnly(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - - - EOT - ); - self::assertEquals([ - new SymfonyContainerService('one', TypeFactory::class('One')), - ], $this->inspect($this->workspace()->path('services.xml'))->services()); - } - - public function testNoServices(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - EOT - ); - self::assertEquals([ - ], $this->inspect($this->workspace()->path('services.xml'))->services()); - } - - public function testRetrieveService(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - EOT - ); - self::assertEquals( - new SymfonyContainerService( - 'service_container', - TypeFactory::class('Symfony\Component\DependencyInjection\ContainerInterface') - ), - $this->inspect($this->workspace()->path('services.xml'))->service('service_container') - ); - } - - public function testRetrieveNonPublicServiceIfConfigured(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - - EOT - ); - self::assertEquals( - new SymfonyContainerService( - 'App\Component\Foo\Service\RequestHandlerService', - TypeFactory::class('App\Component\Foo\Service\RequestHandlerService') - ), - $this->inspect( - $this->workspace()->path('services.xml'), - publicOnly: false, - )->service('App\Component\Foo\Service\RequestHandlerService') - ); - } - - public function testDefinitionWithNoAttributes(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - EOT - ); - self::assertEquals([ - ], $this->inspect($this->workspace()->path('services.xml'))->services()); - } - - public function testParameters(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - /app - dev - - - EOT - ); - self::assertEquals([ - new SymfonyContainerParameter('kernel.project_dir', new StringLiteralType('/app')), - new SymfonyContainerParameter('kernel.environment', new StringLiteralType('dev')), - ], $this->inspect($this->workspace()->path('services.xml'))->parameters()); - } - - public function testCachesResultIfMtimeSame(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - EOT - ); - touch($this->workspace()->path('services.xml'), 100); - $inspector = $this->inspect($this->workspace()->path('services.xml')); - self::assertEquals('Foo', $inspector->service('test')->type->__toString()); - - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - EOT - ); - touch($this->workspace()->path('services.xml'), 100); - self::assertNotNull($inspector->service('test')); - } - - public function testUsesCachedResultIfMtimeDifferent(): void - { - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - EOT - ); - touch($this->workspace()->path('services.xml'), 100); - $inspector = $this->inspect($this->workspace()->path('services.xml')); - self::assertEquals('Foo', $inspector->service('test')->type->__toString()); - - $this->workspace()->put( - 'services.xml', - <<<'EOT' - - - - - - - EOT - ); - touch($this->workspace()->path('services.xml'), 101); - self::assertNull($inspector->service('test')); - } - - private function inspect(string $xmlPath, bool $publicOnly = true): XmlSymfonyContainerInspector - { - return new XmlSymfonyContainerInspector($xmlPath, $publicOnly); - } -} diff --git a/lib/Extension/Symfony/WorseReflection/SymfonyContainerContextResolver.php b/lib/Extension/Symfony/WorseReflection/SymfonyContainerContextResolver.php deleted file mode 100644 index 069a45dfe9..0000000000 --- a/lib/Extension/Symfony/WorseReflection/SymfonyContainerContextResolver.php +++ /dev/null @@ -1,69 +0,0 @@ -memberType() !== ReflectionMember::TYPE_METHOD) { - return null; - } - - if ($member->name() !== 'get') { - return null; - } - - if (count($arguments) === 0) { - return null; - } - - if (!$member->class()->isInstanceOf(ClassName::fromString(self::CONTAINER_CLASS))) { - return null; - } - - $argument = $arguments->at(0)->type(); - if ($argument instanceof StringLiteralType) { - $service = $this->inspector->service($argument->value()); - if (null === $service) { - return TypeFactory::union(TypeFactory::object(), TypeFactory::null()); - } - return $service->type; - } - if ($argument instanceof ClassStringType && $argument->className()) { - $service = $this->inspector->service($argument->className()->__toString()); - if (null === $service) { - return TypeFactory::union(TypeFactory::object(), TypeFactory::null()); - } - $type = $service->type; - if ($type instanceof ClassType) { - $type = $type->asReflectedClasssType($reflector); - } - return $type; - } - - return TypeFactory::undefined(); - } -} diff --git a/lib/Extension/WorseReferenceFinder/Tests/Unit/WorseReferenceFinderExtensionTest.php b/lib/Extension/WorseReferenceFinder/Tests/Unit/WorseReferenceFinderExtensionTest.php deleted file mode 100644 index 4dc81d5d43..0000000000 --- a/lib/Extension/WorseReferenceFinder/Tests/Unit/WorseReferenceFinderExtensionTest.php +++ /dev/null @@ -1,92 +0,0 @@ -createContainer(); - $locator = $container->get(ReferenceFinderExtension::SERVICE_DEFINITION_LOCATOR); - - assert($locator instanceof DefinitionLocator); - - $location = $locator->locateDefinition( - TextDocumentBuilder::create(WorseReferenceFinderExtension::class)->build(), - ByteOffset::fromInt(3) - )->first()->location(); - - $this->assertEquals(Path::canonicalize(__DIR__ . '/../../WorseReferenceFinderExtension.php'), $location->uri()->path()); - } - - public function testLocateType(): void - { - $container = $this->createContainer(); - $locator = $container->get(ReferenceFinderExtension::SERVICE_TYPE_LOCATOR); - - assert($locator instanceof TypeLocator); - - $location = $locator->locateTypes( - TextDocumentBuilder::create( - <<<'EOT' - language('php')->uri('/foo')->build(), - ByteOffset::fromInt(10) - ); - - $this->assertEquals('/foo', $location->first()->location()->uri()->path()); - } - - public function testLocateVariable(): void - { - $container = $this->createContainer(); - $locator = $container->get(ReferenceFinder::class); - - assert($locator instanceof ReferenceFinder); - - $location = $locator->findReferences( - TextDocumentBuilder::create( - <<<'EOT' - language('php')->uri('/foo')->build(), - ByteOffset::fromInt(10) - ); - - $this->assertEquals(1, count(iterator_to_array($location))); - } - - private function createContainer(): Container - { - $container = PhpactorContainer::fromExtensions([ - WorseReferenceFinderExtension::class, - WorseReflectionExtension::class, - ReferenceFinderExtension::class, - FilePathResolverExtension::class, - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - LoggingExtension::class, - ], [ - 'file_path_resolver.application_root' => __DIR__ . '/../../../../../', - ]); - return $container; - } -} diff --git a/lib/Extension/WorseReferenceFinder/WorseReferenceFinderExtension.php b/lib/Extension/WorseReferenceFinder/WorseReferenceFinderExtension.php deleted file mode 100644 index 825b8b072a..0000000000 --- a/lib/Extension/WorseReferenceFinder/WorseReferenceFinderExtension.php +++ /dev/null @@ -1,61 +0,0 @@ -register('worse_reference_finder.definition_locator.reflection', function (Container $container) { - return new WorseReflectionDefinitionLocator( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(Cache::class) - ); - }, [ ReferenceFinderExtension::TAG_DEFINITION_LOCATOR => []]); - $container->register('worse_reference_finder.type_locator.reflection', function (Container $container) { - return new WorseReflectionTypeLocator( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR) - ); - }, [ ReferenceFinderExtension::TAG_TYPE_LOCATOR => []]); - - $container->register('worse_reference_finder.definition_locator.plain_text_class', function (Container $container) { - return new WorsePlainTextClassDefinitionLocator( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - ); - }, [ ReferenceFinderExtension::TAG_DEFINITION_LOCATOR => []]); - - $container->register('worse_reference_finder.definition_locator.variable', function (Container $container) { - return new TolerantVariableDefintionLocator( - new TolerantVariableReferenceFinder( - $container->get(AstProvider::class), - true - ) - ); - }, [ ReferenceFinderExtension::TAG_DEFINITION_LOCATOR => []]); - - $container->register('worse_reference_finder.reference_finder.variable', function (Container $container) { - return new TolerantVariableReferenceFinder( - $container->get(AstProvider::class), - ); - }, [ ReferenceFinderExtension::TAG_REFERENCE_FINDER => []]); - } - - - public function configure(Resolver $schema): void - { - } -} diff --git a/lib/Extension/WorseReflection/Command/DumpAstCommand.php b/lib/Extension/WorseReflection/Command/DumpAstCommand.php deleted file mode 100644 index 12d494d252..0000000000 --- a/lib/Extension/WorseReflection/Command/DumpAstCommand.php +++ /dev/null @@ -1,63 +0,0 @@ -setDescription('Dump and profile the ast for a given file'); - $this->addArgument(self::ARG_PATH, InputArgument::REQUIRED); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - /** @var string $path */ - $path = $input->getArgument(self::ARG_PATH); - - $parseStart = microtime(true); - $rootNode = $this->parser->get(TextDocumentBuilder::fromUri($path)->build()); - $parseEnd = microtime(true); - - $traveralStart = microtime(true); - $out = ''; - $this->dump($output, $rootNode, $out); - $traversalEnd = microtime(true); - - $output->writeln($out); - $output->writeln(''); - $output->writeln(sprintf('Parsing time: %ss', number_format($parseEnd - $parseStart, 4))); - $output->writeln(sprintf('Traversal time: %ss', number_format($traversalEnd - $traveralStart, 4))); - - return 0; - } - - private function dump(OutputInterface $output, Node $node, string &$out, int $depth = 0): void - { - foreach ($node->getChildNodesAndTokens() as $child) { - if ($child instanceof Node) { - $out .= sprintf('<%s ↓%s>', $child->getNodeKindName(), $depth); - $this->dump($output, $child, $out, $depth + 1); - } - if ($child instanceof Token) { - $out .= $child->getFullText($node->getFileContents()); - } - } - } -} diff --git a/lib/Extension/WorseReflection/Documentor/DiagnosticDocumentor.php b/lib/Extension/WorseReflection/Documentor/DiagnosticDocumentor.php deleted file mode 100644 index 31bc6c24ef..0000000000 --- a/lib/Extension/WorseReflection/Documentor/DiagnosticDocumentor.php +++ /dev/null @@ -1,103 +0,0 @@ - $providerIds - */ - public function __construct( - private Container $container, - private array $providerIds - ) { - } - - public function document(string $commandName = ''): string - { - $docs = [ - '.. _diagnostics:', - '', - 'Diagnostics', - '===========', - "\n", - ".. This document is generated via the `$commandName` command", - "\n", - '.. contents::', - ' :depth: 2', - ' :backlinks: none', - ' :local:', - "\n", - ]; - foreach (array_keys($this->providerIds) as $providerId) { - $documentation = $this->documentProvider($this->container->expect($providerId, DiagnosticProvider::class)); - $docs[] = $documentation; - } - return implode("\n", $docs); - } - - private function documentProvider(DiagnosticProvider $provider): string - { - $reflection = new ReflectionClass($provider); - $docs = []; - $docs[] = DocHelper::title('-', sprintf('%s', $reflection->getShortName())); - $docs[] = ''; - $docs[] = trim((string)preg_replace('{(^/\*\*)|(\s+\*\s)|(\s+\*/$)}m', ' ', trim((string)$reflection->getDocComment()))); - $docs[] = ''; - $docs[] = '.. tabs::'; - $docs[] = ''; - foreach ($provider->examples() as $example) { - if ($example->valid) { - continue; - } - $tab = []; - $docs[] = sprintf(' .. tab:: %s', $example->title); - $docs[] = ' ' . DocHelper::indent(8, implode("\n", $this->buildExample($example, $provider))); - } - return implode("\n", $docs); - } - - /** - * @return Diagnostics - */ - private function diagnostics(DiagnosticExample $example, DiagnosticProvider $provider): Diagnostics - { - $reflector = ReflectorBuilder::create()->addSource($example->source)->addDiagnosticProvider($provider)->build(); - return wait($reflector->diagnostics(TextDocumentBuilder::fromPathAndString('file:///test', $example->source))); - } - /** - * @return array - */ - private function buildExample(DiagnosticExample $example, DiagnosticProvider $provider): array - { - $ex = []; - $ex[] = ''; - $ex[] = '.. code-block:: php'; - $ex[] = ''; - $ex[] = ' ' . DocHelper::indent(4, $example->source); - $ex[] = ''; - $diagnostics = $this->diagnostics($example, $provider); - if (!$diagnostics->count()) { - return $ex; - } - $ex[] = 'Diagnostic(s):'; - $ex[] = ''; - foreach ($diagnostics as $diagnostic) { - $ex[] = sprintf('- ``%s``: ``%s``', $diagnostic->severity()->toString(), $diagnostic->message()); - } - $ex[] = ''; - return $ex; - } -} diff --git a/lib/Extension/WorseReflection/Telemetry/WorseTelemetry.php b/lib/Extension/WorseReflection/Telemetry/WorseTelemetry.php deleted file mode 100644 index ef078d626a..0000000000 --- a/lib/Extension/WorseReflection/Telemetry/WorseTelemetry.php +++ /dev/null @@ -1,59 +0,0 @@ -getMethods() as $method) { - yield new ClassHook(CoreReflector::class, $method->getName(), function (TracerContext $tracing, PreContext $context) use ($method) { - return $tracing->spanBuilder( - $context, - $method->getName() - )->setSpanKind(SpanKind::KIND_INTERNAL)->setParent($context->context())->startSpan(); - }); - } - $reflection = new ReflectionClass(SourceCodeReflector::class); - foreach ($reflection->getMethods() as $method) { - yield new ClassHook(SourceCodeReflector::class, $method->getName(), function (TracerContext $tracing, PreContext $context) use ($method) { - return $tracing->spanBuilder( - $context, - $method->getName() - )->setSpanKind(SpanKind::KIND_INTERNAL)->setParent($context->context())->startSpan(); - }); - } - - yield new ClassHook(SourceCodeLocator::class, 'locate', function (TracerContext $tracing, PreContext $context) { - return $tracing->spanBuilder( - $context, - $context->object::class, - )->setSpanKind(SpanKind::KIND_INTERNAL)->setParent( - $context->context() - )->startSpan(); - }); - - yield new ClassHook(AstProvider::class, 'get', function (TracerContext $tracing, PreContext $context) { - return $tracing - ->spanBuilder($context, 'tolerant-php-parser') - ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setParent($context->context()) - ->setAttribute('parser-file', $context->param(1)) - ->startSpan(); - }); - } -} diff --git a/lib/Extension/WorseReflection/Tests/Command/DumpAstCommandTest.php b/lib/Extension/WorseReflection/Tests/Command/DumpAstCommandTest.php deleted file mode 100644 index 44daa9f80b..0000000000 --- a/lib/Extension/WorseReflection/Tests/Command/DumpAstCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -reset(); - $workspace->put('test.php', 'run(new ArrayInput([ - 'path' => $workspace->path('test.php') - ]), $output); - self::assertEquals(0, $exitCode); - self::assertStringContainsString('Parsing time', $output->fetch()); - } -} diff --git a/lib/Extension/WorseReflection/Tests/Example/DiagnosticsTest.php b/lib/Extension/WorseReflection/Tests/Example/DiagnosticsTest.php deleted file mode 100644 index fb7dcb5855..0000000000 --- a/lib/Extension/WorseReflection/Tests/Example/DiagnosticsTest.php +++ /dev/null @@ -1,55 +0,0 @@ -addSource($example->source)->addDiagnosticProvider($provider)->build(); - $diagnostics = wait($reflector->diagnostics(TextDocumentBuilder::fromPathAndString('file:///test', $example->source))); - if ($example->minPhpVersion && version_compare(PHP_VERSION, $example->minPhpVersion, '<')) { - $this->addToAssertionCount(1); - return; - } - - if ($example->valid) { - self::assertCount(0, $diagnostics); - return; - } - ($example->assertion)($diagnostics); - } - - /** - * @return Generator - */ - public static function provideDiagnostics(): Generator - { - $container = PhpactorContainer::fromExtensions([ - WorseReflectionExtension::class, - ]); - foreach ($container->getServiceIdsForTag(WorseReflectionExtension::TAG_DIAGNOSTIC_PROVIDER) as $serviceId => $_) { - /** @var class-string $serviceId */ - $provider = $container->get($serviceId); - foreach ($provider->examples() as $example) { - yield sprintf('%s %s', $serviceId, $example->title) => [ - $provider, - $example - ]; - } - - } - } -} diff --git a/lib/Extension/WorseReflection/Tests/Unit/TestExtension.php b/lib/Extension/WorseReflection/Tests/Unit/TestExtension.php deleted file mode 100644 index 391aa5f2ea..0000000000 --- a/lib/Extension/WorseReflection/Tests/Unit/TestExtension.php +++ /dev/null @@ -1,59 +0,0 @@ -register('test.framewalker', function (Container $container) { - return new TestFrameWalker(); - }, [ WorseReflectionExtension::TAG_FRAME_WALKER => []]); - } - - - public function configure(Resolver $schema): void - { - } -} - -class TestFrameWalker implements Walker -{ - public function enter(FrameResolver $builder, Frame $frame, Node $node): Frame - { - if ($frame->locals()->byName('test_variable')->count()) { - return $frame; - } - - $frame->locals()->set( - Variable::fromSymbolContext( - NodeContext::for(Symbol::fromTypeNameAndPosition('variable', 'test_variable', ByteOffsetRange::fromInts(1, 10))) - ) - ); - return $frame; - } - - public function exit(FrameResolver $builder, Frame $frame, Node $node): Frame - { - return $frame; - } - - public function nodeFqns(): array - { - return []; - } -} diff --git a/lib/Extension/WorseReflection/Tests/Unit/WorseReflectionExtensionTest.php b/lib/Extension/WorseReflection/Tests/Unit/WorseReflectionExtensionTest.php deleted file mode 100644 index 33388855ec..0000000000 --- a/lib/Extension/WorseReflection/Tests/Unit/WorseReflectionExtensionTest.php +++ /dev/null @@ -1,92 +0,0 @@ -createReflector([ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ . '/../../../../..', - ]); - $this->assertEquals((string) $reflector->reflectClass(__CLASS__)->name(), __CLASS__); - } - - public function testRegistersTaggedFramewalkers(): void - { - $reflector = $this->createReflector([ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ . '/../../../../..', - ]); - $frame = $reflector->reflectClass(__CLASS__)->methods()->get('testRegistersTaggedFramewalkers')->frame(); - $this->assertCount(1, $frame->locals()->byName('test_variable')); - } - - public function testProvideReflectorWithStubs(): void - { - $reflector = $this->createReflector([ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ . '/../../../../..' - ]); - $this->assertEquals((string) $reflector->reflectClass(__CLASS__)->name(), __CLASS__); - } - - public function testAdditiveStubPaths(): void - { - $reflector = $this->createReflector([ - WorseReflectionExtension::PARAM_ADDITIVE_STUBS => [ - 'example/stub.stub', - ], - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ . '/../../../../..', - FilePathResolverExtension::PARAM_PROJECT_ROOT => __DIR__ - ]); - - $reflection = $reflector->reflectClass(__CLASS__); - $method = $reflection->methods()->byName('testAdditiveStubPaths')->first(); - self::assertEquals('string', $method->inferredType()->__toString()); - } - - public function testProvideReflectorWithStubsAndCustomCacheDir(): void - { - $reflector = $this->createReflector([ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__, - WorseReflectionExtension::PARAM_STUB_DIR => __DIR__ . '/../../../../../vendor/jetbrains/phpstorm-stubs', - WorseReflectionExtension::PARAM_STUB_CACHE_DIR => $cachePath = __DIR__ . '/../../stubs' - ]); - $this->assertEquals((string) $reflector->reflectClass(__CLASS__)->name(), __CLASS__); - $this->assertFileExists($cachePath); - } - - /** - * @param array $params - */ - private function createReflector(array $params = []): Reflector - { - $container = $this->createContainer($params); - - return $container->get(WorseReflectionExtension::SERVICE_REFLECTOR); - } - - /** - * @param array $params - */ - private function createContainer(array $params): Container - { - return PhpactorContainer::fromExtensions([ - WorseReflectionExtension::class, - FilePathResolverExtension::class, - ClassToFileExtension::class, - ComposerAutoloaderExtension::class, - LoggingExtension::class, - TestExtension::class, - ], $params); - } -} diff --git a/lib/Extension/WorseReflection/Tests/Unit/example/stub.stub b/lib/Extension/WorseReflection/Tests/Unit/example/stub.stub deleted file mode 100644 index 202fe741a4..0000000000 --- a/lib/Extension/WorseReflection/Tests/Unit/example/stub.stub +++ /dev/null @@ -1,14 +0,0 @@ -setDefaults([ - self::PARAM_IMPORT_GLOBALS => false, - self::PARAM_ENABLE_CACHE => true, - self::PARAM_CACHE_LIFETIME => 1.0, - self::PARAM_ENABLE_CONTEXT_LOCATION => true, - self::PARAM_STUB_CACHE_DIR => '%cache%/worse-reflection', - self::PARAM_STUB_DIR => '%application_root%/vendor/jetbrains/phpstorm-stubs', - self::PARAM_ADDITIVE_STUBS => [], - self::PARAM_UNDEFINED_VAR_LEVENSHTEIN => 4, - ]); - $schema->setDescriptions([ - self::PARAM_ENABLE_CACHE => 'If reflection caching should be enabled', - self::PARAM_CACHE_LIFETIME => 'If caching is enabled, limit the amount of time a cache entry can stay alive', - self::PARAM_UNDEFINED_VAR_LEVENSHTEIN => 'Levenshtein distance to use when suggesting corrections for variable names', - self::PARAM_ENABLE_CONTEXT_LOCATION => <<<'EOT' - If source code is passed to a ``Reflector`` then temporarily make it available as a - source location. Note this should NOT be enabled if the source code can be - located in another (e.g. when running a Language Server) - EOT - , - self::PARAM_STUB_DIR => 'Location of the core PHP stubs - these will be scanned and cached on the first request', - self::PARAM_ADDITIVE_STUBS => 'Additive stubs files relative to the project root. These stubs augment existing defininitions.', - self::PARAM_STUB_CACHE_DIR => 'Cache directory for stubs', - self::PARAM_IMPORT_GLOBALS => 'Show hints for non-imported global classes and functions', - ]); - $schema->setTypes([ - self::PARAM_UNDEFINED_VAR_LEVENSHTEIN => 'integer', - ]); - } - - public function load(ContainerBuilder $container): void - { - $this->registerCommands($container); - $this->registerReflection($container); - $this->registerSourceLocators($container); - $this->registerMemberProviders($container); - $this->registerDiagnosticProviders($container); - $this->registerTelemetry($container); - } - - /** - * @return list - */ - public static function additiveStubPaths(Container $container): array - { - $resolver = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class); - $stubPaths = array_map(function (string $path) use ($resolver) { - $projectRoot = $resolver->resolve('%project_root%'); - return Path::join($projectRoot, $resolver->resolve($path)); - - }, $container->parameter(self::PARAM_ADDITIVE_STUBS)->listOfString()); - return $stubPaths; - } - - private function registerReflection(ContainerBuilder $container): void - { - $container->register(self::SERVICE_REFLECTOR, function (Container $container) { - $resolver = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER); - $builder = ReflectorBuilder::create() - ->withSourceReflectorFactory( - new TolerantFactory($container->get(AstProvider::class)) - ) - ->cacheLifetime($container->parameter(self::PARAM_CACHE_LIFETIME)->float()); - - if ($container->parameter(self::PARAM_ENABLE_CONTEXT_LOCATION)->bool()) { - $builder->enableContextualSourceLocation(); - } - - if ($container->parameter(self::PARAM_ENABLE_CACHE)->bool()) { - $builder->enableCache(); - $builder->withCache($container->get(Cache::class)); - $builder->withCacheForDocument($container->get(CacheForDocument::class)); - } - - foreach ($container->getServiceIdsForTag(self::TAG_SOURCE_LOCATOR) as $serviceId => $attrs) { - $builder->addLocator($container->get($serviceId), $attrs['priority'] ?? 0); - } - - foreach (array_keys($container->getServiceIdsForTag(self::TAG_FRAME_WALKER)) as $serviceId) { - $builder->addFrameWalker($container->get($serviceId)); - } - - foreach (array_keys($container->getServiceIdsForTag(self::TAG_MEMBER_TYPE_RESOLVER)) as $serviceId) { - $memberTypeResolver = $container->get($serviceId); - if (null === $memberTypeResolver) { - continue; - } - $builder->addMemberContextResolver($memberTypeResolver); - } - - foreach (array_keys($container->getServiceIdsForTag(self::TAG_MEMBER_PROVIDER)) as $serviceId) { - $memberProvider = $container->get($serviceId); - if (null === $memberProvider) { - continue; - } - $builder->addMemberProvider($memberProvider); - } - foreach (array_keys($container->getServiceIdsForTag(self::TAG_DIAGNOSTIC_PROVIDER)) as $serviceId) { - $builder->addDiagnosticProvider($container->get($serviceId)); - } - - $builder->withLogger( - LoggingExtension::channelLogger($container, 'wr') - ); - - return $builder->build(); - }); - - $container->register(self::SERVICE_AST_PROVIDER, function (Container $container) { - return new CachedAstProvider( - $container->get(TolerantAstProvider::class), - $container->get(Cache::class), - $container->get(CacheForDocument::class), - ); - }); - - $container->register(TolerantAstProvider::class, function (Container $container) { - return new TolerantAstProvider(new Parser(), LoggingExtension::channelLogger($container, 'wr')); - }); - $container->register(Cache::class, function (Container $container) { - return new TtlCache($container->parameter(self::PARAM_CACHE_LIFETIME)->float()); - }); - $container->register(CacheForDocument::class, function (Container $container) { - return new CacheForDocument( - fn () => new StaticCache(), - ); - }); - } - - private function registerSourceLocators(ContainerBuilder $container): void - { - $container->register('worse_reflection.locator.stub', function (Container $container) { - $resolver = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER); - return new StubSourceLocator( - ReflectorBuilder::create()->build(), - $resolver->resolve($container->parameter(self::PARAM_STUB_DIR)->string()), - $resolver->resolve($container->parameter(self::PARAM_STUB_CACHE_DIR)->string()) - ); - }, [ self::TAG_SOURCE_LOCATOR => []]); - - $container->register('worse_reflection.locator.function', function (Container $container) { - return new NativeReflectionFunctionSourceLocator(); - }, [ self::TAG_SOURCE_LOCATOR => []]); - - $container->register('worse_reflection.locator.worse', function (Container $container) { - return new ClassToFileSourceLocator($container->get(ClassToFileExtension::SERVICE_CONVERTER)); - }, [ self::TAG_SOURCE_LOCATOR => []]); - } - - private function registerMemberProviders(ContainerBuilder $container): void - { - $container->register('worse_reflection.member_provider.docblock', function (Container $container) { - return new DocblockMemberProvider(); - }, [ self::TAG_MEMBER_PROVIDER => []]); - $container->register('worse_reflection.member_provider.stubs', function (Container $container) { - $stubPaths = self::additiveStubPaths($container); - return new StubFileMemberProvider($stubPaths); - }, [ self::TAG_MEMBER_PROVIDER => []]); - } - - private function registerDiagnosticProviders(ContainerBuilder $container): void - { - $container->register(MissingMemberProvider::class, function (Container $container) { - return new MissingMemberProvider(); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(DocblockMissingReturnTypeProvider::class, function (Container $container) { - return new DocblockMissingReturnTypeProvider(); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(DocblockMissingParamProvider::class, function (Container $container) { - return new DocblockMissingParamProvider(); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(AssignmentToMissingPropertyProvider::class, function (Container $container) { - return new AssignmentToMissingPropertyProvider(); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(MissingReturnTypeProvider::class, function (Container $container) { - return new MissingReturnTypeProvider(); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(UnresolvableNameProvider::class, function (Container $container) { - return new UnresolvableNameProvider($container->parameter(self::PARAM_IMPORT_GLOBALS)->bool()); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(UnusedImportProvider::class, function (Container $container) { - return new UnusedImportProvider(); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(DeprecatedUsageDiagnosticProvider::class, function (Container $container) { - return new DeprecatedUsageDiagnosticProvider(); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(UndefinedVariableProvider::class, function (Container $container) { - return new UndefinedVariableProvider($container->parameter(self::PARAM_UNDEFINED_VAR_LEVENSHTEIN)->int()); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(DocblockMissingExtendsTagProvider::class, function (Container $container) { - return new DocblockMissingExtendsTagProvider(new ClassGenericDiagnosticHelper()); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - $container->register(DocblockMissingImplementsTagProvider::class, function (Container $container) { - return new DocblockMissingImplementsTagProvider(new ClassGenericDiagnosticHelper()); - }, [ self::TAG_DIAGNOSTIC_PROVIDER => []]); - - $container->register(DiagnosticDocumentor::class, function (Container $container) { - return new DiagnosticDocumentor( - $container, - $container->getServiceIdsForTag(self::TAG_DIAGNOSTIC_PROVIDER) - ); - }, [ - DebugExtension::TAG_DOCUMENTOR => [ 'name' => 'diagnostic' ], - ]); - } - - private function registerCommands(ContainerBuilder $container): void - { - $container->register(DumpAstCommand::class, function (Container $container) { - return new DumpAstCommand($container->expect(self::SERVICE_AST_PROVIDER, AstProvider::class)); - }, [ - ConsoleExtension::TAG_COMMAND => [ - 'name' => 'worse:dump-ast', - ] - ]); - } - - private function registerTelemetry(ContainerBuilder $container): void - { - $container->register(WorseTelemetry::class, function (Container $container) { - return new WorseTelemetry(); - }, [OpenTelemetryExtension::TAG_HOOK_PROVIDER => []]); - } -} diff --git a/lib/Extension/WorseReflectionAnalyse/Command/AnalyseCommand.php b/lib/Extension/WorseReflectionAnalyse/Command/AnalyseCommand.php deleted file mode 100644 index c0e24e8b2d..0000000000 --- a/lib/Extension/WorseReflectionAnalyse/Command/AnalyseCommand.php +++ /dev/null @@ -1,154 +0,0 @@ - - */ - private array $sources = []; - - public function __construct(private Analyser $analyser) - { - parent::__construct(); - } - - public function configure(): void - { - $this->setDescription('Experimental diagnostics for files in the given path'); - $this->addArgument(self::ARG_PATH, InputArgument::REQUIRED, 'Path to analyse'); - $this->addOption(self::OPT_FORMAT, null, InputOption::VALUE_REQUIRED, 'Output format ("table" or "json")', 'table'); - $this->addOption(self::OPT_IGNORE_FAILURE, null, InputOption::VALUE_NONE, 'Exit with 0 even if there were problems'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $start = (float)microtime(true); - - $progressOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; - - /** - * @var array> $results - */ - $results = []; - $path = $input->getArgument(self::ARG_PATH); - - $count = count(iterator_to_array($this->analyser->fileList($path), true)); - $progressOutput->writeln('Analysing files...'); - $progressOutput->writeln(''); - $progress = new ProgressBar($progressOutput, $count); - $progress->start(); - $hasErrors = false; - - foreach ($this->analyser->analyse($path) as $file => $diagnostics) { - $progress->advance(); - $results[$file] = $diagnostics; - if (0 !== $diagnostics->count()) { - $hasErrors = true; - } - } - $progress->finish(); - $progressOutput->writeln(''); - $progressOutput->writeln(''); - - match ($input->getOption(self::OPT_FORMAT)) { - 'json' => $this->renderJson($output, $results), - default => $this->renderTable($output, $results, $start), - }; - - if ($input->getOption(self::OPT_IGNORE_FAILURE)) { - return 0; - } - - return $hasErrors ? 1 : 0; - } - - /** - * @param array> $results - */ - private function renderTable(OutputInterface $output, array $results, float $start): void - { - $errorCount = 0; - foreach ($results as $file => $diagnostics) { - if (!count($diagnostics)) { - continue; - } - $output->writeln($file); - $table = new Table($output); - $table->setHeaders(['line:col', 'severity', 'message']); - $table->setColumnMaxWidth(2, 60); - foreach ($diagnostics as $diagnostic) { - $errorCount++; - $lineCol = $this->lineCol($file, $diagnostic->range()->start()); - $table->addRow([ - sprintf('%s:%s', $lineCol->line(), $lineCol->col()), - $diagnostic->severity()->toString(), - $diagnostic->message(), - ]); - } - $table->render(); - $output->writeln(''); - } - $output->writeln(sprintf( - '%s problems in %s seconds with %sb memory', - number_format($errorCount), - number_format(microtime(true) - $start, 4), - number_format(memory_get_peak_usage()), - )); - } - - /** - * @param array> $results - */ - private function renderJson(OutputInterface $output, array $results): void - { - foreach ($results as $file => $diagnostics) { - foreach ($diagnostics as $diagnostic) { - $lineCol = $this->lineCol($file, $diagnostic->range()->start()); - $output->writeln((string)json_encode([ - 'file' => $file, - 'line' => $lineCol->line(), - 'col' => $lineCol->col(), - 'range' => ['start' => $diagnostic->range()->start()->toInt(), 'end' => $diagnostic->range()->end()->toInt()], - 'code' => $diagnostic->code(), - 'message' => $diagnostic->message(), - 'severity' => $diagnostic->severity()->toString(), - ], JSON_UNESCAPED_SLASHES)); - } - } - } - - private function lineCol(string $file, ByteOffset $offset): LineCol - { - if (!isset($this->sources[$file])) { - $contents = @file_get_contents(Path::makeAbsolute($file, (string)getcwd())); - $this->sources[$file] = false === $contents ? '' : $contents; - } - - if ('' === $this->sources[$file]) { - return new LineCol(1, 1); - } - - return LineCol::fromByteOffset($this->sources[$file], $offset); - } -} diff --git a/lib/Extension/WorseReflectionAnalyse/Model/Analyser.php b/lib/Extension/WorseReflectionAnalyse/Model/Analyser.php deleted file mode 100644 index 235554e413..0000000000 --- a/lib/Extension/WorseReflectionAnalyse/Model/Analyser.php +++ /dev/null @@ -1,73 +0,0 @@ -> - */ - public function analyse(string $path): Generator - { - $cwd = (string)getcwd(); - $absPath = Path::makeAbsolute($path, $cwd); - if (file_exists($absPath) && is_file($absPath)) { - yield $path => wait($this->reflector->diagnostics(TextDocumentBuilder::fromUri($absPath)->build())); - return; - } - - /** @var array $documents */ - $documents = []; - foreach ($this->fileList($absPath) as $file) { - $document = TextDocumentBuilder::fromUri($file->path())->build(); - $documents[$file->path()] = $document; - $this->index->index($document); - } - - foreach ($documents as $filePath => $document) { - try { - yield Path::makeRelative( - $filePath, - $cwd - ) => wait($this->reflector->diagnostics($document)); - } catch (Throwable $error) { - throw new RuntimeException(sprintf( - 'Error while analysing file "%s": %s', - $filePath, - $error->getMessage() - ), 0, $error); - } - } - } - - public function fileList(string $path): FileList - { - $cwd = (string)getcwd(); - $absPath = Path::makeAbsolute($path, $cwd); - - $filesystem = $this->filesystem->get('git'); - return $filesystem->fileList()->phpFiles()->within(FilePath::fromString($absPath)); - } -} diff --git a/lib/Extension/WorseReflectionAnalyse/SourceLocator/AnalysedFilesIndex.php b/lib/Extension/WorseReflectionAnalyse/SourceLocator/AnalysedFilesIndex.php deleted file mode 100644 index 6b300f6da1..0000000000 --- a/lib/Extension/WorseReflectionAnalyse/SourceLocator/AnalysedFilesIndex.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ - private array $byName = []; - - public function __construct(private SourceCodeReflector $reflector) - { - } - - public function index(TextDocument $textDocument): void - { - foreach ($this->reflector->reflectClassesIn($textDocument) as $reflectionClass) { - $this->byName[$reflectionClass->name()->full()] = $textDocument; - } - - foreach ($this->reflector->reflectFunctionsIn($textDocument) as $reflectionFunction) { - $this->byName[$reflectionFunction->name()->full()] = $textDocument; - } - } - - public function documentForName(Name $name): ?TextDocument - { - return $this->byName[$name->full()] ?? null; - } -} diff --git a/lib/Extension/WorseReflectionAnalyse/SourceLocator/AnalysedFilesSourceLocator.php b/lib/Extension/WorseReflectionAnalyse/SourceLocator/AnalysedFilesSourceLocator.php deleted file mode 100644 index 29d69dbff3..0000000000 --- a/lib/Extension/WorseReflectionAnalyse/SourceLocator/AnalysedFilesSourceLocator.php +++ /dev/null @@ -1,27 +0,0 @@ -index->documentForName($name)) { - throw new SourceNotFound(sprintf( - 'Class "%s" not found in analysed files', - (string) $name - )); - } - - return $document; - } -} diff --git a/lib/Extension/WorseReflectionAnalyse/Tests/Command/AnalyseCommandTest.php b/lib/Extension/WorseReflectionAnalyse/Tests/Command/AnalyseCommandTest.php deleted file mode 100644 index ff32f98ddb..0000000000 --- a/lib/Extension/WorseReflectionAnalyse/Tests/Command/AnalyseCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ - __DIR__ . '/../../../../..', - ]); - $command = $container->get(AnalyseCommand::class); - assert($command instanceof AnalyseCommand); - - $input = new ArrayInput([ - 'path' => __FILE__, - ]); - $output = new BufferedOutput(); - $exitCode = $command->run($input, $output); - self::assertEquals(0, $exitCode); - } -} diff --git a/lib/Extension/WorseReflectionAnalyse/WorseReflectionAnalyseExtension.php b/lib/Extension/WorseReflectionAnalyse/WorseReflectionAnalyseExtension.php deleted file mode 100644 index 1457f2e26a..0000000000 --- a/lib/Extension/WorseReflectionAnalyse/WorseReflectionAnalyseExtension.php +++ /dev/null @@ -1,56 +0,0 @@ -registerCommands($container); - $this->registerSourceLocator($container); - } - private function registerCommands(ContainerBuilder $container): void - { - $container->register(AnalyseCommand::class, function (Container $container) { - return new AnalyseCommand( - new Analyser( - $container->get(SourceCodeFilesystemExtension::SERVICE_REGISTRY), - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get(AnalysedFilesIndex::class), - ) - ); - }, [ ConsoleExtension::TAG_COMMAND => [ - 'name' => 'worse:analyse', - ]]); - } - - private function registerSourceLocator(ContainerBuilder $container): void - { - $container->register(AnalysedFilesIndex::class, function (Container $container) { - return new AnalysedFilesIndex(ReflectorBuilder::create()->build()); - }); - - $container->register(AnalysedFilesSourceLocator::class, function (Container $container) { - return new AnalysedFilesSourceLocator($container->get(AnalysedFilesIndex::class)); - }, [ WorseReflectionExtension::TAG_SOURCE_LOCATOR => [ - 'priority' => 128, - ]]); - } -} diff --git a/lib/Extension/WorseReflectionExtra/Application/ClassReflector.php b/lib/Extension/WorseReflectionExtra/Application/ClassReflector.php deleted file mode 100644 index 916a08b72d..0000000000 --- a/lib/Extension/WorseReflectionExtra/Application/ClassReflector.php +++ /dev/null @@ -1,121 +0,0 @@ - classToFileConverter - public function __construct( - private ClassFileNormalizer $classFileNormalizer, - private Reflector $reflector - ) { - } - - /** - * Move - guess if moving by class name or file. - */ - public function reflect(string $classOrFile): array - { - $className = $this->classFileNormalizer->normalizeToClass($classOrFile); - $reflection = $this->reflector->reflectClassLike(ClassName::fromString($className)); - - $return = [ - 'class' => (string) $reflection->name(), - 'class_namespace' => (string) $reflection->name()->namespace(), - 'class_name' => (string) $reflection->name()->short(), - 'methods' => [], - 'properties' => [], - 'constants' => [], - ]; - - - foreach ($reflection->methods() as $method) { - assert($method instanceof ReflectionMethod); - $methodInfo = [ - (string) $method->visibility() . ' function ' . $method->name() - ]; - - $return['methods'][$method->name()] = [ - 'name' => $method->name(), - 'abstract' => $method->isAbstract(), - 'visibility' => (string) $method->visibility(), - 'parameters' => [], - 'static' => $method->isStatic() ? 1 : 0 - ]; - - $paramInfos = []; - foreach ($method->parameters() as $parameter) { - $parameterType = $parameter->type(); - // build parameter synopsis - $paramInfo = []; - if (($parameter->type()->isDefined())) { - $paramInfo[] = $parameter->type()->__toString(); - } - $paramInfo[] = '$' . $parameter->name(); - if ($parameter->default()->isDefined()) { - $paramInfo[] = ' = ' . str_replace("\n", '', var_export($parameter->default()->value(), true)); - } - $paramInfos[] = implode(' ', $paramInfo); - - $return['methods'][$method->name()]['parameters'][$parameter->name()] = [ - 'name' => $parameter->name(), - 'has_type' => ($parameter->type()->isDefined()), - 'type' => $parameter->type()->__toString(), - 'has_default' => $parameter->default()->isDefined(), - 'default' => $parameter->default()->value(), - ]; - } - - $methodInfo[] = '(' . implode(', ', $paramInfos) . ')'; - $methodType = $method->returnType(); - - if (($methodType->isDefined())) { - $methodInfo[] = ': ' . $methodType->__toString(); - } - - $return['methods'][$method->name()]['type'] = $methodType->__toString(); - - $return['methods'][$method->name()]['synopsis'] = implode('', $methodInfo); - $return['methods'][$method->name()]['docblock'] = $method->docblock()->formatted(); - } - - if (!$reflection instanceof ReflectionEnum) { - foreach ($reflection->constants() as $constant) { - $return['constants'][$constant->name()] = [ - 'name' => $constant->name() - ]; - } - } - - - if (!$reflection instanceof ReflectionClass) { - return $return; - } - - foreach ($reflection->properties() as $property) { - $propertyType = $property->inferredType(); - $return['properties'][$property->name()] = [ - 'name' => $property->name(), - 'visibility' => (string) $property->visibility(), - 'static' => $property->isStatic() ? 1 : 0, - 'info' => sprintf( - '%s %s $%s', - (string) $property->visibility(), - $propertyType->__toString(), - $property->name() - ), - ]; - } - - return $return; - } -} diff --git a/lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php b/lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php deleted file mode 100644 index bf04af3a11..0000000000 --- a/lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php +++ /dev/null @@ -1,79 +0,0 @@ -filesystemHelper = new FilesystemHelper(); - } - - /** @return array */ - public function infoForOffset(string $sourcePath, int $offset, bool $showFrame = false): array - { - $cwd = getcwd(); - if (false === $cwd) { - throw new RuntimeException('CWD could not be resolved'); - } - $result = $this->reflector->reflectOffset( - TextDocumentBuilder::create( - $this->filesystemHelper->contentsFromFileOrStdin($sourcePath) - )->uri(Path::makeAbsolute($sourcePath, $cwd))->build(), - ByteOffset::fromInt($offset) - ); - - $nodeContext = $result->nodeContext(); - $return = [ - 'symbol' => $nodeContext->symbol()->name(), - 'symbol_type' => $nodeContext->symbol()->symbolType(), - 'start' => $nodeContext->symbol()->position()->start()->toInt(), - 'end' => $nodeContext->symbol()->position()->end()->toInt(), - 'type' => (string) $nodeContext->type(), - 'class_type' => (string) $nodeContext->containerType(), - 'value' => var_export(TypeUtil::valueOrNull($nodeContext->type()), true), - 'offset' => $offset, - 'type_path' => null, - ]; - - if ($showFrame) { - $frame = []; - - foreach (['locals', 'properties'] as $assignmentType) { - foreach ($result->frame()->$assignmentType() as $local) { - $info = sprintf( - '%s = (%s) %s', - $local->name(), - $local->nodeContext()->type(), - str_replace("\n", '', var_export($local->nodeContext()->value(), true)) - ); - - $frame[$assignmentType][$local->offset()->toInt()] = $info; - } - } - $return['frame'] = $frame; - } - - if (false === ($nodeContext->type()->isDefined())) { - return $return; - } - - $return['type_path'] = $nodeContext->type()->isClass() ? $this->classFileNormalizer->classToFile((string) $nodeContext->type(), true) : null; - $return['class_type_path'] = $nodeContext->containerType()->isDefined() && $nodeContext->containerType()->isClass() ? $this->classFileNormalizer->classToFile($return['class_type'], true) : null; - - return $return; - } -} diff --git a/lib/Extension/WorseReflectionExtra/Command/ClassReflectorCommand.php b/lib/Extension/WorseReflectionExtra/Command/ClassReflectorCommand.php deleted file mode 100644 index af68ada4bd..0000000000 --- a/lib/Extension/WorseReflectionExtra/Command/ClassReflectorCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -setDescription('Reflect a given class (path or FQN)'); - $this->addArgument('name', InputArgument::REQUIRED, 'Source path or FQN'); - FormatHandler::configure($this); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - /** @var string $name */ - $name = $input->getArgument('name'); - - $reflection = $this->reflector->reflect($name); - $this->dumperRegistry->get($input->getOption('format'))->dump($output, $reflection); - - return 0; - } -} diff --git a/lib/Extension/WorseReflectionExtra/Command/OffsetInfoCommand.php b/lib/Extension/WorseReflectionExtra/Command/OffsetInfoCommand.php deleted file mode 100644 index 667f01512a..0000000000 --- a/lib/Extension/WorseReflectionExtra/Command/OffsetInfoCommand.php +++ /dev/null @@ -1,45 +0,0 @@ -setDescription('Return information about given file at the given offset'); - $this->addArgument('path', InputArgument::REQUIRED, 'Source path or FQN'); - $this->addArgument('offset', InputArgument::REQUIRED, 'Destination path or FQN'); - $this->addOption('frame', null, InputOption::VALUE_NONE, 'Show inferred frame state at offset'); - FormatHandler::configure($this); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - $info = $this->infoForOffset->infoForOffset( - $input->getArgument('path'), - $input->getArgument('offset'), - $input->getOption('frame') - ); - - $format = $input->getOption('format'); - $this->dumperRegistry->get($format)->dump($output, $info); - - return 0; - } -} diff --git a/lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php b/lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php deleted file mode 100644 index 770acbbb99..0000000000 --- a/lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php +++ /dev/null @@ -1,90 +0,0 @@ -setRequired([ - 'offset', - 'source', - ]); - } - - public function handle(array $arguments) - { - $offset = $this->reflector->reflectOffset( - TextDocumentBuilder::create($arguments['source'])->build(), - ByteOffset::fromInt($arguments['offset']) - ); - - return InformationResponse::fromString(json_encode( - $this->serialize( - $arguments['offset'], - $offset - ), - JSON_PRETTY_PRINT - )); - } - - private function serialize(int $offset, ReflectionOffset $reflectionOffset) - { - $nodeContext = $reflectionOffset->nodeContext(); - - $return = [ - 'symbol' => $nodeContext->symbol()->name(), - 'symbol_type' => $nodeContext->symbol()->symbolType(), - 'start' => $nodeContext->symbol()->position()->start()->toInt(), - 'end' => $nodeContext->symbol()->position()->end()->toInt(), - 'type' => (string) $nodeContext->type(), - 'container_type' => (string) $nodeContext->containerType(), - 'value' => var_export(TypeUtil::valueOrNull($nodeContext->type()), true), - 'offset' => $offset, - 'type_path' => null, - ]; - - $frame = []; - - foreach (['locals', 'properties'] as $assignmentType) { - $assignments = $reflectionOffset->frame()->$assignmentType(); - foreach ($assignments as $local) { - $info = sprintf( - '%s = (%s) %s', - $local->name(), - $local->type(), - str_replace("\n", '', var_export(TypeUtil::valueOrNull($local->type()), true)) - ); - - $frame[$assignmentType][$local->offset()] = $info; - } - } - $return['frame'] = $frame; - - if (false === ($nodeContext->type()->isDefined())) { - return $return; - } - - return $return; - } -} diff --git a/lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php b/lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php deleted file mode 100644 index 29dde1755a..0000000000 --- a/lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php +++ /dev/null @@ -1,69 +0,0 @@ -registerCommands($container); - $this->registerApplicationServices($container); - $this->registerRpc($container); - } - - private function registerApplicationServices(ContainerBuilder $container): void - { - $container->register('application.offset_info', function (Container $container) { - return new OffsetInfo( - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->get('application.helper.class_file_normalizer') - ); - }); - $container->register('application.class_reflector', function (Container $container) { - return new ClassReflector( - $container->get('application.helper.class_file_normalizer'), - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR) - ); - }); - } - - private function registerCommands(ContainerBuilder $container): void - { - $container->register('command.offset_info', function (Container $container) { - return new OffsetInfoCommand( - $container->get('application.offset_info'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'offset:info' ]]); - $container->register('command.class_reflector', function (Container $container) { - return new ClassReflectorCommand( - $container->get('application.class_reflector'), - $container->get('console.dumper_registry') - ); - }, [ ConsoleExtension::TAG_COMMAND => [ 'name' => 'class:reflect' ]]); - } - - private function registerRpc(ContainerBuilder $container): void - { - $container->register('worse_reflection_extra.rpc.handler.offset_info', function (Container $container) { - return new OffsetInfoHandler($container->get(WorseReflectionExtension::SERVICE_REFLECTOR)); - }, [ RpcExtension::TAG_RPC_HANDLER => ['name' => OffsetInfoHandler::NAME] ]); - } -} diff --git a/lib/FilePathResolver/CachingPathResolver.php b/lib/FilePathResolver/CachingPathResolver.php deleted file mode 100644 index fcf437cd1e..0000000000 --- a/lib/FilePathResolver/CachingPathResolver.php +++ /dev/null @@ -1,23 +0,0 @@ -cache[$path])) { - return $this->cache[$path]; - } - - $this->cache[$path] = $this->innerPathResolver->resolve($path); - - return $this->cache[$path]; - } -} diff --git a/lib/FilePathResolver/Exception/UnknownToken.php b/lib/FilePathResolver/Exception/UnknownToken.php deleted file mode 100644 index 69aa81780e..0000000000 --- a/lib/FilePathResolver/Exception/UnknownToken.php +++ /dev/null @@ -1,17 +0,0 @@ -callback = $callback; - } - - public function tokenName(): string - { - return $this->tokenName; - } - - public function replacementValue(): string - { - $closure = $this->callback; - return $closure(); - } -} diff --git a/lib/FilePathResolver/Expander/ValueExpander.php b/lib/FilePathResolver/Expander/ValueExpander.php deleted file mode 100644 index a5d82e72bd..0000000000 --- a/lib/FilePathResolver/Expander/ValueExpander.php +++ /dev/null @@ -1,24 +0,0 @@ -tokenName; - } - - public function replacementValue(): string - { - return $this->value; - } -} diff --git a/lib/FilePathResolver/Expander/Xdg/AbstractXdgExpander.php b/lib/FilePathResolver/Expander/Xdg/AbstractXdgExpander.php deleted file mode 100644 index e8270140c5..0000000000 --- a/lib/FilePathResolver/Expander/Xdg/AbstractXdgExpander.php +++ /dev/null @@ -1,20 +0,0 @@ -name; - } -} diff --git a/lib/FilePathResolver/Expander/Xdg/SuffixExpanderDecorator.php b/lib/FilePathResolver/Expander/Xdg/SuffixExpanderDecorator.php deleted file mode 100644 index bd3309286c..0000000000 --- a/lib/FilePathResolver/Expander/Xdg/SuffixExpanderDecorator.php +++ /dev/null @@ -1,24 +0,0 @@ -innerExpander->tokenName(); - } - - public function replacementValue(): string - { - return $this->innerExpander->replacementValue().$this->suffix; - } -} diff --git a/lib/FilePathResolver/Expander/Xdg/XdgCacheExpander.php b/lib/FilePathResolver/Expander/Xdg/XdgCacheExpander.php deleted file mode 100644 index 722a63d3f9..0000000000 --- a/lib/FilePathResolver/Expander/Xdg/XdgCacheExpander.php +++ /dev/null @@ -1,11 +0,0 @@ -xdg->getHomeCacheDir(); - } -} diff --git a/lib/FilePathResolver/Expander/Xdg/XdgConfigExpander.php b/lib/FilePathResolver/Expander/Xdg/XdgConfigExpander.php deleted file mode 100644 index f83e8f683d..0000000000 --- a/lib/FilePathResolver/Expander/Xdg/XdgConfigExpander.php +++ /dev/null @@ -1,11 +0,0 @@ -xdg->getHomeConfigDir(); - } -} diff --git a/lib/FilePathResolver/Expander/Xdg/XdgDataExpander.php b/lib/FilePathResolver/Expander/Xdg/XdgDataExpander.php deleted file mode 100644 index 38306a655c..0000000000 --- a/lib/FilePathResolver/Expander/Xdg/XdgDataExpander.php +++ /dev/null @@ -1,11 +0,0 @@ -xdg->getHomeDataDir(); - } -} diff --git a/lib/FilePathResolver/Expanders.php b/lib/FilePathResolver/Expanders.php deleted file mode 100644 index 9284be1a63..0000000000 --- a/lib/FilePathResolver/Expanders.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ -class Expanders implements IteratorAggregate -{ - /** - * @var Expander[] - */ - private array $expanders = []; - - /** - * @param Expander[] $expanders - */ - public function __construct(array $expanders) - { - foreach ($expanders as $expander) { - $this->add($expander); - } - } - - /** - * @return array - */ - public function toArray(): array - { - $array = []; - foreach ($this->expanders as $expander) { - $array[$expander->tokenName()] = $expander->replacementValue(); - } - - return $array; - } - - public function get(string $tokenName): Expander - { - if (!isset($this->expanders[$tokenName])) { - throw new UnknownToken($tokenName, array_keys($this->expanders)); - } - - return $this->expanders[$tokenName]; - } - - - public function getIterator(): Traversable - { - return new ArrayIterator($this->expanders); - } - - private function add(Expander $expander): void - { - $this->expanders[$expander->tokenName()] = $expander; - } -} diff --git a/lib/FilePathResolver/Filter.php b/lib/FilePathResolver/Filter.php deleted file mode 100644 index 26d13d9d79..0000000000 --- a/lib/FilePathResolver/Filter.php +++ /dev/null @@ -1,8 +0,0 @@ -expanders->get($match); - $path = str_replace('%' . $match . '%', $expander->replacementValue(), $path); - } - - return $path; - } -} diff --git a/lib/FilePathResolver/FilteringPathResolver.php b/lib/FilePathResolver/FilteringPathResolver.php deleted file mode 100644 index 21ddfdd7ba..0000000000 --- a/lib/FilePathResolver/FilteringPathResolver.php +++ /dev/null @@ -1,22 +0,0 @@ -filters as $filter) { - $path = $filter->apply($path); - } - - return $path; - } -} diff --git a/lib/FilePathResolver/LoggingPathResolver.php b/lib/FilePathResolver/LoggingPathResolver.php deleted file mode 100644 index e364567b95..0000000000 --- a/lib/FilePathResolver/LoggingPathResolver.php +++ /dev/null @@ -1,31 +0,0 @@ -pathResolver->resolve($path); - $this->logger->log( - $this->level, - sprintf( - 'Resolved path "%s" to "%s"', - $path, - $resolvedPath - ) - ); - - return $resolvedPath; - } -} diff --git a/lib/FilePathResolver/PathResolver.php b/lib/FilePathResolver/PathResolver.php deleted file mode 100644 index eb55fc8978..0000000000 --- a/lib/FilePathResolver/PathResolver.php +++ /dev/null @@ -1,8 +0,0 @@ -tokenExpander = new TokenExpandingFilter($expanders); - } - - public function benchExpandTokenizedString(): void - { - $this->tokenExpander->apply('%a%/%b%/%c%/%d%/%e%'); - } - - public function benchExpandStringWithNoTokens(): void - { - $this->tokenExpander->apply('a/b/c/d/e'); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/CachingPathResolverTest.php b/lib/FilePathResolver/Tests/Unit/CachingPathResolverTest.php deleted file mode 100644 index 234c032029..0000000000 --- a/lib/FilePathResolver/Tests/Unit/CachingPathResolverTest.php +++ /dev/null @@ -1,35 +0,0 @@ - - */ - private ObjectProphecy $resolver; - - public function setUp(): void - { - $this->resolver = $this->prophesize(PathResolver::class); - } - - public function testCachesResult(): void - { - $caching = new CachingPathResolver($this->resolver->reveal()); - $this->resolver->resolve('foo')->willReturn('bar')->shouldBeCalledOnce(); - - $caching->resolve('foo'); - $caching->resolve('foo'); - $caching->resolve('foo'); - $this->assertEquals('bar', $caching->resolve('foo')); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/Expander/CallbackExpanderTest.php b/lib/FilePathResolver/Tests/Unit/Expander/CallbackExpanderTest.php deleted file mode 100644 index ddb965a6e2..0000000000 --- a/lib/FilePathResolver/Tests/Unit/Expander/CallbackExpanderTest.php +++ /dev/null @@ -1,21 +0,0 @@ -assertEquals('bar', $this->expand('%foo%')); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/Expander/ExpanderTestCase.php b/lib/FilePathResolver/Tests/Unit/Expander/ExpanderTestCase.php deleted file mode 100644 index 8b26a45075..0000000000 --- a/lib/FilePathResolver/Tests/Unit/Expander/ExpanderTestCase.php +++ /dev/null @@ -1,18 +0,0 @@ -createExpander() ])))->apply($path); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/Expander/ValueExpanderTest.php b/lib/FilePathResolver/Tests/Unit/Expander/ValueExpanderTest.php deleted file mode 100644 index d7816be3ec..0000000000 --- a/lib/FilePathResolver/Tests/Unit/Expander/ValueExpanderTest.php +++ /dev/null @@ -1,19 +0,0 @@ -assertEquals('/foo/value/bar', $this->expand('/foo/%test%/bar')); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/Expander/Xdg/SuffixExpanderDecoratorTest.php b/lib/FilePathResolver/Tests/Unit/Expander/Xdg/SuffixExpanderDecoratorTest.php deleted file mode 100644 index 74f3985f8a..0000000000 --- a/lib/FilePathResolver/Tests/Unit/Expander/Xdg/SuffixExpanderDecoratorTest.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ - private ObjectProphecy $expander; - - public function setUp(): void - { - $this->expander = $this->prophesize(Expander::class); - } - - public function createExpander(): Expander - { - return new SuffixExpanderDecorator($this->expander->reveal(), '/foo'); - } - - public function testAddsSuffixToInnerExpanderValue(): void - { - $this->expander->tokenName()->willReturn('bar'); - $this->expander->replacementValue()->willReturn('bar'); - $this->assertEquals('/bar/foo', $this->expand('/%bar%')); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/Expander/Xdg/XdgExpanderTest.php b/lib/FilePathResolver/Tests/Unit/Expander/Xdg/XdgExpanderTest.php deleted file mode 100644 index e133b91d98..0000000000 --- a/lib/FilePathResolver/Tests/Unit/Expander/Xdg/XdgExpanderTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ - private ObjectProphecy $xdg; - - public function setUp(): void - { - $this->xdg = $this->prophesize(Xdg::class); - $this->xdg->getHomeDataDir()->willReturn('/home/data'); - $this->xdg->getHomeConfigDir()->willReturn('/home/config'); - $this->xdg->getHomeCacheDir()->willReturn('/home/cache'); - - $this->expander = new TokenExpandingFilter(new Expanders([ - new XdgCacheExpander('cache', $this->xdg->reveal()), - new XdgDataExpander('data', $this->xdg->reveal()), - new XdgConfigExpander('config', $this->xdg->reveal()), - ])); - } - - public function testExpandXdgDirs(): void - { - $this->assertEquals('/home/cache/foo', $this->expander->apply('%cache%/foo')); - $this->assertEquals('/home/data/foo', $this->expander->apply('%data%/foo')); - $this->assertEquals('/home/config/foo', $this->expander->apply('%config%/foo')); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/ExpandersTest.php b/lib/FilePathResolver/Tests/Unit/ExpandersTest.php deleted file mode 100644 index ccabc042c9..0000000000 --- a/lib/FilePathResolver/Tests/Unit/ExpandersTest.php +++ /dev/null @@ -1,35 +0,0 @@ -assertEquals([ - 'foo' => 'bar', - 'bar' => 'foo', - ], $expanders->toArray()); - } - - public function testThrowsExceptionIfUnknownTokenFound(): void - { - $this->expectException(UnknownToken::class); - $expanders = new Expanders([ - new ValueExpander('foo', 'bar'), - new ValueExpander('bar', 'foo'), - ]); - - $expanders->get('baz'); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/Filter/CanonicalizingPathFilterTest.php b/lib/FilePathResolver/Tests/Unit/Filter/CanonicalizingPathFilterTest.php deleted file mode 100644 index 3dfa4c6add..0000000000 --- a/lib/FilePathResolver/Tests/Unit/Filter/CanonicalizingPathFilterTest.php +++ /dev/null @@ -1,19 +0,0 @@ -assertEquals('/bar', $this->apply('/foo/bar/../../bar')); - } - - protected function createFilter(): Filter - { - return new CanonicalizingPathFilter(); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/Filter/FilterTestCase.php b/lib/FilePathResolver/Tests/Unit/Filter/FilterTestCase.php deleted file mode 100644 index b633d7406e..0000000000 --- a/lib/FilePathResolver/Tests/Unit/Filter/FilterTestCase.php +++ /dev/null @@ -1,17 +0,0 @@ -createFilter() ]))->resolve($path); - } - - abstract protected function createFilter(): Filter; -} diff --git a/lib/FilePathResolver/Tests/Unit/Filter/TokenExpandingFilterTest.php b/lib/FilePathResolver/Tests/Unit/Filter/TokenExpandingFilterTest.php deleted file mode 100644 index 00db7d9211..0000000000 --- a/lib/FilePathResolver/Tests/Unit/Filter/TokenExpandingFilterTest.php +++ /dev/null @@ -1,51 +0,0 @@ -assertEquals('/foo', $this->create()->apply('/foo')); - } - - public function testAppliesExpanders(): void - { - $expander1 = $this->prophesize(Expander::class); - $expander2 = $this->prophesize(Expander::class); - $expander3 = $this->prophesize(Expander::class); - - $expander1->tokenName()->willReturn('foo'); - $expander1->replacementValue()->willReturn('baz'); - - $expander2->tokenName()->willReturn('zed'); - $expander2->replacementValue()->shouldNotBeCalled(); - - $expander3->tokenName()->willReturn('bar'); - $expander3->replacementValue()->willReturn('fab'); - - $path = $this->create([ - $expander1->reveal(), - $expander2->reveal(), - $expander3->reveal(), - ])->apply('/start/%foo%/%bar%/end'); - - $this->assertEquals('/start/baz/fab/end', $path); - } - - /** - * @param Expander[] $expanders - */ - private function create(array $expanders = []): TokenExpandingFilter - { - return new TokenExpandingFilter(new Expanders($expanders)); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/FilteringPathResolverTest.php b/lib/FilePathResolver/Tests/Unit/FilteringPathResolverTest.php deleted file mode 100644 index 214a50f376..0000000000 --- a/lib/FilePathResolver/Tests/Unit/FilteringPathResolverTest.php +++ /dev/null @@ -1,37 +0,0 @@ -assertInstanceOf(PathResolver::class, $resolver); - $this->assertEquals('/foo/bar', $resolver->resolve('/foo/bar')); - } - - public function testAppliesFilters(): void - { - $filter1 = $this->prophesize(Filter::class); - $filter2 = $this->prophesize(Filter::class); - - $filter1->apply('foo')->willReturn('bar'); - $filter2->apply('bar')->willReturn('baz'); - - $resolver = new FilteringPathResolver([ - $filter1->reveal(), - $filter2->reveal() - ]); - - $this->assertEquals('baz', $resolver->resolve('foo')); - } -} diff --git a/lib/FilePathResolver/Tests/Unit/LoggingPathResolverTest.php b/lib/FilePathResolver/Tests/Unit/LoggingPathResolverTest.php deleted file mode 100644 index 6b5798c868..0000000000 --- a/lib/FilePathResolver/Tests/Unit/LoggingPathResolverTest.php +++ /dev/null @@ -1,33 +0,0 @@ -prophesize(PathResolver::class); - $logger = $this->prophesize(LoggerInterface::class); - $innerResolver->resolve('foo')->willReturn('bar'); - - $resolver = new LoggingPathResolver( - $innerResolver->reveal(), - $logger->reveal() - ); - - $this->assertEquals( - 'bar', - $resolver->resolve('foo') - ); - - $logger->log('debug', 'Resolved path "foo" to "bar"')->shouldHaveBeenCalled(); - } -} diff --git a/lib/Filesystem/Adapter/Composer/ComposerFileListProvider.php b/lib/Filesystem/Adapter/Composer/ComposerFileListProvider.php deleted file mode 100644 index a03320013f..0000000000 --- a/lib/Filesystem/Adapter/Composer/ComposerFileListProvider.php +++ /dev/null @@ -1,92 +0,0 @@ -classLoader->getPrefixes(), - $this->classLoader->getPrefixesPsr4(), - $this->classLoader->getClassMap(), - $this->classLoader->getFallbackDirs(), - $this->classLoader->getFallbackDirsPsr4() - ); - - $appendIterator = new AppendIterator(); - $files = []; - $seenPaths = []; - $count = 0; - foreach ($prefixes as $paths) { - $paths = (array) $paths; - foreach ($paths as $path) { - $path = Path::canonicalize($path); - - if (false === file_exists($path)) { - continue; - } - - if (is_file($path)) { - if (isset($files[$path])) { - continue; - } - - $files[$path] = new SplFileInfo($path); - continue; - } - - // do not add a directory iterator if a parent directory - // has already been iterated. - // - // TODO: This could be more efficient. - foreach ($seenPaths as $seenPath) { - if (str_starts_with($path, $seenPath)) { - continue 2; - } - } - - $iterator = $this->createFileIterator( - $this->path->makeAbsoluteFromString($path) - ); - - $appendIterator->append($iterator); - - $seenPaths[$path] = $path; - } - } - - if ($files) { - $appendIterator->append(new ArrayIterator(array_values($files))); - } - - return FileList::fromIterator($appendIterator); - } - - private function createFileIterator(string $path): Iterator - { - $path = $path ? $this->path->makeAbsoluteFromString($path) : $this->path->path(); - $files = new RecursiveDirectoryIterator($path); - $files = new RecursiveIteratorIterator($files); - - return $files; - } -} diff --git a/lib/Filesystem/Adapter/Composer/ComposerFilesystem.php b/lib/Filesystem/Adapter/Composer/ComposerFilesystem.php deleted file mode 100644 index dc59eb61ff..0000000000 --- a/lib/Filesystem/Adapter/Composer/ComposerFilesystem.php +++ /dev/null @@ -1,15 +0,0 @@ -path = $path; - - if (false === file_exists($path->__toString().'/.git')) { - throw new NotSupported( - 'The cwd does not seem to be a git repository root (could not find .git folder)' - ); - } - } - - - public function fileList(): FileList - { - $gitFiles = $this->exec([ - 'ls-files', - '--cached', - '--others', - '--exclude-standard' - ]); - $files = []; - - foreach (explode("\n", $gitFiles) as $gitFile) { - $files[] = new SplFileInfo((string) $this->path->makeAbsoluteFromString($gitFile)); - } - - return FileList::fromIterator(new ArrayIterator($files)); - } - - public function remove(FilePath|string $path): void - { - $path = FilePath::fromFilePathOrString($path); - if (false === $this->trackedByGit($path)) { - parent::remove($path); - return; - } - - if ($path->isDirectory()) { - $this->exec(['rm', '-r', '-f', $path->path()]); - return; - } - - $this->exec(['rm', '-f', $path->path()]); - } - - public function move(FilePath|string $srcPath, FilePath|string $destPath): void - { - $srcPath = FilePath::fromFilePathOrString($srcPath); - $destPath = FilePath::fromFilePathOrString($destPath); - - if (false === $this->trackedByGit($srcPath)) { - parent::move($srcPath, $destPath); - return; - } - - $this->exec([ - 'mv', - $srcPath->path(), - $destPath->path() - ]); - } - - public function copy(FilePath|string $srcPath, FilePath|string $destPath): CopyReport - { - $srcPath = FilePath::fromFilePathOrString($srcPath); - $destPath = FilePath::fromFilePathOrString($destPath); - $list = parent::copy($srcPath, $destPath); - $this->exec(['add', $destPath->__toString()]); - - return $list; - } - - public function createPath(string $path): FilePath - { - return $this->path->makeAbsoluteFromString($path); - } - - /** - * @param array $cmd - */ - private function exec(array $cmd): string - { - $process = new Process(array_merge(['git'], $cmd), $this->path); - $process->run(); - - if ($process->getExitCode() !== 0) { - throw new InvalidArgumentException(sprintf( - 'Could not execute git command "%s", exit code "%s", output "%s"', - implode(' ', $cmd), - $process->getExitCode(), - $process->getOutput() - )); - } - - return $process->getOutput(); - } - - private function trackedByGit(FilePath $file): bool - { - $out = $this->exec(['ls-files', (string) $file]); - - return !empty($out); - } -} diff --git a/lib/Filesystem/Adapter/Simple/SimpleFileListProvider.php b/lib/Filesystem/Adapter/Simple/SimpleFileListProvider.php deleted file mode 100644 index d78b335ee5..0000000000 --- a/lib/Filesystem/Adapter/Simple/SimpleFileListProvider.php +++ /dev/null @@ -1,51 +0,0 @@ -createFileIterator( - $this->path->uriAsString() - ) - ); - } - - private function createFileIterator(string $path): Iterator - { - $path = $path ? $this->path->makeAbsoluteFromString($path)->uriAsString() : $this->path->uriAsString(); - $flags = - FilesystemIterator::KEY_AS_PATHNAME | - FilesystemIterator::CURRENT_AS_FILEINFO | - FilesystemIterator::SKIP_DOTS; - - if ($this->followSymlinks) { - $flags = $flags | FilesystemIterator::FOLLOW_SYMLINKS; - } - - $files = new RecursiveDirectoryIterator($path, $flags); - $files = new RecursiveIteratorIterator( - $files, - RecursiveIteratorIterator::LEAVES_ONLY, - RecursiveIteratorIterator::CATCH_GET_CHILD - ); - - return $files; - } -} diff --git a/lib/Filesystem/Adapter/Simple/SimpleFilesystem.php b/lib/Filesystem/Adapter/Simple/SimpleFilesystem.php deleted file mode 100644 index 8b4ad8ce92..0000000000 --- a/lib/Filesystem/Adapter/Simple/SimpleFilesystem.php +++ /dev/null @@ -1,131 +0,0 @@ -fileListProvider = $fileListProvider ?? new SimpleFileListProvider($this->path); - } - - public function fileList(): FileList - { - return $this->fileListProvider->fileList(); - } - - public function remove(FilePath|string $path): void - { - $path = FilePath::fromFilePathOrString($path); - $this->filesystem->remove($path); - } - - public function move(FilePath|string $srcLocation, FilePath|string $destPath): void - { - $srcLocation = FilePath::fromFilePathOrString($srcLocation); - $destPath = FilePath::fromFilePathOrString($destPath); - - $this->makeDirectoryIfNotExists((string) $destPath); - $this->filesystem->rename($srcLocation->__toString(), $destPath->__toString()); - } - - public function copy(FilePath|string $srcLocation, FilePath|string $destPath): CopyReport - { - $srcLocation = FilePath::fromFilePathOrString($srcLocation); - $destPath = FilePath::fromFilePathOrString($destPath); - - if ($srcLocation->isDirectory()) { - return $this->copyDirectory($srcLocation, $destPath); - } - - $this->makeDirectoryIfNotExists((string) $destPath); - $this->filesystem->copy($srcLocation->__toString(), $destPath->__toString()); - - return CopyReport::fromSrcAndDestFiles( - FileList::fromFilePaths([ $srcLocation ]), - FileList::fromFilePaths([ $destPath ]) - ); - } - - public function createPath(string $path): FilePath - { - if (Path::isRelative($path)) { - return FilePath::fromParts([$this->path->path(), $path]); - } - - return FilePath::fromString($path); - } - - public function getContents(FilePath|string $path): string - { - $path = FilePath::fromFilePathOrString($path); - $contents = file_get_contents($path->path()); - - if (false === $contents) { - throw new RuntimeException('Could not file_get_contents'); - } - - return $contents; - } - - public function writeContents(FilePath|string $path, string $contents): void - { - $path = FilePath::fromFilePathOrString($path); - file_put_contents($path->path(), $contents); - } - - public function exists(FilePath|string $path): bool - { - $path = FilePath::fromFilePathOrString($path); - return file_exists($path); - } - - private function makeDirectoryIfNotExists(string $destPath): void - { - if (file_exists(dirname($destPath))) { - return; - } - - $this->filesystem->mkdir(dirname($destPath), 0777); - } - - private function copyDirectory(FilePath $srcLocation, FilePath $destPath): CopyReport - { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($srcLocation->path(), RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::SELF_FIRST - ); - - $destFiles = []; - $srcFiles = []; - foreach ($iterator as $file) { - $filePath = $destPath->path() . '/' . $iterator->getSubPathName(); - if ($file->isDir()) { - continue; - } - - $this->filesystem->copy($file, $filePath); - - $srcFiles[] = FilePath::fromString($file); - $destFiles[] = FilePath::fromString($filePath); - } - - return CopyReport::fromSrcAndDestFiles(FileList::fromFilePaths($srcFiles), FileList::fromFilePaths($destFiles)); - } -} diff --git a/lib/Filesystem/Domain/ChainFileListProvider.php b/lib/Filesystem/Domain/ChainFileListProvider.php deleted file mode 100644 index 68c478337a..0000000000 --- a/lib/Filesystem/Domain/ChainFileListProvider.php +++ /dev/null @@ -1,38 +0,0 @@ -add($provider); - } - } - - public function fileList(): FileList - { - $iterator = new AppendIterator(); - foreach ($this->providers as $provider) { - $iterator->append($provider->fileList()->getSplFileInfoIterator()); - } - - return FileList::fromIterator($iterator); - } - - private function add(FileListProvider $provider): void - { - $this->providers[] = $provider; - } -} diff --git a/lib/Filesystem/Domain/CopyReport.php b/lib/Filesystem/Domain/CopyReport.php deleted file mode 100644 index f4f4794053..0000000000 --- a/lib/Filesystem/Domain/CopyReport.php +++ /dev/null @@ -1,27 +0,0 @@ -srcFiles; - } - - public function destFiles(): FileList - { - return $this->destFiles; - } -} diff --git a/lib/Filesystem/Domain/Exception/FilesystemNotFound.php b/lib/Filesystem/Domain/Exception/FilesystemNotFound.php deleted file mode 100644 index d563e8dabb..0000000000 --- a/lib/Filesystem/Domain/Exception/FilesystemNotFound.php +++ /dev/null @@ -1,9 +0,0 @@ -registry->has($name)) { - return $this->registry->get($this->fallback); - } - - return $this->registry->get($name); - } - - public function has(string $name): bool - { - return $this->registry->has($name); - } - - public function names(): array - { - return $this->registry->names(); - } -} diff --git a/lib/Filesystem/Domain/FileList.php b/lib/Filesystem/Domain/FileList.php deleted file mode 100644 index be2e00e557..0000000000 --- a/lib/Filesystem/Domain/FileList.php +++ /dev/null @@ -1,229 +0,0 @@ - - */ -class FileList implements Iterator -{ - private int $key = 0; - - /** - * @param Iterator $iterator - */ - private function __construct(private Iterator $iterator) - { - } - - public static function fromIterator(Iterator $iterator): self - { - return new self($iterator); - } - - /** - * @param string[] $filePaths - */ - public static function fromFilePaths(array $filePaths): self - { - $files = []; - foreach ($filePaths as $filePath) { - $files[] = new SplFileInfo($filePath); - } - - return new self(new ArrayIterator($files)); - } - - /** - * @return Iterator - */ - public function getSplFileInfoIterator(): Traversable - { - return $this->iterator; - } - - public function contains(FilePath $path): bool - { - foreach ($this as $filePath) { - if ($path == $filePath) { - return true; - } - } - - return false; - } - - public function phpFiles(): self - { - return $this->byExtensions(['php']); - } - - /** - * @param list $extensions - */ - public function byExtensions(array $extensions): self - { - return new self((function () use ($extensions) { - foreach ($this as $filePath) { - if (!in_array($filePath->extension(), $extensions, true)) { - continue; - } - - yield $filePath->asSplFileInfo(); - } - })()); - } - - /** - * @param string[] $includePatterns - * @param string[] $excludePatterns - */ - public function includeAndExclude(array $includePatterns = [], array $excludePatterns = []): self - { - $inclusionMap = []; - if ($includePatterns === []) { - $inclusionMap['/**/*'] = true; - } - - foreach ($includePatterns as $includePattern) { - $inclusionMap[$includePattern] = true; - } - foreach ($excludePatterns as $excludePattern) { - $inclusionMap[$excludePattern] = false; - } - - // Sort map by keys so that more specific paths are getting matched first - uksort($inclusionMap, function (string $a, string $b) { - $aIsDynamic = Glob::isDynamic($a); - $bIsDynamic = Glob::isDynamic($b); - if ($aIsDynamic !== $bIsDynamic) { - return $aIsDynamic ? 1 : -1; - } - - $partsA = explode(DIRECTORY_SEPARATOR, $a); - $partsB = explode(DIRECTORY_SEPARATOR, $b); - $countDiff = count($partsA) <=> count($partsB); - if ($countDiff !== 0) { - // Longer paths should come first - return -$countDiff; - } - - foreach ($partsA as $i => $pathPartA) { - if ($pathPartA === '**' || $pathPartA === '*') { - return 1; - } - - $compare = strcmp($pathPartA, $partsB[$i]); - if ($compare !== 0) { - return $compare; - } - } - // If none of the path segments were different, then they must be equal - return 0; - }); - - return $this->filter(function (SplFileInfo $info) use ($inclusionMap): bool { - foreach ($inclusionMap as $glob => $isIncluded) { - $path = $info->getPathname(); - - // do not include the scheme in comparisons - if ($schemePos = strpos($path, '://')) { - $path = substr($path, $schemePos + 3); - } - - if (Glob::match($path, $glob)) { - return $isIncluded; - } - } - - return false; - }); - } - - public function within(FilePath $path): self - { - return new self(new RegexIterator($this->iterator, sprintf( - '{^%s/.*}', - (string) preg_quote($path) - ))); - } - - public function named(string $name): self - { - return new self(new RegexIterator($this->iterator, sprintf( - '{/%s$}', - preg_quote($name) - ))); - } - - /** - * @param Closure(SplFileInfo): bool $closure - */ - public function filter(Closure $closure): self - { - return new self(new CallbackFilterIterator($this->iterator, $closure)); - } - - public function existing(): self - { - return new self(new CallbackFilterIterator($this->iterator, function (SplFileInfo $file) { - return file_exists($file->__toString()); - })); - } - - public function rewind(): void - { - $this->iterator->rewind(); - } - - #[ReturnTypeWillChange] - public function current() - { - $current = $this->iterator->current(); - - return FilePath::fromSplFileInfo($current); - } - - #[ReturnTypeWillChange] - public function key() - { - return $this->key++; - } - - public function next(): void - { - $this->iterator->next(); - } - - public function valid(): bool - { - return $this->iterator->valid(); - } - - /** - * @return self - */ - public function containingString(string $string): self - { - return $this->filter(function (SplFileInfo $info) use ($string) { - $contents = @file_get_contents($info->getPathname()); - - if (false === $contents) { - return false; - } - - return str_contains($contents, $string); - }); - } -} diff --git a/lib/Filesystem/Domain/FileListProvider.php b/lib/Filesystem/Domain/FileListProvider.php deleted file mode 100644 index e74e8b5856..0000000000 --- a/lib/Filesystem/Domain/FileListProvider.php +++ /dev/null @@ -1,8 +0,0 @@ -uri->path(); - } - - public function uriAsString(): string - { - return $this->uri->__toString(); - } - - public static function fromString(string $string): FilePath - { - $textDocumentUri = TextDocumentUri::fromString($string); - return new self($textDocumentUri); - } - - /** - * @param array $parts - */ - public static function fromParts(array $parts): FilePath - { - $path = Path::join(...$parts); - if (!Path::isAbsolute($path)) { - // Not sure if this makes sense… Maybe it should just throw? - $path = Path::makeAbsolute($path, '/'); - } - - return self::fromString($path); - } - - public static function fromSplFileInfo(SplFileInfo $fileInfo): FilePath - { - return self::fromString((string) $fileInfo); - } - - public static function fromFilePathOrString(FilePath|string $path): FilePath - { - if ($path instanceof FilePath) { - return $path; - } - - if (is_string($path)) { - return self::fromString($path); - } - } - - public function isDirectory(): bool - { - return is_dir($this->uri->path()); - } - - public function asSplFileInfo(): SplFileInfo - { - if ($this->uri->scheme() === 'file') { - return new SplFileInfo($this->uri->path()); - } - return new SplFileInfo($this->uri->__toString()); - } - - public function makeAbsoluteFromString(string $path): FilePath - { - if (Path::isAbsolute($path)) { - $path = self::fromString($path); - - if (false === $path->isWithinOrSame($this)) { - throw new RuntimeException(sprintf( - 'Trying to create descendant from absolute path "%s" that does not lie within context path "%s"', - (string) $path, - (string) $this - )); - } - - return $path; - } - - return self::fromParts([(string) $this, $path]); - } - - public function extension(): string - { - return Path::getExtension($this->uri->path()); - } - - public function isWithin(FilePath $path): bool - { - return Path::isBasePath($path->path(), $this->path()); - } - - public function isWithinOrSame(FilePath $path): bool - { - if ($this->path() == $path->path()) { - return true; - } - - return $this->isWithin($path); - } - - public function isNamed(string $name): bool - { - return basename($this->path()) == $name; - } - - public function path(): string - { - return $this->uri->path(); - } -} diff --git a/lib/Filesystem/Domain/Filesystem.php b/lib/Filesystem/Domain/Filesystem.php deleted file mode 100644 index bb6c82286b..0000000000 --- a/lib/Filesystem/Domain/Filesystem.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ - public function names(): array; -} diff --git a/lib/Filesystem/Domain/MappedFilesystemRegistry.php b/lib/Filesystem/Domain/MappedFilesystemRegistry.php deleted file mode 100644 index 91cd03cbb2..0000000000 --- a/lib/Filesystem/Domain/MappedFilesystemRegistry.php +++ /dev/null @@ -1,47 +0,0 @@ - */ - private array $filesystems = []; - - /** @param array $filesystemMap */ - public function __construct(array $filesystemMap) - { - foreach ($filesystemMap as $name => $filesystem) { - $this->add($name, $filesystem); - } - } - - public function get(string $name): Filesystem - { - if (!isset($this->filesystems[$name])) { - throw new FilesystemNotFound(sprintf( - 'Unknown filesystem "%s", known filesystems "%s"', - $name, - implode('", "', array_keys($this->filesystems)) - )); - } - - return $this->filesystems[$name]; - } - - public function has(string $name): bool - { - return isset($this->filesystems[$name]); - } - - public function names(): array - { - return array_keys($this->filesystems); - } - - private function add(string $name, Filesystem $filesystem): void - { - $this->filesystems[$name] = $filesystem; - } -} diff --git a/lib/Filesystem/Tests/Adapter/AdapterTestCase.php b/lib/Filesystem/Tests/Adapter/AdapterTestCase.php deleted file mode 100644 index 40ecffc210..0000000000 --- a/lib/Filesystem/Tests/Adapter/AdapterTestCase.php +++ /dev/null @@ -1,106 +0,0 @@ -initWorkspace(); - $this->loadProject(); - } - - public function testFind(): void - { - $fileList = $this->filesystem()->fileList(); - $this->assertTrue($fileList->contains($this->filesystem()->createPath('src/Foobar.php'))); - - $location = $this->filesystem()->createPath('src/Hello/Goodbye.php'); - $foo = $fileList->contains($location); - $this->assertTrue($foo); - } - - public function testRemove(): void - { - $file = $this->filesystem()->createPath('src/Hello/Goodbye.php'); - $this->assertTrue(file_exists($file->path())); - $this->filesystem()->remove($file); - $this->assertFalse(file_exists($file->path())); - } - - public function testRemoveDirectory(): void - { - $file = $this->filesystem()->createPath('src/Hello'); - $this->assertTrue(file_exists($file->path())); - $this->filesystem()->remove($file); - $this->assertFalse(file_exists($file->path())); - } - - public function testMove(): void - { - $srcLocation = $this->filesystem()->createPath('src/Hello/Goodbye.php'); - $destLocation = $this->filesystem()->createPath('src/Hello/Hello.php'); - - $this->filesystem()->move($srcLocation, $destLocation); - $this->assertTrue(file_exists($destLocation->path())); - $this->assertFalse(file_exists($srcLocation->path())); - } - - public function testMoveDirectory(): void - { - $srcLocation = $this->filesystem()->createPath('src/Hello'); - $destLocation = $this->filesystem()->createPath('src/Goodbye'); - - $this->filesystem()->move($srcLocation, $destLocation); - $this->assertTrue(file_exists($destLocation->path())); - $this->assertFalse(file_exists($srcLocation->path())); - - $testFile = $this->filesystem()->createPath('src/Goodbye/Goodbye.php'); - $this->assertTrue(file_exists($testFile->path())); - } - - public function testCopy(): void - { - $srcLocation = $this->filesystem()->createPath('src/Hello/Goodbye.php'); - $destLocation = $this->filesystem()->createPath('src/Hello/Hello.php'); - - $this->filesystem()->copy($srcLocation, $destLocation); - $this->assertTrue(file_exists($destLocation->path())); - $this->assertTrue(file_exists($srcLocation->path())); - } - - public function testExists(): void - { - $path = $this->filesystem()->createPath('src/Hello/Goodbye.php'); - $this->assertTrue($this->filesystem()->exists($path)); - - $path = $this->filesystem()->createPath('src/Hello/Plop.php'); - $this->assertFalse($this->filesystem()->exists($path)); - } - - public function testCopyRecursive(): void - { - $srcLocation = $this->filesystem()->createPath('src'); - $destLocation = $this->filesystem()->createPath('src/AAAn'); - - $list = $this->filesystem()->copy($srcLocation, $destLocation); - $this->assertTrue(file_exists($destLocation->path())); - $this->assertTrue(file_exists($srcLocation->path())); - $this->assertTrue(file_exists($srcLocation->path() . '/AAAn/Foobar.php')); - $this->assertTrue(file_exists($srcLocation->path() . '/AAAn/Hello/Goodbye.php')); - $this->assertCount(2, $list->srcFiles()); - $this->assertCount(2, $list->destFiles()); - } - - public function testWriteGet(): void - { - $path = $this->filesystem()->createPath('src/Hello/Goodbye.php'); - - $this->filesystem()->writeContents($path, 'foo'); - $this->assertEquals('foo', $this->filesystem()->getContents($path)); - } - - abstract protected function filesystem(): Filesystem; -} diff --git a/lib/Filesystem/Tests/Adapter/Composer/ComposerFilesystemTest.php b/lib/Filesystem/Tests/Adapter/Composer/ComposerFilesystemTest.php deleted file mode 100644 index 5d31be5ea5..0000000000 --- a/lib/Filesystem/Tests/Adapter/Composer/ComposerFilesystemTest.php +++ /dev/null @@ -1,41 +0,0 @@ -workspacePath()); - exec('composer dumpautoload --quiet'); - } - - public function testClassmap(): void - { - $fileList = $this->filesystem()->fileList(); - $location = $this->filesystem()->createPath('src/Hello/Goodbye.php'); - $fileList = $fileList->named('DB.php'); - $this->assertCount(1, $fileList); - - foreach ($fileList as $file) { - $this->assertInstanceOf(FilePath::class, $file); - } - } - - protected function filesystem(): Filesystem - { - static $classLoader; - - if (!$classLoader) { - $classLoader = require 'vendor/autoload.php'; - } - - return new ComposerFilesystem(FilePath::fromString($this->workspacePath()), $classLoader); - } -} diff --git a/lib/Filesystem/Tests/Adapter/Git/GitFilesystemTest.php b/lib/Filesystem/Tests/Adapter/Git/GitFilesystemTest.php deleted file mode 100644 index 0810d66b4d..0000000000 --- a/lib/Filesystem/Tests/Adapter/Git/GitFilesystemTest.php +++ /dev/null @@ -1,81 +0,0 @@ -workspacePath()); - exec('git init'); - exec('git add *'); - } - - /** - * It sohuld throw an exception if the cwd does not have a .git folder. - */ - public function testNoGitFolder(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The cwd does not seem to be'); - new GitFilesystem(FilePath::fromString(__DIR__)); - } - - /** - * It should fallback to simple filesystem if file is not under VC. - */ - public function testMoveNonVersionedFile(): void - { - touch($this->workspacePath() . '/Test.php'); - $this->filesystem()->move( - FilePath::fromString($this->workspacePath() . '/Test.php'), - FilePath::fromString($this->workspacePath() . '/Foobar.php') - ); - self::assertFileExists($this->workspacePath() . '/Foobar.php'); - self::assertFileDoesNotExist($this->workspacePath() . '/Test.php'); - } - - public function testMoveNonVersionedFileToNonExistingDirectory(): void - { - touch($this->workspacePath() . '/Test.php'); - $this->filesystem()->move( - FilePath::fromString($this->workspacePath() . '/Test.php'), - FilePath::fromString($this->workspacePath() . '/NotExisting/Foobar.php') - ); - self::assertFileDoesNotExist($this->workspacePath() . '/Test.php'); - self::assertFileExists($this->workspacePath() . '/NotExisting/Foobar.php'); - } - - /** - * It should fallback to simple filesystem if file is not under VC. - */ - public function testRemoveNonVersionedFile(): void - { - touch($this->workspacePath() . '/Test.php'); - $this->filesystem()->remove(FilePath::fromString($this->workspacePath() . '/Test.php')); - self::assertFileDoesNotExist($this->workspacePath() . '/Test.php'); - } - - /** - * It lists untracked files - */ - public function testListUntracked(): void - { - $path = $this->workspacePath() . '/Test.php'; - touch($path); - self::assertTrue($this->filesystem()->fileList()->contains(FilePath::fromString($path))); - } - - protected function filesystem(): Filesystem - { - return new GitFilesystem(FilePath::fromString($this->workspacePath())); - } -} diff --git a/lib/Filesystem/Tests/Adapter/IntegrationTestCase.php b/lib/Filesystem/Tests/Adapter/IntegrationTestCase.php deleted file mode 100644 index a92770bc43..0000000000 --- a/lib/Filesystem/Tests/Adapter/IntegrationTestCase.php +++ /dev/null @@ -1,44 +0,0 @@ -exists($this->workspacePath())) { - if ('\\' === \DIRECTORY_SEPARATOR) { - // On Windows, make files in the workspace writable first (recursively), - // because read-only files can't be deleted using the normal filesystem APIs, - // and Git marks files in its .git directory as read-only. - $filesystem->chmod($this->workspacePath(), 0666, 0, true); - } - $filesystem->remove($this->workspacePath()); - } - - $filesystem->mkdir($this->workspacePath()); - } - - protected function workspacePath(): string - { - return realpath(__DIR__.'/..') . '/Workspace'; - } - - protected function loadProject(): void - { - $projectPath = __DIR__.'/project'; - $filesystem = new Filesystem(); - $filesystem->mirror($projectPath, $this->workspacePath()); - chdir($this->workspacePath()); - exec('composer dumpautoload --quiet'); - } - - protected function getProjectAutoloader(): string - { - return require __DIR__.'/project/vendor/autoload.php'; - } -} diff --git a/lib/Filesystem/Tests/Adapter/Simple/SimpleFilesystemTest.php b/lib/Filesystem/Tests/Adapter/Simple/SimpleFilesystemTest.php deleted file mode 100644 index bdb3052499..0000000000 --- a/lib/Filesystem/Tests/Adapter/Simple/SimpleFilesystemTest.php +++ /dev/null @@ -1,16 +0,0 @@ -workspacePath())); - } -} diff --git a/lib/Filesystem/Tests/Adapter/project/composer.json b/lib/Filesystem/Tests/Adapter/project/composer.json deleted file mode 100644 index 907ea3d54e..0000000000 --- a/lib/Filesystem/Tests/Adapter/project/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "acme/test", - "require": { - }, - "require-dev": { - }, - "autoload": { - "psr-4": { - "Acme\\": "src/" - }, - "classmap": [ - "legacy" - ] - }, - "autoload-dev": { - } -} diff --git a/lib/Filesystem/Tests/Adapter/project/legacy/DB.php b/lib/Filesystem/Tests/Adapter/project/legacy/DB.php deleted file mode 100644 index 37ddbf7e12..0000000000 --- a/lib/Filesystem/Tests/Adapter/project/legacy/DB.php +++ /dev/null @@ -1,7 +0,0 @@ -prophesize(FileListProvider::class); - $provider2 = $this->prophesize(FileListProvider::class); - - $provider1->fileList()->willReturn(FileList::fromFilePaths([ - FilePath::fromString('/foobar1'), - FilePath::fromString('/foobar2'), - ])); - $provider2->fileList()->willReturn(FileList::fromFilePaths([ - FilePath::fromString('/foobar3'), - ])); - - $chain = new ChainFileListProvider([ - $provider1->reveal(), - $provider2->reveal(), - ]); - - $fileList = $chain->fileList(); - $this->assertInstanceOf(FileList::class, $fileList); - $list = iterator_to_array($fileList); - $this->assertCount(3, $list); - } -} diff --git a/lib/Filesystem/Tests/Unit/Domain/FallbackFilesystemRegistryTest.php b/lib/Filesystem/Tests/Unit/Domain/FallbackFilesystemRegistryTest.php deleted file mode 100644 index 1016b15e7d..0000000000 --- a/lib/Filesystem/Tests/Unit/Domain/FallbackFilesystemRegistryTest.php +++ /dev/null @@ -1,54 +0,0 @@ - - */ - private ObjectProphecy|FilesystemRegistry $innerRegistry; - - private FallbackFilesystemRegistry $registry; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy|Filesystem $filesystem1; - - public function setUp(): void - { - $this->innerRegistry = $this->prophesize(FilesystemRegistry::class); - $this->registry = new FallbackFilesystemRegistry($this->innerRegistry->reveal(), 'bar'); - $this->filesystem1 = $this->prophesize(Filesystem::class); - } - - public function testDecoration(): void - { - $this->innerRegistry->names()->willReturn([ 'one' ]); - $this->innerRegistry->has('foo')->willReturn(true); - $this->innerRegistry->get('foo')->willReturn($this->filesystem1->reveal()); - - $this->assertEquals([ 'one' ], $this->registry->names()); - $this->assertTrue($this->registry->has('foo')); - $this->assertSame($this->filesystem1->reveal(), $this->registry->get('foo')); - } - - public function testFallback(): void - { - $this->innerRegistry->has('foo')->willReturn(false); - $this->innerRegistry->get('bar')->willReturn($this->filesystem1->reveal()); - - $filesystem = $this->registry->get('foo'); - $this->assertSame($this->filesystem1->reveal(), $filesystem); - } -} diff --git a/lib/Filesystem/Tests/Unit/Domain/FileListTest.php b/lib/Filesystem/Tests/Unit/Domain/FileListTest.php deleted file mode 100644 index baeea4c89e..0000000000 --- a/lib/Filesystem/Tests/Unit/Domain/FileListTest.php +++ /dev/null @@ -1,275 +0,0 @@ -workspace()->reset(); - } - - #[TestDox('It returns true if it contains a file path.')] - public function testContains(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/Foo/Bar.php'), - FilePath::fromString('/Foo/Foo.php'), - ]); - - $this->assertTrue($list->contains(FilePath::fromString('/Foo/Bar.php'))); - } - - #[TestDox('It returns files within a path')] - public function testWithin(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/Foo/Bar.php'), - FilePath::fromString('/Foo/Foo.php'), - FilePath::fromString('/Boo/Bar.php'), - FilePath::fromString('/Foo.php'), - ]); - $expected = FileList::fromFilePaths([ - FilePath::fromString('/Foo/Bar.php'), - FilePath::fromString('/Foo/Foo.php'), - ]); - - $this->assertEquals( - iterator_to_array($expected), - iterator_to_array($list->within(FilePath::fromString('/Foo'))) - ); - } - - /** - * It returns all PHP files with given name (including extension). - */ - public function testNamed(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/reports/Foo/Bar.php.html'), - FilePath::fromString('/reports/Foo/Foo.php.html'), - FilePath::fromString('/reports/Boo/Bar.php.html'), - FilePath::fromString('/reports/Foo.php.html'), - FilePath::fromString('/Foo/Bar.php'), - FilePath::fromString('/Foo/Foo.php'), - FilePath::fromString('/Boo/Bar.php'), - FilePath::fromString('/Foo.php'), - ]); - $expected = FileList::fromFilePaths([ - FilePath::fromString('/Foo/Bar.php'), - FilePath::fromString('/Boo/Bar.php'), - ]); - - $this->assertEquals( - array_values(iterator_to_array($expected)), - array_values(iterator_to_array($list->named('Bar.php'))) - ); - } - - public function testCallback(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/Foo/Bar.php'), - FilePath::fromString('/Foo/Foo.php'), - FilePath::fromString('/Boo/Bar.php'), - FilePath::fromString('/Foo.php'), - ]); - $expected = FileList::fromFilePaths([ - FilePath::fromString('/Foo/Bar.php'), - FilePath::fromString('/Boo/Bar.php'), - ]); - - $this->assertEquals( - array_values(iterator_to_array($expected)), - array_values(iterator_to_array($list->filter(function (SplFileInfo $file) { - return $file->getFileName() == 'Bar.php'; - }))) - ); - } - - public function testExisting(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString(__FILE__), - FilePath::fromString('/Foo.php'), - ]); - $expected = FileList::fromFilePaths([ - FilePath::fromString(__FILE__), - ]); - - $this->assertEquals( - array_values(iterator_to_array($expected)), - array_values(iterator_to_array($list->existing())) - ); - } - - public function testExcludesFilesMatchingPatterns(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/vendor/foo/bar/tests/bartest.php'), - FilePath::fromString('/vendor/foo/bar/tests/footest.php'), - FilePath::fromString('/vendor/foo/bar/src/bar.php'), - FilePath::fromString('/vendor/foo/bar/src/foo.php'), - FilePath::fromString('/resultCache.php'), - ]); - - self::assertEquals( - [ - FilePath::fromString('/vendor/foo/bar/src/bar.php'), - FilePath::fromString('/vendor/foo/bar/src/foo.php'), - ], - iterator_to_array($list->includeAndExclude( - includePatterns: ['/**/*'], - excludePatterns: ['/vendor/**/tests/*', '/resultCache.php'] - )) - ); - } - - public function testIncldesFilesMatchingPatterns(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/vendor/foo/bar/tests/bartest.php'), - FilePath::fromString('/vendor/foo/bar/tests/footest.php'), - FilePath::fromString('/vendor/foo/bar/src/bar.php'), - FilePath::fromString('/vendor/foo/bar/src/foo.php'), - ]); - - self::assertEquals( - [ - FilePath::fromString('/vendor/foo/bar/tests/bartest.php'), - FilePath::fromString('/vendor/foo/bar/tests/footest.php'), - ], - iterator_to_array($list->includeAndExclude( - includePatterns: [ '/vendor/**/tests/*'], - )) - ); - } - - public function testIncludesEverythingByDefault(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/vendor/cache/important/bartest.php'), - FilePath::fromString('/vendor/cache/important/footest.php'), - FilePath::fromString('/vendor/cache/bar.php'), - FilePath::fromString('/vendor/cache/foo.php'), - ])->includeAndExclude( - includePatterns: [], - excludePatterns: [], - ); - - self::assertEquals( - [ - FilePath::fromString('/vendor/cache/important/bartest.php'), - FilePath::fromString('/vendor/cache/important/footest.php'), - FilePath::fromString('/vendor/cache/bar.php'), - FilePath::fromString('/vendor/cache/foo.php'), - ], - iterator_to_array($list) - ); - } - - public function testIncludesExcludePatterns(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/vendor/cache/important/bartest.php'), - FilePath::fromString('/vendor/cache/important/footest.php'), - FilePath::fromString('/vendor/cache/bar.php'), - FilePath::fromString('/vendor/cache/foo.php'), - ])->includeAndExclude( - includePatterns: [ '/src/**/*', '/vendor/cache/important/**/*'], - excludePatterns: [ '/vendor/**/*' ], - ); - - self::assertEquals( - [ - FilePath::fromString('/vendor/cache/important/bartest.php'), - FilePath::fromString('/vendor/cache/important/footest.php'), - ], - iterator_to_array($list) - ); - } - - /** - * @param array $fileList - * @param array $includePatterns - * @param array $excludePatterns - * @param array $expected - */ - #[DataProvider('provideExcludesWithShortFolderName')] - public function testExcludesWithShortFolderName( - array $fileList, - array $includePatterns, - array $excludePatterns, - array $expected, - ): void { - $list = FileList::fromFilePaths($fileList)->includeAndExclude( - includePatterns:$includePatterns, - excludePatterns: $excludePatterns - ); - - self::assertEquals($expected, array_map(fn (FilePath $x) => (string) $x, iterator_to_array($list))); - } - - public static function provideExcludesWithShortFolderName(): Generator - { - yield 'ascii file name' => [ - [ - FilePath::fromString('/src/package/test.php'), - FilePath::fromString('/src/a/test.php'), - ], - [ '/src/**/*'], - [ '/src/a/*' ], - [ - '/src/package/test.php', - ], - ]; - - yield 'unicode file name' => [ - [ - FilePath::fromString('/src/package/test.php'), - FilePath::fromString('/src/ü/test.php'), - ], - [ '/src/**/*'], - [ '/src/ü/*' ], - [ - '/src/package/test.php', - ], - ]; - } - - public function testContainingString(): void - { - $this->workspace()->put('one', 'one two three'); - $this->workspace()->put('two', 'four five six'); - - $list = FileList::fromFilePaths([ - FilePath::fromString($this->workspace()->path('one')), - FilePath::fromString($this->workspace()->path('two')) - ]); - - self::assertCount(2, $list); - self::assertCount(1, $list->containingString('one')); - self::assertCount(1, $list->containingString('two')); - self::assertCount(1, $list->containingString('four')); - self::assertCount(0, $list->containingString('seven')); - } - - public function testContainingStringFileNotExisting(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString($this->workspace()->path('one')), - FilePath::fromString($this->workspace()->path('two')) - ]); - - self::assertCount(2, $list); - self::assertCount(0, $list->containingString('one')); - } -} diff --git a/lib/Filesystem/Tests/Unit/Domain/FilePathTest.php b/lib/Filesystem/Tests/Unit/Domain/FilePathTest.php deleted file mode 100644 index 18e23b04be..0000000000 --- a/lib/Filesystem/Tests/Unit/Domain/FilePathTest.php +++ /dev/null @@ -1,162 +0,0 @@ -expectException(InvalidUriException::class); - $this->expectExceptionMessage('must be absolute'); - FilePath::fromString('foobar'); - } - - public function testFromParts(): void - { - $path = FilePath::fromParts(['Hello', 'Goodbye']); - $this->assertEquals('/Hello/Goodbye', $path->path()); - } - - #[DataProvider('provideFilePathOrString')] - public function testFromFilePathOrString(FilePath|string $path, string $expectedPath): void - { - $filePath = FilePath::fromFilePathOrString($path); - $this->assertInstanceOf(FilePath::class, $filePath); - $this->assertEquals($expectedPath, (string) $filePath); - } - - /** - * @return Generator - */ - public static function provideFilePathOrString(): Generator - { - yield 'FilePath instance' => [ - FilePath::fromString('/foo.php'), - '/foo.php' - ]; - - yield 'string' => [ - '/foo.php', - '/foo.php' - ]; - - yield 'URI string (Unix style)' => [ - 'file:///foo.php', - '/foo.php', - ]; - - yield 'URI string (Windows style)' => [ - 'file:///C:/foo.php', - 'C:/foo.php', - ]; - - yield 'PHAR string' => [ - 'phar:///foo.php', - '/foo.php', - ]; - } - - #[DataProvider('provideUnsupportedInput')] - public function testThrowExceptionOnUnknowableType(string $input, string $expectedExceptionMessage): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage($expectedExceptionMessage); - FilePath::fromString($input); - } - - /** - * @return Generator - */ - public static function provideUnsupportedInput(): Generator - { - yield 'unsupported scheme' => [ - 'ftp://host/foo.php', - 'are supported', // only X schemes are supported - ]; - - yield 'URI without a path' => [ - 'http://.?x=1&n', - 'are supported', // only X schemes are supported - ]; - } - - #[TestDox('It generates an absolute path from a relative.')] - public function testAbsoluteFromString(): void - { - $base = FilePath::fromString('/path/to/something'); - $new = $base->makeAbsoluteFromString('else/yes'); - $this->assertEquals('/path/to/something/else/yes', $new->path()); - } - - #[TestDox('If creating a descendant file and the path is absolute and NOT in th - current branch, an exception should be thrown.')] - public function testDescendantOutsideOfBranchException(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Trying to create descendant'); - $base = FilePath::fromString('/path/to/something'); - $base->makeAbsoluteFromString('/else/yes'); - } - - #[TestDox('If given an absolute path and it lies within the current branch, return new file path.')] - public function testDescendantInsideOfBranchException(): void - { - $base = FilePath::fromString('/path/to/something'); - $path = $base->makeAbsoluteFromString('/path/to/something/yes'); - $this->assertEquals('/path/to/something/yes', (string) $path); - } - - #[TestDox('It should provide the absolute path.')] - public function testAbsolute(): void - { - $path = FilePath::fromString('/path/to/something/else/yes'); - $this->assertEquals('/path/to/something/else/yes', $path->path()); - } - - #[TestDox('It should return true if it is within another path')] - public function testWithin(): void - { - $path1 = FilePath::fromString('/else/yes'); - $path2 = FilePath::fromString('/else/yes/foobar'); - - $this->assertTrue($path2->isWithin($path1)); - } - - #[TestDox('It returns the files extension.')] - public function itReturnsTheExtension(): void - { - $path = FilePath::fromString('/foobar.php'); - $this->assertEquals('php', $path->extension()); - } - - #[TestDox('It returns true or false if it is named a given name.')] - public function testIsNamed(): void - { - $path1 = FilePath::fromString('/else/foobar'); - $path2 = FilePath::fromString('/else/yes/foobar'); - $path3 = FilePath::fromString('/else/yes/brabar'); - - $this->assertTrue($path1->isNamed('foobar')); - $this->assertTrue($path2->isNamed('foobar')); - $this->assertFalse($path3->isNamed('foobar')); - } - - public function testAsSplFileInfo(): void - { - $path1 = FilePath::fromSplFileInfo(new SplFileInfo((string)TextDocumentUri::fromString(__FILE__))); - self::assertEquals(Path::canonicalize(__FILE__), $path1->__toString()); - self::assertEquals(Path::canonicalize(__FILE__), $path1->asSplFileInfo()->__toString()); - } -} diff --git a/lib/Filesystem/Tests/Unit/Domain/MappedFilesystemRegistryTest.php b/lib/Filesystem/Tests/Unit/Domain/MappedFilesystemRegistryTest.php deleted file mode 100644 index 7a383e6f4f..0000000000 --- a/lib/Filesystem/Tests/Unit/Domain/MappedFilesystemRegistryTest.php +++ /dev/null @@ -1,63 +0,0 @@ - - */ - private ObjectProphecy $filesystem; - - public function setUp(): void - { - $this->filesystem = $this->prophesize(Filesystem::class); - } - - public function testRetrievesFilesystems(): void - { - $registry = $this->createRegistry([ - 'foobar' => $this->filesystem->reveal() - ]); - - $filesystem = $registry->get('foobar'); - - $this->assertEquals($this->filesystem->reveal(), $filesystem); - } - - public function testHasFilesystem(): void - { - $registry = $this->createRegistry([ - 'foobar' => $this->filesystem->reveal() - ]); - - $this->assertTrue($registry->has('foobar')); - $this->assertFalse($registry->has('barbar')); - } - - public function testExceptionOnNotFound(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unknown filesystem "barfoo"'); - $registry = $this->createRegistry([ - 'foobar' => $this->filesystem->reveal() - ]); - - $registry->get('barfoo'); - } - - /** @param array $filesystems */ - private function createRegistry(array $filesystems): MappedFilesystemRegistry - { - return new MappedFilesystemRegistry($filesystems); - } -} diff --git a/lib/Indexer/Adapter/Filesystem/FilesystemFileListProvider.php b/lib/Indexer/Adapter/Filesystem/FilesystemFileListProvider.php deleted file mode 100644 index f928705057..0000000000 --- a/lib/Indexer/Adapter/Filesystem/FilesystemFileListProvider.php +++ /dev/null @@ -1,56 +0,0 @@ - $excludePatterns - * @param list $includePatterns - * @param list $supportedExtensions - */ - public function __construct( - private Filesystem $filesystem, - private array $includePatterns = [], - private array $excludePatterns = [], - private array $supportedExtensions = ['php', 'phar'], - ) { - } - - public function provideFileList(Index $index, ?string $subPath = null): FileList - { - if (null !== $subPath && $this->filesystem->exists($subPath) && is_file($subPath)) { - return FileList::fromSingleFilePath($subPath); - } - - $files = $this->filesystem->fileList() - ->byExtensions($this->supportedExtensions) - ->includeAndExclude($this->includePatterns, $this->excludePatterns) - ; - - if ($subPath) { - $files = $files->within(FilePath::fromString($subPath)); - } - - if (!$subPath) { - $files = $files->filter(function (SplFileInfo $fileInfo) use ($index) { - return false === $index->isFresh($fileInfo); - }); - } - - return FileList::fromInfoIterator( - new FileInfoPharExpandingIterator( - $files->getSplFileInfoIterator(), - $this->supportedExtensions, - ) - ); - } -} diff --git a/lib/Indexer/Adapter/Php/FileInfoPharExpandingIterator.php b/lib/Indexer/Adapter/Php/FileInfoPharExpandingIterator.php deleted file mode 100644 index af469e8aaa..0000000000 --- a/lib/Indexer/Adapter/Php/FileInfoPharExpandingIterator.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ -class FileInfoPharExpandingIterator implements IteratorAggregate -{ - /** - * @param Iterator $innerIterator - * @param list $supportedExtensions - */ - public function __construct( - private Iterator $innerIterator, - private array $supportedExtensions = ['php'] - ) { - } - - public function getIterator(): Traversable - { - foreach ($this->innerIterator as $fileInfo) { - if ($fileInfo->getExtension() !== 'phar') { - yield $fileInfo; - } - try { - $phar = new Phar($fileInfo->getPathname()); - } catch (UnexpectedValueException) { - continue; - } - $iterator = new RecursiveIteratorIterator($phar); - foreach ($iterator as $fileInfo) { - assert($fileInfo instanceof SplFileInfo); - if (!in_array($fileInfo->getExtension(), $this->supportedExtensions)) { - continue; - } - yield $fileInfo; - } - } - } -} diff --git a/lib/Indexer/Adapter/Php/FileSearchIndex.php b/lib/Indexer/Adapter/Php/FileSearchIndex.php deleted file mode 100644 index 2c4fc2e070..0000000000 --- a/lib/Indexer/Adapter/Php/FileSearchIndex.php +++ /dev/null @@ -1,130 +0,0 @@ - - */ - private array $subjects = []; - - private int $counter = 0; - - private bool $dirty = false; - - public function __construct(private string $path) - { - } - - public function search(Criteria $criteria): Generator - { - $this->open(); - - foreach ($this->subjects as [ $recordType, $identifier, $type, $flags ]) { - $record = RecordFactory::create($recordType, $identifier); - if ($record instanceof ClassRecord) { - $record = $record->withType($type); - $record->setFlags((int)$flags); - } - - if (false === $criteria->isSatisfiedBy($record)) { - continue; - } - - yield $record; - } - } - - public function write(Record $record): void - { - $this->open(); - $info = [ - $record->recordType(), - $record->identifier(), - $record instanceof ClassRecord ? $record->type() : null, - $record instanceof HasFlags ? $record->flags() : null, - ]; - $this->subjects[$this->recordHash($record)] = $info; - $this->dirty = true; - - if (++$this->counter % self::BATCH_SIZE === 0) { - $this->flush(); - } - } - - public function remove(Record $record): void - { - unset($this->subjects[$this->recordHash($record)]); - $this->dirty = true; - } - - public function flush(): void - { - if (false === $this->dirty) { - return; - } - - $this->open(); - - $content = implode("\n", array_unique(array_map(function (array $parts) { - return implode(self::DELIMITER, $parts); - }, $this->subjects))); - - $written = file_put_contents($this->path, $content); - if (false === $written) { - if (file_exists(dirname($this->path))) { - throw new RuntimeException(sprintf( - 'Directory "%s" already exists', - dirname($this->path), - )); - } - - mkdir(dirname($this->path), 0777, true); - file_put_contents($this->path, $content); - } - - $this->dirty = false; - } - - private function open(): void - { - if ($this->initialized) { - return; - } - - if (!file_exists($this->path)) { - return; - } - - $this->subjects = array_filter(array_map(function (string $line) { - $parts = explode(self::DELIMITER, $line); - - return [$parts[0], $parts[1], $parts[2] ?? null, $parts[3] ?? null]; - }, explode("\n", (string)file_get_contents($this->path)))); - - $this->initialized = true; - } - - private function recordHash(Record $record): string - { - return $record->recordType().$record->identifier(); - } -} diff --git a/lib/Indexer/Adapter/Php/InMemory/InMemoryIndex.php b/lib/Indexer/Adapter/Php/InMemory/InMemoryIndex.php deleted file mode 100644 index 9b2c27395c..0000000000 --- a/lib/Indexer/Adapter/Php/InMemory/InMemoryIndex.php +++ /dev/null @@ -1,84 +0,0 @@ - - */ - private array $index; - - /** - * @param array $index - */ - public function __construct(array $index = []) - { - $this->searchIndex = new InMemorySearchIndex(); - $this->lastUpdate = 0; - foreach ($index as $record) { - $this->write($record); - } - } - - public function lastUpdate(): int - { - return $this->lastUpdate; - } - - public function write(Record $record): void - { - $this->index[$this->recordKey($record)] = $record; - $this->searchIndex->write($record); - } - - public function get(Record $record): Record - { - $key = $this->recordKey($record); - - return $this->index[$key] ?? $record; - } - - public function isFresh(SplFileInfo $fileInfo): bool - { - return false; - } - - public function reset(): void - { - $this->index = []; - } - - public function exists(): bool - { - return $this->lastUpdate !== 0; - } - - public function done(): void - { - $this->lastUpdate = time(); - } - - public function has(Record $record): bool - { - return isset($this->index[$this->recordKey($record)]); - } - - public function optimise(bool $dryRun): iterable - { - return []; - } - - private function recordKey(Record $record): string - { - return $record->recordType().$record->identifier(); - } -} diff --git a/lib/Indexer/Adapter/Php/InMemory/InMemorySearchIndex.php b/lib/Indexer/Adapter/Php/InMemory/InMemorySearchIndex.php deleted file mode 100644 index 3a102ea7db..0000000000 --- a/lib/Indexer/Adapter/Php/InMemory/InMemorySearchIndex.php +++ /dev/null @@ -1,64 +0,0 @@ - - */ - private array $buffer = []; - - public static function fromRecords(Record ...$records): self - { - $instance = new self(); - foreach ($records as $record) { - $instance->write($record); - } - - return $instance; - } - - - /** - * @return Generator - */ - public function search(Criteria $criteria): Generator - { - foreach ($this->buffer as [$recordType, $identifier]) { - $record = RecordFactory::create($recordType, $identifier); - - if (!$criteria->isSatisfiedBy($record)) { - continue; - } - - yield $record; - } - } - - public function write(Record $record): void - { - $this->buffer[$record->identifier()] = [$record->recordType(), $record->identifier()]; - } - - public function flush(): void - { - } - - public function remove(Record $record): void - { - unset($this->buffer[$record->identifier()]); - } - - public function has(ClassRecord $record): bool - { - return isset($this->buffer[$record->identifier()]); - } -} diff --git a/lib/Indexer/Adapter/Php/PhpIndexerLister.php b/lib/Indexer/Adapter/Php/PhpIndexerLister.php deleted file mode 100644 index ed05b7a9e3..0000000000 --- a/lib/Indexer/Adapter/Php/PhpIndexerLister.php +++ /dev/null @@ -1,33 +0,0 @@ - $name ? Path::join($this->indexDirectory, $name) : '', - array_filter( - (array)scandir($this->indexDirectory), - fn (string|false $name) => !in_array($name, ['.', '..']) - ), - ), fn (string $indexPath) => is_dir($indexPath)); - - foreach ($indexes as $indexPath) { - $info = IndexInfo::fromSplFileInfo(new SplFileInfo($indexPath)); - // warmup the size - $info->size(); - yield $info; - } - } -} diff --git a/lib/Indexer/Adapter/Php/Serialized/FileRepository.php b/lib/Indexer/Adapter/Php/Serialized/FileRepository.php deleted file mode 100644 index a42b01bacc..0000000000 --- a/lib/Indexer/Adapter/Php/Serialized/FileRepository.php +++ /dev/null @@ -1,240 +0,0 @@ - - */ - private array $buffer = []; - - private int $counter = 0; - - public function __construct( - private string $path, - private RecordSerializer $serializer, - private LoggerInterface $logger = new NullLogger(), - ) { - $this->initializeLastUpdate(); - } - - public function put(Record $record): void - { - $this->buffer[$this->bufferKey($record)] = $record; - - if (++$this->counter % self::BATCH_SIZE === 0) { - $this->flush(); - } - } - - /** - * @template TRecord of Record - * @param TRecord $record - * @return TRecord - */ - public function get(Record $record): ?Record - { - $bufferKey = $this->bufferKey($record); - - if (isset($this->buffer[$bufferKey])) { - /** @phpstan-ignore-next-line */ - return $this->buffer[$bufferKey]; - } - - $path = $this->pathFor($record); - - if (!file_exists($path)) { - $this->remove($record); - return null; - } - - try { - $deserialized = $this->serializer->deserialize((string)file_get_contents($path)); - } catch (Throwable $corrupted) { - $this->logger->warning(sprintf( - 'Record at path "%s" is corrupted, removing: %s', - $path, - $corrupted->getMessage() - )); - $this->remove($record); - return null; - } - - if (null === $deserialized) { - return null; - } - - if (!$deserialized instanceof $record) { - $this->logger->warning(sprintf( - 'Invalid cache entry file: "%s", got instance of "%s"', - $path, - get_class($deserialized) - )); - - return null; - } - - return $deserialized; - } - - public function putTimestamp(?int $time = null): void - { - $time = $time ?? time(); - $this->ensureDirectoryExists(dirname($this->timestampPath())); - file_put_contents($this->timestampPath(), $time); - $this->lastUpdate = $time; - } - - public function lastUpdate(): int - { - return $this->lastUpdate; - } - - public function reset(): void - { - Filesystem::removeDir($this->path); - $this->putTimestamp(0); - } - - public function remove(Record $record): void - { - $path = $this->pathFor($record); - - if (!file_exists($path)) { - return; - } - - if (@unlink($path)) { - return; - } - - $this->logger->warning(sprintf( - 'Could not remove index file "%s"', - $path - )); - } - - public function flush(): void - { - foreach ($this->buffer as $record) { - $path = $this->pathFor($record); - $this->ensureDirectoryExists(dirname($path)); - file_put_contents($path, $this->serializer->serialize($record)); - } - $this->buffer = []; - } - /** - * @return Generator - */ - public function iterator(): Generator - { - $flags = - FilesystemIterator::KEY_AS_PATHNAME | - FilesystemIterator::CURRENT_AS_FILEINFO | - FilesystemIterator::SKIP_DOTS; - - if (!file_exists($this->path)) { - return; - } - - if (!is_dir($this->path)) { - return; - } - - $files = new RecursiveDirectoryIterator($this->path, $flags); - $files = new RecursiveIteratorIterator( - $files, - RecursiveIteratorIterator::LEAVES_ONLY, - RecursiveIteratorIterator::CATCH_GET_CHILD - ); - - foreach ($files as $file) { - assert($file instanceof SplFileInfo); - - if ($file->getExtension() !== 'cache') { - continue; - } - - $contents = file_get_contents($file->getPathname()); - - if (false === $contents) { - continue; - } - - $record = $this->serializer->deserialize( - $contents - ); - - if (null === $record) { - continue; - } - - yield $file->getPathname() => $record; - } - } - - private function ensureDirectoryExists(string $path): void - { - if (file_exists($path)) { - return; - } - - mkdir($path, 0777, true); - } - - private function initializeLastUpdate(): void - { - $this->lastUpdate = file_exists($this->timestampPath()) ? - (int)file_get_contents($this->timestampPath()) : - 0 - ; - } - - private function timestampPath(): string - { - return sprintf('%s/timestamp.v%d', $this->path, self::VERSION); - } - - private function pathFor(Record $record): string - { - $hash = md5($record->identifier()); - return sprintf( - '%s/%s_%s/%s/%s.cache', - $this->path, - $record->recordType(), - substr($hash, 0, 1), - substr($hash, 1, 1), - $hash - ); - } - - private function bufferKey(Record $record): string - { - return $record->recordType().$record->identifier(); - } -} diff --git a/lib/Indexer/Adapter/Php/Serialized/SerializedIndex.php b/lib/Indexer/Adapter/Php/Serialized/SerializedIndex.php deleted file mode 100644 index 1e642e87db..0000000000 --- a/lib/Indexer/Adapter/Php/Serialized/SerializedIndex.php +++ /dev/null @@ -1,137 +0,0 @@ -repository->lastUpdate(); - } - - public function optimise(bool $dryRun): iterable - { - $count = 0; - clearstatcache(true); - foreach ($this->repository->iterator() as $cachePath => $record) { - if ($record instanceof HasPath) { - if (!$record->filePath()) { - yield sprintf( - 'Record %s in %s has no file path: %s', - $record::class, - (string)$cachePath, - sprintf('%s:%s', $record->recordType(), $record->identifier()) - ); - if ($dryRun === false) { - $this->repository->remove($record); - } - continue; - } - - try { - $doc = $this->locator->get(TextDocumentUri::fromString($record->filePath())); - } catch (TextDocumentNotFound) { - if ($dryRun === false) { - $this->repository->remove($record); - } - yield sprintf( - 'File does not exist so removed %s:%s', - $record->recordType(), - $record->identifier() - ); - continue; - } - } - if ($record instanceof HasFileReferences) { - $removed = 0; - foreach ($record->references() as $reference) { - try { - $doc = $this->locator->get(TextDocumentUri::fromString($reference)); - } catch (TextDocumentNotFound) { - $removed++; - $record->removeReference($reference); - } - } - if ($removed > 0) { - $this->write($record); - yield sprintf( - 'removed %d dead refernces from %s:%s', - $removed, - $record->recordType(), - $record->identifier(), - ); - } - } - - $count++; - - // arbitrarily flush to disk after every N records - if ($count % 500 === 0) { - $this->repository->flush(); - } - - yield null; - } - - $this->repository->flush(); - } - - public function get(Record $record): Record - { - return $this->repository->get($record) ?? $record; - } - - public function write(Record $record): void - { - $this->repository->put($record); - } - - public function isFresh(SplFileInfo $fileInfo): bool - { - try { - $mtime = $fileInfo->getCTime(); - } catch (RuntimeException) { - // file likely doesn't exist - return false; - } - - return $mtime < $this->lastUpdate(); - } - - public function reset(): void - { - $this->repository->reset(); - } - - public function exists(): bool - { - return $this->repository->lastUpdate() > 0; - } - - public function done(): void - { - $this->repository->flush(); - $this->repository->putTimestamp(); - } - - public function has(Record $record): bool - { - return $this->repository->get($record) ? true : false; - } -} diff --git a/lib/Indexer/Adapter/ReferenceFinder/IndexedImplementationFinder.php b/lib/Indexer/Adapter/ReferenceFinder/IndexedImplementationFinder.php deleted file mode 100644 index f0495c437b..0000000000 --- a/lib/Indexer/Adapter/ReferenceFinder/IndexedImplementationFinder.php +++ /dev/null @@ -1,167 +0,0 @@ -containerTypeResolver = new ContainerTypeResolver($reflector); - } - - public function findImplementations( - TextDocument $document, - ByteOffset $byteOffset, - bool $includeDefinition = false - ): Locations { - $nodeContext = $this->reflector->reflectOffset( - $document, - $byteOffset->toInt() - )->nodeContext(); - - $symbolType = $nodeContext->symbol()->symbolType(); - - if ( - $symbolType === Symbol::METHOD || - $symbolType === Symbol::CONSTANT || - $symbolType === Symbol::CASE || - $symbolType === Symbol::VARIABLE || - $symbolType === Symbol::PROPERTY - ) { - if ($symbolType === Symbol::CASE) { - $symbolType = 'case'; - } - if ($symbolType === Symbol::VARIABLE) { - $symbolType = Symbol::PROPERTY; - } - return $this->memberImplementations($nodeContext, $symbolType, $includeDefinition); - } - - $locations = []; - $implementations = $this->resolveImplementations(FullyQualifiedName::fromString($nodeContext->type()->__toString())); - - foreach ($implementations as $implementation) { - $record = $this->query->class()->get($implementation); - - if (!$record instanceof ClassRecord) { - continue; - } - - $locations[] = new Location( - TextDocumentUri::fromString($record->filePath()), - ByteOffsetRange::fromByteOffsets($record->start(), $record->end()), - ); - } - - return new Locations($locations); - } - - /** - * @param ReflectionMember::TYPE_* $symbolType - * - * @return Locations - */ - private function memberImplementations(NodeContext $nodeContext, string $symbolType, bool $includeDefinition): Locations - { - $container = $nodeContext->containerType(); - $methodName = $nodeContext->symbol()->name(); - $containerType = $this->containerTypeResolver->resolveDeclaringContainerType($symbolType, $methodName, $container); - - if (!$containerType) { - return new Locations([]); - } - - $implementations = $this->resolveImplementations( - FullyQualifiedName::fromString($containerType), - true - ); - - $locations = []; - - foreach ($implementations as $implementation) { - $record = $this->query->class()->get($implementation); - - if (null === $record) { - continue; - } - - try { - $reflection = $this->reflector->reflectClassLike($implementation->__toString()); - $member = $reflection->members()->byMemberType($symbolType)->belongingTo($reflection->name())->get($methodName); - } catch (NotFound) { - continue; - } - - if (false === $includeDefinition) { - if (!$reflection instanceof ReflectionClass) { - continue; - } - - if ($member instanceof ReflectionMethod) { - if ($member->isAbstract()) { - continue; - } - } - } - - if (!$record instanceof HasPath) { - continue; - } - - $path = $record->filePath(); - - if (null === $path) { - continue; - } - - $locations[] = new Location(TextDocumentUri::fromString($path), $member->position()); - } - - return new Locations($locations); - } - - /** - * @return Generator - */ - private function resolveImplementations(FullyQualifiedName $type, bool $yieldFirst = false): Generator - { - if ($yieldFirst) { - yield $type; - } - - foreach ($this->query->class()->implementing($type) as $implementingType) { - if (false === $this->deepReferences) { - yield $implementingType; - continue; - } - - yield from $this->resolveImplementations($implementingType, true); - } - } -} diff --git a/lib/Indexer/Adapter/ReferenceFinder/IndexedNameSearcher.php b/lib/Indexer/Adapter/ReferenceFinder/IndexedNameSearcher.php deleted file mode 100644 index c631d4e48f..0000000000 --- a/lib/Indexer/Adapter/ReferenceFinder/IndexedNameSearcher.php +++ /dev/null @@ -1,79 +0,0 @@ -resolveTypeCriteria($type); - - if (null !== $typeCriteria) { - $criteria = Criteria::and( - $criteria, - Criteria::or( - $typeCriteria, - - // B/C for old indexes - Criteria::isClassTypeUndefined(), - ), - ); - } - - foreach ($this->client->search($criteria) as $result) { - yield NameSearchResult::create( - $result->recordType(), - FullyQualifiedName::fromString($result->identifier()), - $result instanceof HasPath ? TextDocumentUri::fromString($result->filepath()) : null, - ); - } - } - - /** - * @param null|NameSearcherType::* $type - */ - private function resolveTypeCriteria(?string $type): ?Criteria - { - return match($type) { - NameSearcherType::ATTRIBUTE => Criteria::isAttribute(), - NameSearcherType::ATTRIBUTE_TARGET_CLASS => Criteria::isClassAttribute(), - NameSearcherType::ATTRIBUTE_TARGET_CLASS_CONSTANT => Criteria::isClassConstantAttribute(), - NameSearcherType::ATTRIBUTE_TARGET_PROPERTY => Criteria::isPropertyAttribute(), - NameSearcherType::ATTRIBUTE_TARGET_PARAMETER => Criteria::isParameterAttribute(), - NameSearcherType::ATTRIBUTE_TARGET_METHOD => Criteria::isMethodAttribute(), - NameSearcherType::ATTRIBUTE_TARGET_FUNCTION => Criteria::isFunctionAttribute(), - NameSearcherType::ATTRIBUTE_TARGET_PROMOTED_PROPERTY => Criteria::isPromotedPropertyAttribute(), - NameSearcherType::CLASS_ => Criteria::isClassConcrete(), - NameSearcherType::INTERFACE => Criteria::isClassInterface(), - NameSearcherType::TRAIT => Criteria::isClassTrait(), - NameSearcherType::ENUM => Criteria::isClassEnum(), - default => null, - }; - } -} diff --git a/lib/Indexer/Adapter/ReferenceFinder/IndexedReferenceFinder.php b/lib/Indexer/Adapter/ReferenceFinder/IndexedReferenceFinder.php deleted file mode 100644 index 184badc7a5..0000000000 --- a/lib/Indexer/Adapter/ReferenceFinder/IndexedReferenceFinder.php +++ /dev/null @@ -1,228 +0,0 @@ -containerTypeResolver = $containerTypeResolver ?: new ContainerTypeResolver($reflector); - } - - /** - * @return Generator - */ - public function findReferences(TextDocument $document, ByteOffset $byteOffset): Generator - { - try { - $nodeContext = $this->reflector->reflectOffset( - $document, - $byteOffset->toInt() - )->nodeContext(); - } catch (NotFound) { - return; - } - - foreach ($this->resolveReferences($nodeContext) as $locationConfidence) { - if ($locationConfidence->isSurely()) { - yield PotentialLocation::surely($locationConfidence->location()); - continue; - } - - if ($locationConfidence->isMaybe()) { - yield PotentialLocation::maybe($locationConfidence->location()); - continue; - } - - yield PotentialLocation::not($locationConfidence->location()); - } - - return true; - } - - /** - * @return Generator - */ - private function resolveReferences(NodeContext $nodeContext): Generator - { - $symbolType = $nodeContext->symbol()->symbolType(); - if ($symbolType === Symbol::CLASS_) { - foreach ($this->implementationsOf($nodeContext->type()->__toString()) as $implementationFqn) { - yield from $this->query->class()->referencesTo($implementationFqn); - } - return; - } - - if ($symbolType === Symbol::FUNCTION) { - yield from $this->query->function()->referencesTo($nodeContext->symbol()->name()); - return; - } - - $memberType = $nodeContext->symbol()->symbolType(); - if (in_array($memberType, [ - Symbol::METHOD, - Symbol::CONSTANT, - Symbol::PROPERTY, - Symbol::VARIABLE, - Symbol::CASE, - ])) { - $containerType = $this->containerTypeResolver->resolveDeclaringClass( - $this->symbolTypeToMemberType($nodeContext), - $nodeContext->symbol()->name(), - $nodeContext->containerType() - ); - - if (null === $containerType) { - yield from $this->query->member()->referencesTo( - $this->symbolTypeToReferenceType($nodeContext), - $nodeContext->symbol()->name(), - null - ); - return; - } - - // note that we check the all implementations: this will multiply - // the number of NOT and MAYBE matches - foreach ($this->implementationsOf($containerType) as $implemenations) { - yield from $this->memberReferencesTo( - $this->symbolTypeToReferenceType($nodeContext), - $nodeContext->symbol()->name(), - $implemenations - ); - } - return; - } - } - - /** - * @return Generator - */ - private function implementationsOf(string $fqn): Generator - { - yield $fqn; - - if (false === $this->deepReferences) { - return; - } - - foreach ($this->query->class()->implementing($fqn) as $implementation) { - yield $implementation->__toString(); - } - } - - /** - * @return ReflectionMember::TYPE_* - */ - private function symbolTypeToMemberType(NodeContext $nodeContext): string - { - $symbolType = $nodeContext->symbol()->symbolType(); - - return match ($symbolType) { - Symbol::CASE => ReflectionMember::TYPE_CASE, - Symbol::METHOD => ReflectionMember::TYPE_METHOD, - Symbol::PROPERTY => ReflectionMember::TYPE_PROPERTY, - Symbol::VARIABLE => ReflectionMember::TYPE_PROPERTY, - Symbol::CONSTANT => ReflectionMember::TYPE_CONSTANT, - - default => throw new RuntimeException(sprintf( - 'Could not convert symbol type "%s" to member type', - $symbolType - )) - }; - } - - /** - * @return MemberRecord::TYPE_* - */ - private function symbolTypeToReferenceType(NodeContext $nodeContext): string - { - $symbolType = $nodeContext->symbol()->symbolType(); - - return match ($symbolType) { - Symbol::CASE => MemberRecord::TYPE_CONSTANT, - Symbol::METHOD => MemberRecord::TYPE_METHOD, - Symbol::PROPERTY => MemberRecord::TYPE_PROPERTY, - Symbol::VARIABLE => MemberRecord::TYPE_PROPERTY, - Symbol::CONSTANT => MemberRecord::TYPE_CONSTANT, - - default => throw new RuntimeException(sprintf( - 'Could not convert symbol type "%s" to reference type', - $symbolType - )) - }; - } - - /** - * @param "method"|"constant"|"property" $referenceType - * @return Generator - */ - private function memberReferencesTo(string $referenceType, string $memberName, string $containerType): Generator - { - if ($memberName === '__construct' && $referenceType === 'method') { - yield from $this->newObjectReferences($containerType); - return; - } - yield from $this->query->member()->referencesTo($referenceType, $memberName, $containerType); - } - /** - * @return Generator - */ - private function newObjectReferences(string $containerType): Generator - { - /** @var ?ClassRecord $class */ - $class = $this->query->class()->get($containerType); - if (!$class) { - return; - } - - foreach ($class->references() as $reference) { - /** @var ?FileRecord $file */ - $file = $this->query->file()->get($reference); - if (null === $file) { - continue; - } - foreach ($file->references() as $fileReference) { - if ( - $fileReference->type() !== 'class' || - !$fileReference->hasFlag(RecordReference::FLAG_NEW_OBJECT) || - $fileReference->identifier() !== $containerType - ) { - continue; - } - yield LocationConfidence::surely( - Location::fromPathAndOffsets( - $file->filePath() ?? '', - $fileReference->start(), - $fileReference->end() - ) - ); - } - } - } -} diff --git a/lib/Indexer/Adapter/ReferenceFinder/Util/ContainerTypeResolver.php b/lib/Indexer/Adapter/ReferenceFinder/Util/ContainerTypeResolver.php deleted file mode 100644 index 358cb3fa7e..0000000000 --- a/lib/Indexer/Adapter/ReferenceFinder/Util/ContainerTypeResolver.php +++ /dev/null @@ -1,52 +0,0 @@ -reflector->reflectClassLike($containerFqn); - $members = $classLike->members()->byMemberType($memberType); - - return $members->get($memberName)->original()->declaringClass()->name()->__toString(); - } catch (NotFound) { - return $containerFqn; - } - } - - /** - * @param ReflectionMember::TYPE_* $memberType - */ - public function resolveDeclaringClass(string $memberType, string $memberName, ?string $containerFqn): ?string - { - if (null === $containerFqn) { - return null; - } - - try { - $classLike = $this->reflector->reflectClassLike($containerFqn); - $members = $classLike->members()->byMemberType($memberType); - - return $members->get($memberName)->declaringClass()->name()->__toString(); - } catch (NotFound) { - return $containerFqn; - } - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/AbstractClassLikeIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/AbstractClassLikeIndexer.php deleted file mode 100644 index 6b9ed93990..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/AbstractClassLikeIndexer.php +++ /dev/null @@ -1,91 +0,0 @@ -implements() as $implementedClass) { - $implementedRecord = $index->get(ClassRecord::fromName($implementedClass)); - - if (false === $implementedRecord->removeImplementation($record->fqn())) { - continue; - } - - $index->write($implementedRecord); - } - } - - protected function indexInterfaceList(QualifiedNameList $interfaceList, ClassRecord $record, Index $index): void - { - foreach ($interfaceList->children as $interfaceName) { - if (!$interfaceName instanceof QualifiedName) { - continue; - } - - $interfaceName = (string) $interfaceName->getResolvedName(); - $interfaceRecord = $index->get(ClassRecord::fromName($interfaceName)); - $record->addImplements( - FullyQualifiedName::fromString($interfaceName) - ); - - assert($interfaceRecord instanceof ClassRecord); - $interfaceRecord->addImplementation($record->fqn()); - - $index->write($interfaceRecord); - } - } - - /** - * @param ClassRecord::TYPE_* $type - */ - protected function getClassLikeRecord(string $type, Node $node, Index $index, TextDocument $document): ClassRecord - { - assert($node instanceof NamespacedNameInterface); - $name = $node->getNamespacedName()->getFullyQualifiedNameText(); - - if (empty($name)) { - throw new CannotIndexNode(sprintf( - 'Name is empty for file "%s"', - $document->uri()?->__toString() ?? 'unknown', - )); - } - if (!$document->uri()) { - throw new CannotIndexNode(sprintf( - 'Document has no URI for class "%s"', - $name - )); - } - - $record = $index->get(ClassRecord::fromName($name)); - assert($record instanceof ClassRecord); - /** @var ClassDeclaration|InterfaceDeclaration|EnumDeclaration|TraitDeclaration $node */ - $record->setStart(ByteOffset::fromInt($node->name->getStartPosition())); - $record->setEnd(ByteOffset::fromInt($node->name->getEndPosition())); - $record->setFilePath($document->uriOrThrow()); - $record->setType($type); - - return $record; - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/ClassDeclarationIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/ClassDeclarationIndexer.php deleted file mode 100644 index 3c461792b6..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/ClassDeclarationIndexer.php +++ /dev/null @@ -1,144 +0,0 @@ -name instanceof MissingToken) { - throw new CannotIndexNode(sprintf( - 'Class name is missing (maybe a reserved word) in: %s', - $document->uri()?->__toString() ?? '?', - )); - } - $record = $this->getClassLikeRecord(ClassRecord::TYPE_CLASS, $node, $index, $document); - - $this->removeImplementations($index, $record); - $record->clearImplemented(); - - $this->indexClassInterfaces($index, $record, $node); - $this->indexBaseClass($index, $record, $node); - - $this->indexAttributes($record, $node); - - $index->write($record); - } - - public function indexAttributes(ClassRecord $record, ClassDeclaration $node): void - { - $attributes = $node->attributes ?? []; - if (count($attributes) === 0) { - return; - } - - foreach ($attributes as $attributeGroup) { - foreach ($attributeGroup->attributes->children as $attribute) { - if (!$attribute instanceof Attribute) { - continue; - } - /** @phpstan-ignore-next-line */ - if ((string) $attribute->name?->getResolvedName() !== \Attribute::class) { - continue; - } - - $targetTexts = $this->listAttributeTargetTexts($attribute); - if ([] === $targetTexts) { - $record->addFlag(ClassRecord::FLAG_ATTRIBUTE); - return; - } - - foreach ($targetTexts as $targetText) { - $record->addFlag(match($targetText) { - (string)\Attribute::TARGET_CLASS, 'Attribute::TARGET_CLASS' => ClassRecord::FLAG_ATTRIBUTE_TARGET_CLASS, - (string)\Attribute::TARGET_FUNCTION, 'Attribute::TARGET_FUNCTION' => ClassRecord::FLAG_ATTRIBUTE_TARGET_FUNCTION, - (string)\Attribute::TARGET_METHOD, 'Attribute::TARGET_METHOD' => ClassRecord::FLAG_ATTRIBUTE_TARGET_METHOD, - (string)\Attribute::TARGET_PROPERTY, 'Attribute::TARGET_PROPERTY' => ClassRecord::FLAG_ATTRIBUTE_TARGET_PROPERTY, - (string)\Attribute::TARGET_CLASS_CONSTANT, 'Attribute::TARGET_CLASS_CONSTANT' => ClassRecord::FLAG_ATTRIBUTE_TARGET_CLASS_CONSTANT, - (string)\Attribute::TARGET_PARAMETER, 'Attribute::TARGET_PARAMETER' => ClassRecord::FLAG_ATTRIBUTE_TARGET_PARAMETER, - (string)\Attribute::IS_REPEATABLE, 'Attribute::IS_REPEATABLE' => ClassRecord::FLAG_ATTRIBUTE_IS_REPEATABLE, - default => ClassRecord::FLAG_ATTRIBUTE, - }); - } - - return; - } - } - } - - private function indexClassInterfaces(Index $index, ClassRecord $classRecord, ClassDeclaration $node): void - { - // @phpstan-ignore-next-line because ClassInterfaceClause _can_ (and has been) be NULL - if (null === $interfaceClause = $node->classInterfaceClause) { - return; - } - - if (null == $interfaceList = $interfaceClause->interfaceNameList) { - return; - } - - $this->indexInterfaceList($interfaceList, $classRecord, $index); - } - - private function indexBaseClass(Index $index, ClassRecord $record, ClassDeclaration $node): void - { - // @phpstan-ignore-next-line because classBaseClause _can_ be NULL - if (null === $baseClause = $node->classBaseClause) { - return; - } - - // @phpstan-ignore-next-line because classBaseClause _can_ be NULL - if (null === $baseClass = $baseClause->baseClass) { - return; - } - - /** @phpstan-ignore-next-line */ - if ($baseClass instanceof MissingToken) { - return; - } - - $name = $baseClass->getResolvedName(); - $record->addImplements(FullyQualifiedName::fromString((string)$name)); - $baseClassRecord = $index->get(ClassRecord::fromName($name)); - assert($baseClassRecord instanceof ClassRecord); - $baseClassRecord->addImplementation($record->fqn()); - $index->write($baseClassRecord); - } - - /** - * @return string[] - */ - private function listAttributeTargetTexts(Node $attribute): array - { - $targetTexts = []; - - $isNotTarget = fn (Node $node): bool => !$node instanceof ScopedPropertyAccessExpression; - - foreach ($attribute->getDescendantNodes($isNotTarget) as $target) { - if ($isNotTarget($target)) { - continue; - } - - $targetTexts[] = ltrim($target->getText(), '\\'); - } - - return $targetTexts; - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexer.php deleted file mode 100644 index f71c3d3d66..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexer.php +++ /dev/null @@ -1,90 +0,0 @@ -parent instanceof CallExpression; - } - - public function beforeParse(Index $index, TextDocument $document): void - { - $fileRecord = $index->get(FileRecord::fromPath($document->uriOrThrow()->__toString())); - assert($fileRecord instanceof FileRecord); - - foreach ($fileRecord->references() as $outgoingReference) { - if ($outgoingReference->type() !== ClassRecord::RECORD_TYPE) { - continue; - } - - $record = $index->get(ClassRecord::fromName($outgoingReference->identifier())); - assert($record instanceof ClassRecord); - $record->removeReference($fileRecord->identifier()); - $index->write($record); - $fileRecord->removeReferencesToRecordType($outgoingReference->type()); - $index->write($fileRecord); - } - } - - public function index(Index $index, TextDocument $document, Node $node): void - { - assert($node instanceof QualifiedName); - - $name = - $node->parent?->parent instanceof TraitUseClause ? - TolerantQualifiedNameResolver::getResolvedName($node) : - $node->getResolvedName(); - - if (empty($name)) { - return; - } - - if (in_array((string)$name, self::NOT_CLASS_NAMES)) { - return; - } - - $targetRecord = $index->get(ClassRecord::fromName($name)); - assert($targetRecord instanceof ClassRecord); - $targetRecord->addReference($document->uriOrThrow()->__toString()); - - $index->write($targetRecord); - - $fileRecord = $index->get(FileRecord::fromPath($document->uriOrThrow()->__toString())); - assert($fileRecord instanceof FileRecord); - $reference = new RecordReference( - ClassRecord::RECORD_TYPE, - $targetRecord->identifier(), - $node->getStartPosition(), - end: $node->getEndPosition() - ); - - if ($node->parent instanceof ObjectCreationExpression) { - $reference->addFlag(RecordReference::FLAG_NEW_OBJECT); - } - - $fileRecord->addReference($reference); - $index->write($fileRecord); - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/ConstantDeclarationIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/ConstantDeclarationIndexer.php deleted file mode 100644 index 42c86db6a5..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/ConstantDeclarationIndexer.php +++ /dev/null @@ -1,105 +0,0 @@ -callableExpression instanceof QualifiedName) { - return false; - } - - if ('define' === NodeUtil::shortName($node->callableExpression)) { - return true; - } - - return false; - } - - public function index(Index $index, TextDocument $document, Node $node): void - { - if ($node instanceof ConstDeclaration) { - $this->fromConstDeclaration($node, $index, $document); - return; - } - - if ($node instanceof CallExpression) { - $this->fromDefine($node, $index, $document); - return; - } - } - - public function beforeParse(Index $index, TextDocument $document): void - { - } - - private function fromConstDeclaration(Node $node, Index $index, TextDocument $document): void - { - assert($node instanceof ConstDeclaration); - if (!$node->constElements instanceof ConstElementList) { - return; - } - foreach ($node->constElements->getChildNodes() as $constNode) { - assert($constNode instanceof ConstElement); - $record = $index->get(ConstantRecord::fromName($constNode->getNamespacedName()->getFullyQualifiedNameText())); - assert($record instanceof ConstantRecord); - $record->setStart(ByteOffset::fromInt($node->getStartPosition())); - $record->setEnd(ByteOffset::fromInt($node->getEndPosition())); - $record->setFilePath($document->uriOrThrow()); - $index->write($record); - } - } - - private function fromDefine(CallExpression $node, Index $index, TextDocument $document): void - { - assert($node instanceof CallExpression); - - if (null === $node->argumentExpressionList) { - return; - } - - foreach ($node->argumentExpressionList->getChildNodes() as $expression) { - if (!$expression instanceof ArgumentExpression) { - return; - } - $string = $expression->expression; - if (!$string instanceof StringLiteral) { - return; - } - - $record = $index->get(ConstantRecord::fromName($string->getStringContentsText())); - assert($record instanceof ConstantRecord); - $record->setStart(ByteOffset::fromInt($node->getStartPosition())); - $record->setEnd(ByteOffset::fromInt($node->getEndPosition())); - $record->setFilePath($document->uriOrThrow()); - $index->write($record); - - // Return after the first argument, because we only need the name of the constant. - return; - } - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/EnumDeclarationIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/EnumDeclarationIndexer.php deleted file mode 100644 index db0e7ac22c..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/EnumDeclarationIndexer.php +++ /dev/null @@ -1,36 +0,0 @@ -name instanceof MissingToken) { - throw new CannotIndexNode(sprintf( - 'Class name is missing (maybe a reserved word) in: %s', - $document->uri()?->__toString() ?? '?', - )); - } - $record = $this->getClassLikeRecord(ClassRecord::TYPE_ENUM, $node, $index, $document); - - $this->removeImplementations($index, $record); - $record->clearImplemented(); - - $index->write($record); - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/FunctionDeclarationIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/FunctionDeclarationIndexer.php deleted file mode 100644 index f5ddbceb1a..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/FunctionDeclarationIndexer.php +++ /dev/null @@ -1,34 +0,0 @@ -get(FunctionRecord::fromName($node->getNamespacedName()->getFullyQualifiedNameText())); - assert($record instanceof FunctionRecord); - $record->setStart(ByteOffset::fromInt($node->getStartPosition())); - $record->setEnd(ByteOffset::fromInt($node->getEndPosition())); - $record->setFilePath($document->uriOrThrow()); - $index->write($record); - } - - public function beforeParse(Index $index, TextDocument $document): void - { - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/FunctionReferenceIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/FunctionReferenceIndexer.php deleted file mode 100644 index ca67efd695..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/FunctionReferenceIndexer.php +++ /dev/null @@ -1,67 +0,0 @@ -parent instanceof CallExpression; - } - - public function beforeParse(Index $index, TextDocument $document): void - { - $fileRecord = $index->get(FileRecord::fromPath($document->uriOrThrow()->__toString())); - assert($fileRecord instanceof FileRecord); - - foreach ($fileRecord->references() as $outgoingReference) { - if ($outgoingReference->type() !== FunctionRecord::RECORD_TYPE) { - continue; - } - - $record = $index->get(FunctionRecord::fromName($outgoingReference->identifier())); - assert($record instanceof FunctionRecord); - $record->removeReference($fileRecord->identifier()); - $index->write($record); - } - } - - public function index(Index $index, TextDocument $document, Node $node): void - { - assert($node instanceof QualifiedName); - - // this is slow - $name = $node->getResolvedName() ? $node->getResolvedName() : null; - - if (null === $name) { - $name = (string)$node; - } - - $targetRecord = $index->get(FunctionRecord::fromName($name)); - assert($targetRecord instanceof FunctionRecord); - $targetRecord->addReference($document->uriOrThrow()); - $index->write($targetRecord); - - $fileRecord = $index->get(FileRecord::fromPath($document->uriOrThrow()->__toString())); - assert($fileRecord instanceof FileRecord); - - $fileRecord->addReference( - new RecordReference( - FunctionRecord::RECORD_TYPE, - $targetRecord->identifier(), - $node->getStartPosition(), - end: $node->getEndPosition() - ) - ); - $index->write($fileRecord); - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/InterfaceDeclarationIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/InterfaceDeclarationIndexer.php deleted file mode 100644 index 2209e7e214..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/InterfaceDeclarationIndexer.php +++ /dev/null @@ -1,53 +0,0 @@ -name instanceof MissingToken) { - throw new CannotIndexNode(sprintf( - 'Class name is missing (maybe a reserved word) in: %s', - $document->uri()?->__toString() ?? '?', - )); - } - $record = $this->getClassLikeRecord(ClassRecord::TYPE_INTERFACE, $node, $index, $document); - - // remove any references to this interface and other classes before - // updating with the current data - $this->removeImplementations($index, $record); - $record->clearImplemented(); - - $this->indexImplementedInterfaces($index, $record, $node); - - $index->write($record); - } - - private function indexImplementedInterfaces(Index $index, ClassRecord $classRecord, InterfaceDeclaration $node): void - { - if (null === $interfaceClause = $node->interfaceBaseClause) { - return; - } - - if (null == $interfaceList = $interfaceClause->interfaceNameList) { - return; - } - - $this->indexInterfaceList($interfaceList, $classRecord, $index); - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php deleted file mode 100644 index 20ea9cc97a..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php +++ /dev/null @@ -1,238 +0,0 @@ -get(FileRecord::fromPath($document->uriOrThrow()->__toString())); - assert($fileRecord instanceof FileRecord); - - foreach ($fileRecord->references() as $outgoingReference) { - if ($outgoingReference->type() !== MemberRecord::RECORD_TYPE) { - continue; - } - - $memberRecord = $index->get(MemberRecord::fromIdentifier($outgoingReference->identifier())); - assert($memberRecord instanceof MemberRecord); - $memberRecord->removeReference($fileRecord->identifier()); - $index->write($memberRecord); - $fileRecord->removeReferencesToRecordType($outgoingReference->type()); - $index->write($fileRecord); - } - } - - public function index(Index $index, TextDocument $document, Node $node): void - { - if ($node instanceof TraitSelectOrAliasClause) { - $this->indexTraitSelectOrAliasClause($index, $document, $node); - return; - } - if ($node instanceof ScopedPropertyAccessExpression) { - $this->indexScopedPropertyAccess($index, $document, $node); - return; - } - - if ($node instanceof MemberAccessExpression) { - $this->indexMemberAccessExpression($index, $document, $node); - return; - } - } - - /** - * @param MemberRecord::TYPE_* $memberType - */ - private function indexScopedPropertyAccess(Index $index, TextDocument $document, ScopedPropertyAccessExpression $node, ?string $memberType = null): void - { - $containerType = $node->scopeResolutionQualifier; - - if (!$containerType instanceof QualifiedName) { - return; - } - - $containerType = $this->resolveContainerType($containerType, $node); - $memberName = $this->resolveScopedPropertyAccessName($node); - - if ($memberName === '') { - return; - } - - $memberType = $memberType ?? $this->resolveScopedPropertyAccessMemberType($node); - - $this->writeIndex( - $index, - $memberType, - $containerType, - $memberName, - $document, - $this->resolveStart($node->memberName), - $this->resolveEnd($node->memberName) - ); - } - - /** - * @return MemberRecord::TYPE_* - */ - private function resolveScopedPropertyAccessMemberType(ScopedPropertyAccessExpression $node): string - { - if ($node->parent instanceof CallExpression) { - return MemberRecord::TYPE_METHOD; - } - - if ($node->memberName instanceof Variable) { - return MemberRecord::TYPE_PROPERTY; - } - - return MemberRecord::TYPE_CONSTANT; - } - - /** - * @return MemberRecord::TYPE_METHOD|MemberRecord::TYPE_PROPERTY - */ - private function resolveMemberAccessType(MemberAccessExpression $node): string - { - if ($node->parent instanceof CallExpression) { - return MemberRecord::TYPE_METHOD; - } - - return MemberRecord::TYPE_PROPERTY; - } - - private function resolveScopedPropertyAccessName(ScopedPropertyAccessExpression $node): string - { - $memberName = $node->memberName; - - if ($memberName instanceof Token) { - return (string)$memberName->getText($node->getFileContents()); - } - - if (!$memberName instanceof Variable) { - return ''; - } - - return (string)$memberName->getName(); - } - - private function indexMemberAccessExpression(Index $index, TextDocument $document, MemberAccessExpression $node): void - { - $memberName = $node->memberName; - - /** @phpstan-ignore-next-line Member name could be NULL */ - if (null === $memberName) { - return; - } - - if (!$memberName instanceof Token) { - return; - } - - $memberName = $memberName->getText($node->getFileContents()); - - if (empty($memberName)) { - return; - } - - $memberType = $this->resolveMemberAccessType($node); - - $this->writeIndex( - $index, - $memberType, - null, - (string)$memberName, - $document, - $this->resolveStart($node->memberName), - $this->resolveEnd($node->memberName) - ); - } - - /** - * @param MemberRecord::TYPE_* $memberType - */ - private function writeIndex( - Index $index, - string $memberType, - ?string $containerFqn, - string $memberName, - TextDocument $document, - int $offsetStart, - int $offsetEnd - ): void { - $record = $index->get(MemberRecord::fromMemberReference(MemberReference::create($memberType, $containerFqn, $memberName))); - assert($record instanceof MemberRecord); - $record->addReference($document->uriOrThrow()->__toString()); - $index->write($record); - - $fileRecord = $index->get(FileRecord::fromPath($document->uriOrThrow()->__toString())); - assert($fileRecord instanceof FileRecord); - $fileRecord->addReference( - RecordReference::fromRecordAndOffsetAndContainerType($record, $offsetStart, $offsetEnd, $containerFqn) - ); - $index->write($fileRecord); - } - - /** - * @param Token|Node $nodeOrToken - */ - private function resolveStart($nodeOrToken): int - { - if ($nodeOrToken instanceof Token) { - return $nodeOrToken->start; - } - - return $nodeOrToken->getStartPosition(); - } - - /** - * @param Token|Node $nodeOrToken - */ - private function resolveEnd($nodeOrToken): int - { - if ($nodeOrToken instanceof Token) { - return $nodeOrToken->start + $nodeOrToken->length; - } - - return $nodeOrToken->getEndPosition(); - } - - private function resolveContainerType(QualifiedName $containerType, Node $node): ?string - { - $containerType = (string)$containerType->getResolvedName(); - - // let static analysis solve these later - we cannot determine the - // correct values efficiently now (traits etc). - if (in_array($containerType, ['self', 'static', 'parent'])) { - return null; - } - - return $containerType; - } - - private function indexTraitSelectOrAliasClause(Index $index, TextDocument $document, TraitSelectOrAliasClause $node): void - { - if ($node->name instanceof ScopedPropertyAccessExpression) { - $this->indexScopedPropertyAccess($index, $document, $node->name, MemberRecord::TYPE_METHOD); - } - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/TraitDeclarationIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/TraitDeclarationIndexer.php deleted file mode 100644 index 5c144afec4..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/TraitDeclarationIndexer.php +++ /dev/null @@ -1,32 +0,0 @@ -name instanceof MissingToken) { - throw new CannotIndexNode(sprintf( - 'Class name is missing (maybe a reserved word) in: %s', - $document->uri()?->__toString() ?? '?', - )); - } - $record = $this->getClassLikeRecord(ClassRecord::TYPE_TRAIT, $node, $index, $document); - $index->write($record); - } -} diff --git a/lib/Indexer/Adapter/Tolerant/Indexer/TraitUseClauseIndexer.php b/lib/Indexer/Adapter/Tolerant/Indexer/TraitUseClauseIndexer.php deleted file mode 100644 index de56c49ab4..0000000000 --- a/lib/Indexer/Adapter/Tolerant/Indexer/TraitUseClauseIndexer.php +++ /dev/null @@ -1,60 +0,0 @@ -traitNameList) { - return; - } - - foreach ($node->traitNameList->children as $qualifiedName) { - if (!$qualifiedName instanceof QualifiedName) { - continue; - } - - /** @var ClassDeclaration|EnumDeclaration|null $parentDeclaration */ - $parentDeclaration = $node->getFirstAncestor(ClassDeclaration::class, EnumDeclaration::class); - - if ($parentDeclaration === null) { - continue; - } - - $traitRecord = $index->get(ClassRecord::fromName( - // This call is a hack from WorseReflection (!) because of a bug in - // the tolerant PHP parser which does not provide the resolved - // use namespace. - TolerantQualifiedNameResolver::getResolvedName($qualifiedName) - )); - - assert($traitRecord instanceof ClassRecord); - $traitRecord->addImplementation(FullyQualifiedName::fromString($parentDeclaration->getNamespacedName()->__toString())); - $index->write($traitRecord); - } - } - - public function beforeParse(Index $index, TextDocument $document): void - { - } -} diff --git a/lib/Indexer/Adapter/Tolerant/TolerantIndexBuilder.php b/lib/Indexer/Adapter/Tolerant/TolerantIndexBuilder.php deleted file mode 100644 index 774c66c550..0000000000 --- a/lib/Indexer/Adapter/Tolerant/TolerantIndexBuilder.php +++ /dev/null @@ -1,102 +0,0 @@ -indexers as $indexer) { - $indexer->beforeParse($this->index, $document); - } - - $node = $this->parser->get($document); - $this->indexNode($document, $node); - } - - public function done(): void - { - $this->index->done(); - } - - private function indexNode(TextDocument $document, Node $node): void - { - foreach ($this->indexers as $indexer) { - try { - if ($indexer->canIndex($node)) { - $indexer->index($this->index, $document, $node); - } - } catch (CannotIndexNode $cannotIndexNode) { - $this->logger->warning(sprintf( - 'Cannot index node of class "%s" in file "%s": %s', - get_class($node), - $document->uri()?->__toString() ?? 'unknown', - $cannotIndexNode->getMessage() - )); - } catch (Throwable $cannotIndexNode) { - throw new RuntimeException(sprintf( - 'Could not index document "%s": %s', - $document->uri() ?? '', - $cannotIndexNode->getMessage() - ), 0, $cannotIndexNode); - } - } - - foreach ($node->getChildNodes() as $childNode) { - $this->indexNode($document, $childNode); - } - } -} diff --git a/lib/Indexer/Adapter/Tolerant/TolerantIndexer.php b/lib/Indexer/Adapter/Tolerant/TolerantIndexer.php deleted file mode 100644 index fd34db8cb4..0000000000 --- a/lib/Indexer/Adapter/Tolerant/TolerantIndexer.php +++ /dev/null @@ -1,16 +0,0 @@ -__toString() === '') { - throw new SourceNotFound('Name is empty'); - } - - $record = $this->index->get(ClassRecord::fromName($name->__toString())); - $filePath = $record->filePath(); - - if (null === $filePath || !file_exists($filePath)) { - throw new SourceNotFound(sprintf( - 'Class "%s" is indexed, but it does not exist at path "%s"!', - $name->full(), - $filePath - )); - } - - return TextDocumentBuilder::fromUri($filePath)->build(); - } -} diff --git a/lib/Indexer/Adapter/Worse/IndexerConstantSourceLocator.php b/lib/Indexer/Adapter/Worse/IndexerConstantSourceLocator.php deleted file mode 100644 index a52d2168d2..0000000000 --- a/lib/Indexer/Adapter/Worse/IndexerConstantSourceLocator.php +++ /dev/null @@ -1,45 +0,0 @@ -__toString())) { - throw new SourceNotFound('Name is empty'); - } - - $record = $this->index->get( - ConstantRecord::fromName($name->__toString()) - ); - - $filePath = $record->filePath(); - if (null === $filePath) { - throw new SourceNotFound('constant not indexed'); - } - - if (!file_exists($filePath)) { - throw new SourceNotFound(sprintf( - 'Constant "%s" is indexed, but it does not exist at path "%s"!', - $name->full(), - $filePath - )); - } - - return TextDocumentBuilder::fromUri($filePath)->build(); - } -} diff --git a/lib/Indexer/Adapter/Worse/IndexerFunctionSourceLocator.php b/lib/Indexer/Adapter/Worse/IndexerFunctionSourceLocator.php deleted file mode 100644 index c78a3bb6bc..0000000000 --- a/lib/Indexer/Adapter/Worse/IndexerFunctionSourceLocator.php +++ /dev/null @@ -1,42 +0,0 @@ -__toString() === '') { - throw new SourceNotFound('Name is empty'); - } - - $record = $this->index->get( - FunctionRecord::fromName($name->__toString()) - ); - - $filePath = $record->filePath(); - - if (null === $filePath || !file_exists($filePath)) { - throw new SourceNotFound(sprintf( - 'Function "%s" is indexed, but it does not exist at path "%s"!', - $name->full(), - $filePath ?? '' - )); - } - - return TextDocumentBuilder::fromUri($filePath)->build(); - } -} diff --git a/lib/Indexer/Adapter/Worse/WorseRecordReferenceEnhancer.php b/lib/Indexer/Adapter/Worse/WorseRecordReferenceEnhancer.php deleted file mode 100644 index 99a02bf9f1..0000000000 --- a/lib/Indexer/Adapter/Worse/WorseRecordReferenceEnhancer.php +++ /dev/null @@ -1,75 +0,0 @@ -type() !== MemberRecord::RECORD_TYPE) { - return $reference; - } - - if ($reference->contaninerType()) { - return $reference; - } - $filePath = $record->filePath(); - if (!$filePath) { - return $reference; - } - - try { - $contents = $this->locator->get(TextDocumentUri::fromString($filePath)); - } catch (TextDocumentNotFound $error) { - $this->logger->warning(sprintf( - 'Record Enhancer: Could not read file "%s": %s', - $record->filePath(), - $error->getMessage() - )); - return $reference; - } - - try { - $offset = $this->reflector->reflectOffset($contents, $reference->start()); - } catch (NotFound $notFound) { - $this->logger->debug(sprintf( - 'Record Enhancer: Could not reflect offset %s in file "%s": %s', - $reference->start(), - $record->filePath(), - $notFound->getMessage() - )); - return $reference; - } - - $containerType = $offset->nodeContext()->containerType(); - - if (!($containerType->isDefined())) { - return $reference; - } - - if ($containerType instanceof ClassType) { - $containerType = $containerType->name()->__toString(); - } - - return $reference->withContainerType($containerType); - } -} diff --git a/lib/Indexer/Extension/Command/IndexBuildCommand.php b/lib/Indexer/Extension/Command/IndexBuildCommand.php deleted file mode 100644 index d06356a909..0000000000 --- a/lib/Indexer/Extension/Command/IndexBuildCommand.php +++ /dev/null @@ -1,131 +0,0 @@ -usage = MemoryUsage::create(); - } - - protected function configure(): void - { - $this->setDescription('Build the index'); - $this->addArgument(self::ARG_SUB_PATH, InputArgument::OPTIONAL, 'Sub path to index'); - $this->addOption(self::OPT_RESET, null, InputOption::VALUE_NONE, 'Purge index before building'); - $this->addOption(self::OPT_WATCH, null, InputOption::VALUE_NONE, 'Watch for updated files (poll for changes ever x seconds, default 10)'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $subPath = Cast::toStringOrNull($input->getArgument(self::ARG_SUB_PATH)); - $watch = Cast::toBool($input->getOption(self::OPT_WATCH)); - - if ($input->getOption(self::OPT_RESET)) { - $this->indexer->reset(); - } - - if (is_string($subPath)) { - $subPath = Path::join( - Cast::toStringOrNull(getcwd()), - $subPath - ); - } - - $this->buildIndex($output, $subPath); - - if ($watch) { - $this->watch($output); - } - - return 0; - } - - private function buildIndex(OutputInterface $output, ?string $subPath = null): void - { - $start = microtime(true); - - $output->write('Building job...'); - $job = $this->indexer->getJob($subPath); - $output->writeln('done'); - $output->writeln('Building index:'); - $output->write("\n"); - - if ($job->size() === 0) { - $output->writeln('No files found'); - return; - } - - $progress = new ProgressBar($output, $job->size(), 0.001); - $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%'); - $progress->setPlaceholderFormatterDefinition('memory', function () { - return MemoryUsage::create()->memoryUsageFormatted(); - }); - foreach ($job->generator() as $filePath) { - if ($output->isVerbose()) { - $output->writeln(sprintf('Updated %s', $filePath)); - continue; - } - $progress->advance(); - } - - $progress->finish(); - $output->write("\n"); - $output->write("\n"); - - $output->writeln(sprintf( - 'Done in %s seconds using %sb of memory', - number_format(microtime(true) - $start, 2), - number_format(memory_get_usage(true)) - )); - } - - private function watch(OutputInterface $output): void - { - Loop::run(function () use ($output) { - $process = yield $this->watcher->watch(); - - // Signals are not supported on Windows - if (defined('SIGINT')) { - Loop::onSignal(SIGINT, function () use ($output, $process): void { - $output->write('Shutting down watchers...'); - $process->stop(); - $output->writeln('done'); - Loop::stop(); - }); - } - - $output->writeln(sprintf('Watching for file changes with %s...', $this->watcher->describe())); - - while (null !== $file = yield $process->wait()) { - $job = $this->indexer->getJob($file->path()); - foreach ($job->generator() as $filePath) { - $output->writeln(sprintf('Updating %s', $filePath)); - } - } - }); - } -} diff --git a/lib/Indexer/Extension/Command/IndexCleanCommand.php b/lib/Indexer/Extension/Command/IndexCleanCommand.php deleted file mode 100644 index 3fd2a5c48f..0000000000 --- a/lib/Indexer/Extension/Command/IndexCleanCommand.php +++ /dev/null @@ -1,185 +0,0 @@ -setDescription('Removing a project index from the cache'); - $this->setHelp(sprintf(<< - - Removing an index by the number in the list view: - bin/console index:clean - - Removing all indicies - bin/console index:clean %s - - === Interactive version === - Listing the available indices and asking which ones should be removed - bin/console index:clean - - DOCS, self::OPT_CLEAN_ALL)); - $this->addArgument(self::ARG_INDEX_NAME, InputArgument::IS_ARRAY, 'Index names to delete'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - if (!$output instanceof ConsoleOutput) { - throw new RuntimeException('Symfony'); - } - - $indexNames = $input->getArgument(self::ARG_INDEX_NAME); - - // Non interactive commands with no index to delete should do nothing - if (count($indexNames) === 0 && !$input->isInteractive()) { - return 0; - } - - $section = $output->section(); - $indicies = $this->getIndicies($section); - if (count($indexNames) !== 0) { - if ($indexNames[0] === self::OPT_CLEAN_ALL) { - foreach ($indicies as $index) { - $this->removeIndex($output, $index); - } - return 0; - } - foreach ($indexNames as $indexName) { - $this->removeIndex($section, $indicies->get($indexName)); - } - return 0; - } - $index = null; - $section->clear(); - - while (true) { - $this->renderIndexTable($indicies, $section); - if ($index) { - $section->writeln(sprintf('Removed index "%s"', $index->name())); - } - try { - // pass $output instead of $section because the interactive input - // is corrupted with he Section. - $index = $this->getInteractiveAnswer($indicies, $input, $output); - } catch (Exception $e) { - $section->clear(); - $section->writeln(sprintf('%s', $e->getMessage())); - continue; - } - if (!$index) { - break; - } - - if ($index->absolutePath() === self::OPT_CLEAN_ALL) { - foreach ($indicies as $index) { - $this->removeIndex($output, $index); - } - break; - } - - $this->removeIndex($output, $index); - $indicies = $indicies->remove($index); - $section->clear(); - } - - return 0; - } - - private function renderIndexTable(IndexInfos $indexList, OutputInterface $output): void - { - $totalSize = 0; - $table = new Table($output); - $table->setHeaders(['#' , 'Directory', 'Size', 'Age', 'Modified']); - $offset = 1; - foreach ($indexList as $index) { - $totalSize += $index->size(); - $table->addRow([ - $offset++, - $index->name(), - PhpactorFilesystem::formatSize($index->size()), - sprintf('%.1f days', $index->ageInDays()), - sprintf('%.1f days', $index->lastModifiedInDays()), - ]); - } - $table->addRow(new TableSeparator()); - $table->addRow(['Σ', self::OPT_CLEAN_ALL, PhpactorFilesystem::formatSize($indexList->totalSize()), '', '']); - $table->render(); - - $output->writeln(sprintf('Total size: %s', PhpactorFilesystem::formatSize($totalSize))); - } - - private function getInteractiveAnswer(IndexInfos $infos, InputInterface $input, OutputInterface $output): ?IndexInfo - { - $question = new Question('Index to remove: ', null); - $question->setAutocompleterValues(array_merge($infos->offsets(), $infos->names(), [self::OPT_CLEAN_ALL])); - $result = (new QuestionHelper())->ask($input, $output, $question); - - if (!$result) { - return null; - } - - if ($result === self::OPT_CLEAN_ALL) { - return new IndexInfo(self::OPT_CLEAN_ALL, '', 0, 0, 0); - } - - if (is_numeric($result)) { - return $infos->getByOffset((int)$result); - } - - return $infos->get((string)$result); - } - - private function getIndicies(OutputInterface $output): IndexInfos - { - $indexes = []; - $progress = new ProgressBar($output); - foreach ($this->indexLister->list() as $info) { - $indexes[] = $info; - $progress->advance(); - } - $progress->finish(); - - return new IndexInfos($indexes); - } - - private function removeIndex(OutputInterface $output, IndexInfo $index): void - { - $output->writeln(sprintf('Removing %s', $index->name())); - $this->filesystem->remove($index->absolutePath()); - } -} diff --git a/lib/Indexer/Extension/Command/IndexOptimiseCommand.php b/lib/Indexer/Extension/Command/IndexOptimiseCommand.php deleted file mode 100644 index d67ccd9426..0000000000 --- a/lib/Indexer/Extension/Command/IndexOptimiseCommand.php +++ /dev/null @@ -1,82 +0,0 @@ -setDescription('Optimise the index'); - $this->addOption(self::OPT_DRY_RUN, null, InputOption::VALUE_NONE, 'Do not make any changes'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $start = microtime(true); - $dryRun = $input->getOption(self::OPT_DRY_RUN); - $optimisations = 0; - - $output->writeln('Optimising index...'); - - if ($output->isVerbose()) { - $progress = new ProgressBar(new NullOutput()); - } else { - $progress = new ProgressBar($output); - } - $progress->setFormat('%current% [%bar%] %optimisations% optimisations'); - $progress->setPlaceholderFormatterDefinition('optimisations', function () use (&$optimisations): string { - return (string)$optimisations; - }); - - foreach ($this->indexer->optimise((bool)$dryRun) as $tick) { - if ($tick !== null) { - $optimisations++; - if ($output->isVerbose()) { - $output->writeln($tick); - } - } - $progress->advance(); - } - - $progress->finish(); - - $output->write("\n"); - $output->write("\n"); - - if ($dryRun) { - $output->writeln(sprintf( - '%d optimisations would have been done in %s seconds using %sb of memory', - $optimisations, - number_format(microtime(true) - $start, 2), - number_format(memory_get_usage(true)) - )); - - return 0; - } - - $output->writeln(sprintf( - '%d optimisations done in %s seconds using %sb of memory', - $optimisations, - number_format(microtime(true) - $start, 2), - number_format(memory_get_usage(true)) - )); - - return 0; - } -} diff --git a/lib/Indexer/Extension/Command/IndexQueryCommand.php b/lib/Indexer/Extension/Command/IndexQueryCommand.php deleted file mode 100644 index d5703fc6e3..0000000000 --- a/lib/Indexer/Extension/Command/IndexQueryCommand.php +++ /dev/null @@ -1,108 +0,0 @@ -addArgument(self::ARG_IDENITIFIER, InputArgument::REQUIRED, 'Query (function name, class name, #)'); - $this->setDescription( - 'Show the indexed information for a given identifier' - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $class = $this->query->class()->get(Cast::toString($input->getArgument(self::ARG_IDENITIFIER))); - - if ($class) { - $this->renderClass($output, $class); - } - - $function = $this->query->function()->get( - Cast::toString($input->getArgument(self::ARG_IDENITIFIER)) - ); - - if ($function) { - $this->renderFunction($output, $function); - } - - $member = $this->query->member()->get(Cast::toString($input->getArgument(self::ARG_IDENITIFIER))); - - if ($member) { - $this->renderMember($output, $member); - } - - return 0; - } - - private function renderClass(OutputInterface $output, ClassRecord $class): void - { - $output->writeln('Class:'.$class->fqn()); - $output->writeln('Path:'.$class->filePath()); - $output->writeln('Type:'.$class->type()); - $output->writeln('Implements:'); - foreach ($class->implements() as $fqn) { - $output->writeln(' - ' . (string)$fqn); - } - $output->writeln('Implementations:'); - foreach ($class->implementations() as $fqn) { - $output->writeln(' - ' . (string)$fqn); - } - $output->writeln('Referenced by:'); - foreach ($class->references() as $path) { - $file = $this->query->file()->get($path); - $output->writeln(sprintf('- %s:%s', $path, implode(', ', array_map(function (RecordReference $reference) { - return $reference->start().'-'.$reference->end(); - }, $file->references()->to($class)->toArray())))); - } - } - - private function renderFunction(OutputInterface $output, FunctionRecord $function): void - { - $output->writeln('Function:'.$function->fqn()); - $output->writeln('Path:'.$function->filePath()); - $output->writeln('Referenced by:'); - foreach ($function->references() as $path) { - $file = $this->query->file()->get($path); - $output->writeln(sprintf('- %s:%s', $path, implode(', ', array_map(function (RecordReference $reference) { - return $reference->start().'-'.$reference->end(); - }, $file->references()->to($function)->toArray())))); - } - } - - private function renderMember(OutputInterface $output, MemberRecord $member): void - { - $output->writeln('Member:'.$member->memberName()); - $output->writeln('Member Type:'.$member->type()); - $output->writeln('Referenced by:'); - foreach ($this->query->member()->referencesTo($member->type(), $member->memberName()) as $index => $location) { - $output->writeln(sprintf( - '%-3d %s:%s-%s', - $index + 1 . '.', - $location->location()->uri()->path(), - $location->location()->range()->start()->toInt(), - $location->location()->range()->end()->toInt(), - )); - } - } -} diff --git a/lib/Indexer/Extension/Command/IndexSearchCommand.php b/lib/Indexer/Extension/Command/IndexSearchCommand.php deleted file mode 100644 index 82fa2efeb0..0000000000 --- a/lib/Indexer/Extension/Command/IndexSearchCommand.php +++ /dev/null @@ -1,122 +0,0 @@ -setDescription('Search the index'); - - $this->addOption(self::OPT_FQN_BEGINS, null, InputOption::VALUE_REQUIRED, 'FQN begins with'); - $this->addOption(self::OPT_SHORT_NAME_BEGINS, null, InputOption::VALUE_REQUIRED, 'Short-name begins with'); - $this->addOption(self::OPT_SHORT_NAME, null, InputOption::VALUE_REQUIRED, 'Exact short name'); - $this->addOption(self::OPT_IS_FUNCTION, null, InputOption::VALUE_NONE, 'Functions only'); - $this->addOption(self::OPT_IS_CONSTANT, null, InputOption::VALUE_NONE, 'Constants only'); - $this->addOption(self::OPT_IS_CLASS_LIKE, null, InputOption::VALUE_NONE, 'Class-likes. Shorthand for --is-trait --is-interface --is-class --is-enum'); - $this->addOption(self::OPT_IS_CLASS, null, InputOption::VALUE_NONE, 'Classes only'); - $this->addOption(self::OPT_IS_TRAIT, null, InputOption::VALUE_NONE, 'Traits only'); - $this->addOption(self::OPT_IS_INTERFACE, null, InputOption::VALUE_NONE, 'Interfaces only'); - $this->addOption(self::OPT_IS_ENUM, null, InputOption::VALUE_NONE, 'Enums only'); - $this->addOption(self::OPT_LIMIT, 'l', InputOption::VALUE_REQUIRED, 'Limit number of results'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $shortName = $input->getOption(self::OPT_SHORT_NAME); - $shortNameBegins = $input->getOption(self::OPT_SHORT_NAME_BEGINS); - $fqnBegins = $input->getOption(self::OPT_FQN_BEGINS); - $isFunction = $input->getOption(self::OPT_IS_FUNCTION); - $isConstant = $input->getOption(self::OPT_IS_CONSTANT); - $isClassLike = $input->getOption(self::OPT_IS_CLASS_LIKE); - $isClass = $input->getOption(self::OPT_IS_CLASS); - $isTrait = $input->getOption(self::OPT_IS_TRAIT); - $isInterface = $input->getOption(self::OPT_IS_INTERFACE); - $isEnum = $input->getOption(self::OPT_IS_ENUM); - $limitRaw = $input->getOption(self::OPT_LIMIT); - $limit = is_numeric($limitRaw) ? (int)$limitRaw : null; - - $criterias = []; - - if ($shortName) { - $criterias[] = Criteria::exactShortName($shortName); - } - - if ($shortNameBegins) { - $criterias[] = Criteria::shortNameBeginsWith($shortNameBegins); - } - - if ($fqnBegins) { - $criterias[] = Criteria::fqnBeginsWith($fqnBegins); - } - - if ($isFunction) { - $criterias[] = Criteria::isFunction(); - } - - if ($isConstant) { - $criterias[] = Criteria::isConstant(); - } - - if ($isClassLike) { - $criterias[] = Criteria::isClass(); - } - - if ($isClass) { - $criterias[] = Criteria::isClassConcrete(); - } - - if ($isTrait) { - $criterias[] = Criteria::isClassTrait(); - } - - if ($isInterface) { - $criterias[] = Criteria::isClassInterface(); - } - - if ($isEnum) { - $criterias[] = Criteria::isClassEnum(); - } - - foreach ($this->searchClient->search(Criteria::and(...$criterias)) as $index => $result) { - if ($limit && $index === $limit) { - break; - } - $output->writeln(sprintf( - '%s # %s%s', - $result->recordType(), - $result->identifier(), - $result instanceof ClassRecord ? sprintf(' (%s)', $result->type()) : '', - )); - } - - return 0; - } -} diff --git a/lib/Indexer/Extension/IndexerExtension.php b/lib/Indexer/Extension/IndexerExtension.php deleted file mode 100644 index 3b11d8735d..0000000000 --- a/lib/Indexer/Extension/IndexerExtension.php +++ /dev/null @@ -1,466 +0,0 @@ -setDefaults([ - self::PARAM_ENABLED_WATCHERS => ['inotify', 'watchman', 'find', 'php'], - self::PARAM_INDEX_PATH => '%cache%/index/%project_id%', - self::PARAM_INCLUDE_PATTERNS => [ - '/**/*.php', - '/**/*.phar', - ], - self::PARAM_EXCLUDE_PATTERNS => [ - '/vendor/**/Tests/**/*', - '/vendor/**/tests/**/*', - '/vendor/composer/**/*', - // rector frequently breaks phpunit testcase reflection so just - // ignore the stubs by default - '/vendor/rector/rector/stubs-rector' - ], - self::PARAM_STUB_PATHS => [], - self::PARAM_INDEXER_POLL_TIME => 5000, - self::PARAM_INDEXER_BUFFER_TIME => 500, - self::PARAM_INDEXER_FOLLOW_SYMLINKS => false, - self::PARAM_INDEXER_MAX_FILESIZE_TO_INDEX => 1_000_000, - self::PARAM_PROJECT_ROOT => '%project_root%', - self::PARAM_REFERENCES_DEEP_REFERENCES => true, - self::PARAM_IMPLEMENTATIONS_DEEP_REFERENCES => true, - self::PARAM_SUPPORTED_EXTENSIONS => ['php', 'phar'], - self::PARAM_SEARCH_INCLUDE_PATTERNS => [], - ]); - $schema->setDescriptions([ - self::PARAM_ENABLED_WATCHERS => 'List of allowed watchers. The first watcher that supports the current system will be used', - self::PARAM_INDEX_PATH => 'Path where the index should be saved', - self::PARAM_STUB_PATHS => 'Paths to external folders to index. They will be indexed only once, if you want to take any changes into account you will have to reindex your project manually.', - self::PARAM_INCLUDE_PATTERNS => 'Glob patterns to include while indexing', - self::PARAM_EXCLUDE_PATTERNS => 'Glob patterns to exclude while indexing', - self::PARAM_INDEXER_POLL_TIME => 'For polling indexers only: the time, in milliseconds, between polls (e.g. filesystem scans)', - self::PARAM_INDEXER_BUFFER_TIME => 'For real-time indexers only: the time, in milliseconds, to buffer the results', - self::PARAM_INDEXER_FOLLOW_SYMLINKS => 'To allow indexer to follow symlinks', - self::PARAM_INDEXER_MAX_FILESIZE_TO_INDEX => 'Files larger than this will not be indexed. (Size in bytes)', - self::PARAM_PROJECT_ROOT => 'The root path to use for scanning the index', - self::PARAM_REFERENCES_DEEP_REFERENCES => 'Recurse over class implementations to resolve all references', - self::PARAM_IMPLEMENTATIONS_DEEP_REFERENCES => 'Recurse over class implementations to resolve all class implementations (not just the classes directly implementing the subject)', - self::PARAM_SUPPORTED_EXTENSIONS => 'File extensions (e.g. `php`) for files that should be indexed', - self::PARAM_SEARCH_INCLUDE_PATTERNS => 'When searching the index exclude records whose fully qualified names match any of these regex patterns (use to exclude suggestions from search results). Namespace separators must be escaped as `\\\\\\\\` for example `^Foo\\\\\\\\` to include all namespaces whose first segment is `Foo`', - ]); - $schema->setTypes([ - self::PARAM_ENABLED_WATCHERS => 'array', - self::PARAM_INDEX_PATH => 'string', - self::PARAM_INCLUDE_PATTERNS => 'array', - self::PARAM_EXCLUDE_PATTERNS => 'array', - self::PARAM_STUB_PATHS => 'array', - self::PARAM_INDEXER_POLL_TIME => 'integer', - self::PARAM_INDEXER_BUFFER_TIME => 'integer', - self::PARAM_INDEXER_FOLLOW_SYMLINKS => 'boolean', - self::PARAM_INDEXER_MAX_FILESIZE_TO_INDEX => 'integer', - self::PARAM_PROJECT_ROOT => 'string', - self::PARAM_REFERENCES_DEEP_REFERENCES => 'boolean', - self::PARAM_IMPLEMENTATIONS_DEEP_REFERENCES => 'boolean', - self::PARAM_SUPPORTED_EXTENSIONS => 'array', - self::PARAM_SEARCH_INCLUDE_PATTERNS => 'array', - ]); - } - - - public function load(ContainerBuilder $container): void - { - $this->registerCommands($container); - $this->registerModel($container); - $this->registerWorseAdapters($container); - $this->registerRpc($container); - $this->registerReferenceFinderAdapters($container); - $this->registerWatcher($container); - } - - private function registerWorseAdapters(ContainerBuilder $container): void - { - $container->register(IndexerClassSourceLocator::class, function (Container $container) { - return new IndexerClassSourceLocator($container->get(IndexAccess::class)); - }, [ - WorseReflectionExtension::TAG_SOURCE_LOCATOR => [ - 'priority' => 128, - ] - ]); - - $container->register(IndexerFunctionSourceLocator::class, function (Container $container) { - return new IndexerFunctionSourceLocator($container->get(IndexAccess::class)); - }, [ - WorseReflectionExtension::TAG_SOURCE_LOCATOR => [ - 'priority' => 128, - ], - ]); - $container->register(IndexerConstantSourceLocator::class, function (Container $container) { - return new IndexerConstantSourceLocator($container->get(IndexAccess::class)); - }, [ - WorseReflectionExtension::TAG_SOURCE_LOCATOR => [ - 'priority' => 100, - ], - ]); - } - - private function registerCommands(ContainerBuilder $container): void - { - $container->register(IndexBuildCommand::class, function (Container $container) { - return new IndexBuildCommand( - $container->get(Indexer::class), - $container->get(Watcher::class) - ); - }, [ ConsoleExtension::TAG_COMMAND => ['name' => 'index:build']]); - - $container->register(IndexOptimiseCommand::class, function (Container $container) { - return new IndexOptimiseCommand( - $container->get(Indexer::class), - ); - }, [ ConsoleExtension::TAG_COMMAND => ['name' => 'index:optimise']]); - - $container->register(IndexCleanCommand::class, function (Container $container) { - $indexPath = $container->get( - FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER - )->resolve($container->parameter(self::PARAM_INDEX_PATH)->string()); - - $indexPath = dirname($indexPath); - return new IndexCleanCommand(new PhpIndexerLister($indexPath), new Filesystem()); - }, [ ConsoleExtension::TAG_COMMAND => ['name' => 'index:clean']]); - - $container->register(IndexQueryCommand::class, function (Container $container) { - return new IndexQueryCommand($container->get(QueryClient::class)); - }, [ ConsoleExtension::TAG_COMMAND => ['name' => 'index:query']]); - - $container->register(IndexSearchCommand::class, function (Container $container) { - return new IndexSearchCommand($container->get(SearchClient::class)); - }, [ ConsoleExtension::TAG_COMMAND => ['name' => 'index:search']]); - } - - private function registerModel(ContainerBuilder $container): void - { - $container->register(IndexAgent::class, function (Container $container) { - return $container->get(IndexAgentBuilder::class) - ->setReferenceEnhancer($container->get(WorseRecordReferenceEnhancer::class)) - ->buildAgent(); - }); - - $container->register(IndexAccess::class, function (Container $container) { - // the worse reflection locators would have a circular reference so - // we create a new instance for them. - return $container->get(IndexAgentBuilder::class) - ->buildAgent()->access(); - }); - - $container->register(QueryClient::class, function (Container $container) { - return $container->get(IndexAgent::class)->query(); - }); - - $container->register(SearchClient::class, function (Container $container) { - return $container->get(IndexAgent::class)->search(); - }); - - $container->register(IndexAgentBuilder::class, function (Container $container) { - $resolver = $container->expect( - FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, - PathResolver::class - ); - $indexPath = $resolver->resolve($container->parameter(self::PARAM_INDEX_PATH)->string()); - - /** @var array $stubPaths */ - $stubPaths = $container->parameter(self::PARAM_STUB_PATHS)->value(); - $stubPaths = array_map(fn (string $path): string => $resolver->resolve($path), $stubPaths); - - return IndexAgentBuilder::create($indexPath, $this->projectRoot($container)) - /** @phpstan-ignore-next-line */ - ->setExcludePatterns($container->get(self::SERVICE_INDEXER_EXCLUDE_PATTERNS)) - /** @phpstan-ignore-next-line */ - ->setIncludePatterns($container->get(self::SERVICE_INDEXER_INCLUDE_PATTERNS)) - ->setSearchIncludePatterns($container->parameter(self::PARAM_SEARCH_INCLUDE_PATTERNS)->listOfString()) - /** @phpstan-ignore-next-line */ - ->setSupportedExtensions($container->parameter(self::PARAM_SUPPORTED_EXTENSIONS)->value()) - ->setFollowSymlinks($container->parameter(self::PARAM_INDEXER_FOLLOW_SYMLINKS)->bool()) - ->setMaxFileSizeToIndex($container->parameter(self::PARAM_INDEXER_MAX_FILESIZE_TO_INDEX)->int()) - ->setStubPaths($stubPaths); - }); - - $container->register(Indexer::class, function (Container $container) { - return $container->get(IndexAgent::class)->indexer(); - }); - - $container->register(self::SERVICE_INDEXER_EXCLUDE_PATTERNS, function (Container $container) { - $projectRoot = $this->projectRoot($container); - return array_map(function (string $pattern) use ($projectRoot) { - return Path::join($projectRoot, $pattern); - }, $container->getParameter(self::PARAM_EXCLUDE_PATTERNS)); - }); - - $container->register(self::SERVICE_INDEXER_INCLUDE_PATTERNS, function (Container $container) { - $projectRoot = $container->getParameter(FilePathResolverExtension::PARAM_PROJECT_ROOT); - - return array_map(function (string $pattern) use ($projectRoot) { - return Path::join($projectRoot, $pattern); - }, $container->getParameter(self::PARAM_INCLUDE_PATTERNS)); - }); - - $container->register(WorseRecordReferenceEnhancer::class, function (Container $container) { - return new WorseRecordReferenceEnhancer( - $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class), - $this->logger($container), - $container->has(TextDocumentLocator::class) ? $container->get(TextDocumentLocator::class) : new FilesystemTextDocumentLocator(), - ); - }); - } - - private function registerReferenceFinderAdapters(ContainerBuilder $container): void - { - $container->register(IndexedImplementationFinder::class, function (Container $container) { - return new IndexedImplementationFinder( - $container->get(QueryClient::class), - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - $container->parameter(self::PARAM_IMPLEMENTATIONS_DEEP_REFERENCES)->bool() - ); - }, [ ReferenceFinderExtension::TAG_IMPLEMENTATION_FINDER => []]); - - $container->register(IndexedReferenceFinder::class, function (Container $container) { - return new IndexedReferenceFinder( - $container->get(QueryClient::class), - $container->get(WorseReflectionExtension::SERVICE_REFLECTOR), - new ContainerTypeResolver($container->get(WorseReflectionExtension::SERVICE_REFLECTOR)), - $container->parameter(self::PARAM_REFERENCES_DEEP_REFERENCES)->bool() - ); - }, [ ReferenceFinderExtension::TAG_REFERENCE_FINDER => []]); - - $container->register(IndexedNameSearcher::class, function (Container $container) { - return new IndexedNameSearcher( - $container->get(SearchClient::class) - ); - }, [ ReferenceFinderExtension::TAG_NAME_SEARCHER => []]); - } - - private function registerRpc(ContainerBuilder $container): void - { - if (!class_exists(RpcExtension::class)) { - return; - } - - $container->register(IndexHandler::class, function (Container $container) { - return new IndexHandler( - $container->get(Indexer::class), - $container->get(Watcher::class) - ); - }, [ - RpcExtension::TAG_RPC_HANDLER => [ - 'name' => IndexHandler::NAME, - ], - ]); - } - - private function registerWatcher(ContainerBuilder $container): void - { - $container->register(Watcher::class, function (Container $container) { - $watchers = []; - - foreach ($container->getServiceIdsForTag(self::TAG_WATCHER) as $serviceId => $attrs) { - if (!isset($attrs['name'])) { - throw new RuntimeException(sprintf( - 'Watcher "%s" must provide the `name` attribute', - $serviceId - )); - } - - $watchers[$attrs['name']] = $serviceId; - } - - /** @var list $enabledWatchers */ - $enabledWatchers = $container->getParameter(self::PARAM_ENABLED_WATCHERS); - if ($diff = array_diff($enabledWatchers, array_keys($watchers))) { - throw new RuntimeException(sprintf( - 'Unknown watchers "%s" specified, available watchers: "%s"', - implode('", "', $diff), - implode('", "', array_keys($watchers)) - )); - } - - $watchers = (function (Container $container, array $watchers, array $enabledWatchers) { - $filtered = []; - foreach ($watchers as $name => $serviceId) { - if (!in_array($name, $enabledWatchers)) { - continue; - } - - $filtered[$name] = $container->get($serviceId); - }; - - $ordered = []; - foreach ($enabledWatchers as $enabledWatcher) { - $ordered[] = $filtered[$enabledWatcher]; - } - - return $ordered; - })($container, $watchers, $enabledWatchers); - - if ($watchers === []) { - return new NullWatcher(); - } - - return new PatternMatchingWatcher( - new FallbackWatcher($watchers, $this->logger($container)), - $container->get(self::SERVICE_INDEXER_INCLUDE_PATTERNS), - $container->get(self::SERVICE_INDEXER_EXCLUDE_PATTERNS) - ); - }); - $container->register(WatcherConfig::class, function (Container $container) { - $resolver = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER); - assert($resolver instanceof PathResolver); - - // NOTE: the project root should NOT have a scheme in it (file://), but there is no validation - // about this, so we parse it using the text document URI - $path = TextDocumentUri::fromString($resolver->resolve('%project_root%')); - - return new WatcherConfig([ - $path->path() - ], $container->parameter(self::PARAM_INDEXER_POLL_TIME)->int()); - }); - - // register watchers - order of registration currently determines - // priority - - $container->register(WatchmanWatcher::class, function (Container $container) { - return new BufferedWatcher(new WatchmanWatcher( - $container->get(WatcherConfig::class), - $this->logger($container) - ), $container->parameter(self::PARAM_INDEXER_BUFFER_TIME)->int()); - }, [ - self::TAG_WATCHER => [ - 'name' => 'watchman', - ] - ]); - - $container->register(InotifyWatcher::class, function (Container $container) { - return new BufferedWatcher(new InotifyWatcher( - $container->get(WatcherConfig::class), - $this->logger($container) - ), $container->parameter(self::PARAM_INDEXER_BUFFER_TIME)->int()); - }, [ - self::TAG_WATCHER => [ - 'name' => 'inotify', - ] - ]); - - $container->register(FsWatchWatcher::class, function (Container $container) { - return new FsWatchWatcher( - $container->get(WatcherConfig::class), - $this->logger($container) - ); - }, [ - self::TAG_WATCHER => [ - 'name' => 'fswatch', - ] - ]); - - $container->register(FindWatcher::class, function (Container $container) { - return new FindWatcher( - $container->get(WatcherConfig::class), - $this->logger($container) - ); - }, [ - self::TAG_WATCHER => [ - 'name' => 'find', - ] - ]); - - $container->register(PhpPollWatcher::class, function (Container $container) { - return new PhpPollWatcher( - $container->get(WatcherConfig::class), - $this->logger($container) - ); - }, [ - self::TAG_WATCHER => [ - 'name' => 'php', - ] - ]); - } - - private function projectRoot(Container $container): string - { - return $container->get( - FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER - )->resolve($container->parameter(self::PARAM_PROJECT_ROOT)->string()); - } - - private function logger(Container $container): LoggerInterface - { - return LoggingExtension::channelLogger($container, 'indexer'); - } -} diff --git a/lib/Indexer/Extension/Rpc/IndexHandler.php b/lib/Indexer/Extension/Rpc/IndexHandler.php deleted file mode 100644 index 1eab604c0e..0000000000 --- a/lib/Indexer/Extension/Rpc/IndexHandler.php +++ /dev/null @@ -1,69 +0,0 @@ -setDefaults([ - self::PARAM_WATCH => false, - self::PARAM_INTERVAL => 5000 - ]); - $resolver->setTypes([ - self::PARAM_INTERVAL => 'integer' - ]); - } - - /** - * @param array $arguments - */ - public function handle(array $arguments): Response - { - $job = $this->indexer->getJob(); - $job->run(); - - if ($arguments[self::PARAM_WATCH] === true) { - Loop::run(function () use ($arguments) { - $process = yield $this->watcher->watch(); - - while (null !== $file = yield $process->wait()) { - assert($file instanceof ModifiedFile); - $job = $this->indexer->getJob($file->path()); - $job->run(); - yield new Delayed($arguments[self::PARAM_INTERVAL]); - } - }); - } - - return EchoResponse::fromMessage(sprintf( - 'Indexed %s files', - $job->size() - )); - } - - public function name(): string - { - return self::NAME; - } -} diff --git a/lib/Indexer/IndexAgent.php b/lib/Indexer/IndexAgent.php deleted file mode 100644 index 80ad73b430..0000000000 --- a/lib/Indexer/IndexAgent.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ - private array $includePatterns = [ - '/**/*.php', - '/**/*.phar', - ]; - - /** - * @var array - */ - private array $stubPaths = []; - - /** - * @var array - */ - private array $excludePatterns = [ - ]; - - /** - * @var array|null - */ - private ?array $indexers = null; - - private bool $followSymlinks = false; - - /** - * @var list - */ - private array $searchIncludePatterns = []; - - /** - * @var list - */ - private array $supportedExtensions = ['php', 'phar']; - - /** - * Max filesize to index in bytes. (Default 1MB) - */ - private int $maxFileSizeToIndex = 1_000_000; - - private LoggerInterface $logger; - - private ?TextDocumentLocator $documentLocator = null; - - private function __construct( - private string $indexRoot, - private string $projectRoot, - ) { - $this->enhancer = new NullRecordReferenceEnhancer(); - $this->logger = new NullLogger(); - } - - public function setDocumentLocator(TextDocumentLocator $locator): self - { - $this->documentLocator = $locator; - - return $this; - } - - public static function create(string $indexRootPath, string $projectRoot): self - { - return new self($indexRootPath, $projectRoot); - } - - public function setLogger(LoggerInterface $logger): self - { - $this->logger = $logger; - - return $this; - } - - public function addStubPath(string $path): self - { - $this->stubPaths[] = $path; - - return $this; - } - - public function setReferenceEnhancer(RecordReferenceEnhancer $enhancer): self - { - $this->enhancer = $enhancer; - - return $this; - } - - public function buildAgent(): IndexAgent - { - return $this->buildTestAgent(); - } - - public function buildTestAgent(): TestIndexAgent - { - $index = $this->buildIndex(); - $search = $this->buildSearch($index); - $index = new SearchAwareIndex($index, $search); - $query = $this->buildQuery($index); - $builder = $this->buildBuilder($index); - $indexer = $this->buildIndexer($builder, $index); - $search = new HydratingSearchClient($index, $search); - - return new RealIndexAgent($index, $query, $search, $indexer); - } - - /** - * @param array $indexers - */ - public function setIndexers(array $indexers): self - { - $this->indexers = $indexers; - - return $this; - } - - /** - * @param array $excludePatterns - */ - public function setExcludePatterns(array $excludePatterns): self - { - $this->excludePatterns = $excludePatterns; - - return $this; - } - - /** - * @param array $includePatterns - */ - public function setIncludePatterns(array $includePatterns): self - { - $this->includePatterns = $includePatterns; - - return $this; - } - /** - * @param list $searchIncludePatterns - */ - public function setSearchIncludePatterns(array $searchIncludePatterns): self - { - $this->searchIncludePatterns = $searchIncludePatterns; - - return $this; - } - - - /** - * @param list $supportedExtensions - */ - public function setSupportedExtensions(array $supportedExtensions): self - { - $this->supportedExtensions = $supportedExtensions; - - return $this; - } - - public function setFollowSymlinks(bool $followSymlinks): self - { - $this->followSymlinks = $followSymlinks; - - return $this; - } - - /** - * @param array $stubPaths - */ - public function setStubPaths(array $stubPaths): self - { - $this->stubPaths = $stubPaths; - - return $this; - } - - public function setMaxFileSizeToIndex(int $maxFileSizeToIndex): self - { - $this->maxFileSizeToIndex = $maxFileSizeToIndex; - - return $this; - } - - private function buildIndex(): Index - { - $repository = new FileRepository( - $this->indexRoot, - $this->buildRecordSerializer(), - $this->logger - ); - - return new SerializedIndex( - $repository, - $this->documentLocator ?? new FilesystemTextDocumentLocator(), - ); - } - - private function buildQuery(Index $index): QueryClient - { - return new QueryClient( - $index, - $this->enhancer - ); - } - - private function buildSearch(IndexAccess $index): SearchIndex - { - $search = new FileSearchIndex($this->indexRoot . '/search'); - $search = new ValidatingSearchIndex($search, $index, $this->logger); - $search = new FilteredSearchIndex($search, [ - ClassRecord::RECORD_TYPE, - FunctionRecord::RECORD_TYPE, - ConstantRecord::RECORD_TYPE, - ]); - if ($this->searchIncludePatterns !== []) { - $search = new SearchIncludeIndex($search, $this->searchIncludePatterns); - } - - return $search; - } - - private function buildBuilder(Index $index): IndexBuilder - { - if (null !== $this->indexers) { - return new TolerantIndexBuilder($index, $this->indexers, $this->logger); - } - return TolerantIndexBuilder::create($index); - } - - private function buildIndexer(IndexBuilder $builder, Index $index): Indexer - { - return new Indexer( - $builder, - $index, - $this->buildFileListProvider(), - $this->maxFileSizeToIndex, - $this->buildDirtyTracker(), - ); - } - - private function buildFileListProvider(): FileListProvider - { - return new ChainFileListProvider(...$this->buildFileListProviders()); - } - - private function buildFilesystem(string $root): SimpleFilesystem - { - return new SimpleFilesystem( - FilePath::fromString($this->indexRoot), - new SimpleFileListProvider( - FilePath::fromString($root), - $this->followSymlinks - ) - ); - } - - private function buildRecordSerializer(): RecordSerializer - { - return new PhpSerializer(); - } - - /** - * @return array - */ - private function buildFileListProviders(): array - { - $providers = [ - new FilesystemFileListProvider( - $this->buildFilesystem($this->projectRoot), - $this->includePatterns, - $this->excludePatterns, - $this->supportedExtensions, - ) - ]; - - foreach ($this->stubPaths as $stubPath) { - $providers[] = new FilesystemFileListProvider( - $this->buildFilesystem($stubPath) - ); - } - - $providers[] = $this->buildDirtyTracker(); - - return $providers; - } - - private function buildDirtyTracker(): DirtyFileListProvider - { - return new DirtyFileListProvider($this->indexRoot . '/dirty'); - } -} diff --git a/lib/Indexer/Model/DirtyDocumentTracker.php b/lib/Indexer/Model/DirtyDocumentTracker.php deleted file mode 100644 index a8414dd0ce..0000000000 --- a/lib/Indexer/Model/DirtyDocumentTracker.php +++ /dev/null @@ -1,10 +0,0 @@ - - */ -class FileList implements IteratorAggregate, Countable -{ - /** - * Indexed by full path - * - * @var array - */ - private array $splFileInfos; - - /** - * @param iterable $splFileInfos - */ - public function __construct(iterable $splFileInfos) - { - $this->splFileInfos = []; - - foreach ($splFileInfos as $splFileInfo) { - $this->splFileInfos[$splFileInfo->getPathname()] = $splFileInfo; - } - } - - public static function empty(): self - { - return new self([]); - } - - /** - * @param Traversable $splFileInfos - */ - public static function fromInfoIterator(Traversable $splFileInfos): self - { - return new self($splFileInfos); - } - - public static function fromSingleFilePath(string $subPath): self - { - return new self([new SplFileInfo($subPath)]); - } - - public function merge(FileList $fileList): self - { - return new self(array_merge($this->splFileInfos, $fileList->splFileInfos)); - } - - /** - * @return Iterator - */ - public function getIterator(): Iterator - { - return new ArrayIterator($this->splFileInfos); - } - - public function count(): int - { - return count($this->splFileInfos); - } -} diff --git a/lib/Indexer/Model/FileListProvider.php b/lib/Indexer/Model/FileListProvider.php deleted file mode 100644 index f84d94c5bc..0000000000 --- a/lib/Indexer/Model/FileListProvider.php +++ /dev/null @@ -1,8 +0,0 @@ - - */ - private array $providers; - - public function __construct(FileListProvider ...$providers) - { - $this->providers = $providers; - } - - public function provideFileList(Index $index, ?string $subPath = null): FileList - { - $fileList = FileList::empty(); - foreach ($this->providers as $provider) { - $fileList = $fileList->merge($provider->provideFileList($index, $subPath)); - } - - return $fileList; - } -} diff --git a/lib/Indexer/Model/FileListProvider/DirtyFileListProvider.php b/lib/Indexer/Model/FileListProvider/DirtyFileListProvider.php deleted file mode 100644 index 6fcda9c753..0000000000 --- a/lib/Indexer/Model/FileListProvider/DirtyFileListProvider.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ - private array $seen = []; - - public function __construct(private string $dirtyPath) - { - } - - public function markDirty(TextDocumentUri $uri): void - { - if (isset($this->seen[$uri->path()])) { - return; - } - - $handle = @fopen($this->dirtyPath, 'a'); - if (false === $handle) { - throw new RuntimeException(sprintf( - 'Dirty index file path "%s" cannot be created, maybe the directory does not exist?', - $this->dirtyPath - )); - } - fwrite($handle, $uri->path() . "\n"); - fclose($handle); - $this->seen[$uri->path()] = true; - } - - public function provideFileList(Index $index, ?string $subPath = null): FileList - { - return FileList::fromInfoIterator($this->paths()); - } - - /** - * @return Generator - */ - private function paths(): Generator - { - $contents = @file_get_contents($this->dirtyPath); - if (false === $contents) { - return; - } - - $paths = explode("\n", $contents); - foreach ($paths as $path) { - if (!file_exists($path)) { - continue; - } - yield new SplFileInfo($path); - } - - unlink($this->dirtyPath); - } -} diff --git a/lib/Indexer/Model/Index.php b/lib/Indexer/Model/Index.php deleted file mode 100644 index 0420d49329..0000000000 --- a/lib/Indexer/Model/Index.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ - public function optimise(bool $dryRun): iterable; - -} diff --git a/lib/Indexer/Model/Index/SearchAwareIndex.php b/lib/Indexer/Model/Index/SearchAwareIndex.php deleted file mode 100644 index d6fe24443f..0000000000 --- a/lib/Indexer/Model/Index/SearchAwareIndex.php +++ /dev/null @@ -1,66 +0,0 @@ -innerIndex->lastUpdate(); - } - - public function write(Record $record): void - { - $this->innerIndex->write($record); - $this->search->write($record); - } - - public function isFresh(SplFileInfo $fileInfo): bool - { - return $this->innerIndex->isFresh($fileInfo); - } - - public function reset(): void - { - $this->innerIndex->reset(); - } - - public function exists(): bool - { - return $this->innerIndex->exists(); - } - - public function done(): void - { - $this->innerIndex->done(); - $this->search->flush(); - } - - - public function get(Record $record): Record - { - return $this->innerIndex->get($record); - } - - public function has(Record $record): bool - { - return $this->innerIndex->has($record); - } - - public function optimise(bool $dryRun): Generator - { - yield from $this->innerIndex->optimise($dryRun); - } -} diff --git a/lib/Indexer/Model/IndexAccess.php b/lib/Indexer/Model/IndexAccess.php deleted file mode 100644 index 476d29a507..0000000000 --- a/lib/Indexer/Model/IndexAccess.php +++ /dev/null @@ -1,26 +0,0 @@ -getRealPath(), - basename($fileInfo->getPathname()), - null, - $fileInfo->getCTime(), - (function (SplFileInfo $info) { - $path = Path::join($info->getRealPath(), 'timestamp'); - if (!file_exists($path)) { - return $info->getMTime(); - } - return (int)file_get_contents($path); - })($fileInfo) - ); - } - - public function absolutePath(): string - { - return $this->absolutePath; - } - - public function name(): string - { - return $this->directoryName; - } - - public function size(): int - { - if ($this->size) { - return $this->size; - } - - $this->size = Filesystem::sizeOfPath($this->absolutePath()); - return $this->size; - } - - public function ageInDays(): float - { - return (time() - $this->createdAt) / self::SECONDS_IN_DAY; - } - - public function lastModifiedInDays(): float - { - return (time() - $this->updatedAt) / self::SECONDS_IN_DAY; - } -} diff --git a/lib/Indexer/Model/IndexInfos.php b/lib/Indexer/Model/IndexInfos.php deleted file mode 100644 index b07e5bdded..0000000000 --- a/lib/Indexer/Model/IndexInfos.php +++ /dev/null @@ -1,95 +0,0 @@ - - */ -class IndexInfos implements IteratorAggregate, Countable -{ - /** - * @param IndexInfo[] $infos - */ - public function __construct(private array $infos) - { - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->infos); - } - - public function get(string $name): IndexInfo - { - foreach ($this->infos as $info) { - if ($info->name() === $name) { - return $info; - } - } - - throw new RuntimeException(sprintf( - 'Index "%s" not found. Available indicies are: %s', - $name, - implode(', ', $this->names()) - )); - } - - public function count(): int - { - return count($this->infos); - } - - /** - * @return string[] - */ - public function names(): array - { - return array_map(function (IndexInfo $info): string { - return $info->name(); - }, $this->infos); - } - - /** - * @return int[] - */ - public function offsets(): array - { - return range(1, count($this->infos) + 1); - } - - public function getByOffset(int $int): IndexInfo - { - $offset = 1; - foreach ($this->infos as $info) { - if ($offset++ === $int) { - return $info; - } - } - - throw new RuntimeException(sprintf( - 'Index at offset "%s" not found. Available offsets are: %s', - $int, - implode(', ', $this->offsets()) - )); - } - - public function remove(IndexInfo $target): self - { - return new self(array_filter($this->infos, fn (IndexInfo $info) => $info->name() !== $target->name())); - } - - public function totalSize():int - { - return array_reduce( - $this->infos, - fn (int $size, IndexInfo $current) => $size + $current->size(), - 0 - ); - } -} diff --git a/lib/Indexer/Model/IndexJob.php b/lib/Indexer/Model/IndexJob.php deleted file mode 100644 index ed3a0461db..0000000000 --- a/lib/Indexer/Model/IndexJob.php +++ /dev/null @@ -1,56 +0,0 @@ - - */ - public function generator(): Generator - { - foreach ($this->fileList as $fileInfo) { - assert($fileInfo instanceof SplFileInfo); - if ($fileInfo->isLink()) { - continue; - } - - if (($fileInfo->getSize() ?: 0) >= $this->maxFileSizeToIndex) { - continue; - } - - $contents = @file_get_contents($fileInfo->getPathname()); - - if (false === $contents) { - continue; - } - - $this->indexBuilder->index( - TextDocumentBuilder::create($contents)->uri($fileInfo->getPathname())->build() - ); - yield $fileInfo->getPathname(); - } - $this->indexBuilder->done(); - } - - public function run(): void - { - iterator_to_array($this->generator()); - } - - public function size(): int - { - return $this->fileList->count(); - } -} diff --git a/lib/Indexer/Model/IndexLister.php b/lib/Indexer/Model/IndexLister.php deleted file mode 100644 index b39e5f197e..0000000000 --- a/lib/Indexer/Model/IndexLister.php +++ /dev/null @@ -1,13 +0,0 @@ - - */ - public function list(): Generator; -} diff --git a/lib/Indexer/Model/IndexQuery.php b/lib/Indexer/Model/IndexQuery.php deleted file mode 100644 index 77774df5bd..0000000000 --- a/lib/Indexer/Model/IndexQuery.php +++ /dev/null @@ -1,11 +0,0 @@ -builder, - $this->provider->provideFileList($this->index, $subPath), - $this->maxFileSizeToIndex, - ); - } - /** - * @return Generator - */ - public function optimise(bool $dryRun): Generator - { - yield from $this->index->optimise($dryRun); - } - - public function index(TextDocument $textDocument): void - { - $this->builder->index($textDocument); - } - - /** - * Index a file but mark it as dirty so that it will be reloaded from disk on the next indexing run. - */ - public function indexDirty(TextDocument $textDocument): void - { - if (null === $textDocument->uri()) { - return; - } - - $this->dirtyDocumentTracker->markDirty($textDocument->uri()); - $this->builder->index($textDocument); - } - - public function reset(): void - { - $this->index->reset(); - } - - public function flush(): void - { - $this->index->done(); - } -} diff --git a/lib/Indexer/Model/LocationConfidence.php b/lib/Indexer/Model/LocationConfidence.php deleted file mode 100644 index 9faefb5191..0000000000 --- a/lib/Indexer/Model/LocationConfidence.php +++ /dev/null @@ -1,58 +0,0 @@ -confidence; - } - - public static function maybe(Location $location): self - { - return new self($location, self::CONFIDENCE_MAYBE); - } - - public static function not(Location $location): self - { - return new self($location, self::CONFIDENCE_NOT); - } - - public static function surely(Location $location): self - { - return new self($location, self::CONFIDENCE_SURELY); - } - - public function isSurely(): bool - { - return $this->confidence === self::CONFIDENCE_SURELY; - } - - public function isMaybe(): bool - { - return $this->confidence === self::CONFIDENCE_MAYBE; - } - - public function isNot(): bool - { - return $this->confidence === self::CONFIDENCE_NOT; - } - - public function location(): Location - { - return $this->location; - } -} diff --git a/lib/Indexer/Model/MemberCandidates.php b/lib/Indexer/Model/MemberCandidates.php deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Indexer/Model/MemberReference.php b/lib/Indexer/Model/MemberReference.php deleted file mode 100644 index 7f70eaba04..0000000000 --- a/lib/Indexer/Model/MemberReference.php +++ /dev/null @@ -1,46 +0,0 @@ -type; - } - - public function containerType(): ?FullyQualifiedName - { - return $this->name; - } - - public function memberName(): ?string - { - return $this->memberName; - } -} diff --git a/lib/Indexer/Model/MemoryUsage.php b/lib/Indexer/Model/MemoryUsage.php deleted file mode 100644 index 8c30eccbc5..0000000000 --- a/lib/Indexer/Model/MemoryUsage.php +++ /dev/null @@ -1,87 +0,0 @@ -formatMemory($this->memoryUsage), $this->formatMemory($this->memoryLimit)); - } - - public function memoryLimit(): ?int - { - return $this->memoryLimit; - } - - public function memoryUsage(): int - { - return $this->memoryUsage; - } - - private function formatMemory(?int $nbBytes): string - { - if (null === $nbBytes) { - return '∞'; - } - - return number_format($nbBytes / 1000 / 1000, $this->precision); - } - - private static function parseMemoryLimit(string $limit): ?int - { - if ($limit === '-1') { - return null; - } - - if (is_numeric($limit)) { - return (int)$limit; - } - - if (strlen($limit) < 2) { - throw new RuntimeException(sprintf( - 'Invalid memory limit "%s"', - $limit - )); - } - - - $unit = substr($limit, -1, 1); - $amount = (int)substr($limit, 0, -1); - - if ($unit === 'K') { - return $amount * 1000; - } - - if ($unit === 'M') { - return $amount * 1000 * 1000; - } - - if ($unit === 'G') { - return $amount * 1000 * 1000 * 1000; - } - - return null; - } -} diff --git a/lib/Indexer/Model/Name/FullyQualifiedName.php b/lib/Indexer/Model/Name/FullyQualifiedName.php deleted file mode 100644 index fee0b128e8..0000000000 --- a/lib/Indexer/Model/Name/FullyQualifiedName.php +++ /dev/null @@ -1,32 +0,0 @@ -fqn; - } - - public static function fromString(string $fqn): self - { - return new self($fqn); - } - - public function head(): self - { - $id = $this->fqn; - $offset = strrpos($id, '\\'); - - if (false !== $offset) { - $id = substr($id, $offset + 1); - } - - return new self($id); - } -} diff --git a/lib/Indexer/Model/Query/ClassQuery.php b/lib/Indexer/Model/Query/ClassQuery.php deleted file mode 100644 index c834b780be..0000000000 --- a/lib/Indexer/Model/Query/ClassQuery.php +++ /dev/null @@ -1,62 +0,0 @@ -index->has($prototype) ? $this->index->get($prototype) : null; - } - - /** - * @return array - */ - public function implementing(string $name): array - { - $record = $this->index->get(ClassRecord::fromName($name)); - assert($record instanceof ClassRecord); - - return array_map(function (string $fqn) { - return FullyQualifiedName::fromString($fqn); - }, $record->implementations()); - } - - /** - * @return Generator - */ - public function referencesTo(string $identifier): Generator - { - $record = $this->index->get(ClassRecord::fromName($identifier)); - assert($record instanceof ClassRecord); - - foreach ($record->references() as $fileReference) { - $fileRecord = $this->index->get(FileRecord::fromPath($fileReference)); - assert($fileRecord instanceof FileRecord); - - foreach ($fileRecord->references()->to($record) as $classReference) { - yield LocationConfidence::surely( - Location::fromPathAndOffsets( - $fileRecord->filePath() ?? '', - $classReference->start(), - $classReference->end(), - ) - ); - } - } - } -} diff --git a/lib/Indexer/Model/Query/ConstantQuery.php b/lib/Indexer/Model/Query/ConstantQuery.php deleted file mode 100644 index a54552afce..0000000000 --- a/lib/Indexer/Model/Query/ConstantQuery.php +++ /dev/null @@ -1,20 +0,0 @@ -index->has($prototype) ? $this->index->get($prototype) : null; - } -} diff --git a/lib/Indexer/Model/Query/Criteria.php b/lib/Indexer/Model/Query/Criteria.php deleted file mode 100644 index 0952ea22fa..0000000000 --- a/lib/Indexer/Model/Query/Criteria.php +++ /dev/null @@ -1,144 +0,0 @@ - - */ - private array $criterias; - - public function __construct(Criteria ...$criterias) - { - $this->criterias = $criterias; - } - - public function isSatisfiedBy(Record $record): bool - { - foreach ($this->criterias as $criteria) { - if (false === $criteria->isSatisfiedBy($record)) { - return false; - } - } - - return true; - } -} diff --git a/lib/Indexer/Model/Query/Criteria/ExactShortName.php b/lib/Indexer/Model/Query/Criteria/ExactShortName.php deleted file mode 100644 index ad7a574178..0000000000 --- a/lib/Indexer/Model/Query/Criteria/ExactShortName.php +++ /dev/null @@ -1,23 +0,0 @@ -shortName() === $this->name; - } -} diff --git a/lib/Indexer/Model/Query/Criteria/FalseCriteria.php b/lib/Indexer/Model/Query/Criteria/FalseCriteria.php deleted file mode 100644 index 7cd113cc9d..0000000000 --- a/lib/Indexer/Model/Query/Criteria/FalseCriteria.php +++ /dev/null @@ -1,14 +0,0 @@ -filePath(); - if (!$path) { - return false; - } - if ($pos = strpos($path, ':///')) { - $path = substr($path, $pos + 3); - } - - return str_starts_with($path, $this->prefix); - } -} diff --git a/lib/Indexer/Model/Query/Criteria/FqnBeginsWith.php b/lib/Indexer/Model/Query/Criteria/FqnBeginsWith.php deleted file mode 100644 index f01a4a29b8..0000000000 --- a/lib/Indexer/Model/Query/Criteria/FqnBeginsWith.php +++ /dev/null @@ -1,27 +0,0 @@ -name) { - return false; - } - - if (!$record instanceof HasFullyQualifiedName) { - return false; - } - - return str_starts_with($record->fqn()->__toString(), $this->name); - } -} diff --git a/lib/Indexer/Model/Query/Criteria/HasFlags.php b/lib/Indexer/Model/Query/Criteria/HasFlags.php deleted file mode 100644 index 3acdf5ada5..0000000000 --- a/lib/Indexer/Model/Query/Criteria/HasFlags.php +++ /dev/null @@ -1,23 +0,0 @@ -hasFlag($this->flag); - } -} diff --git a/lib/Indexer/Model/Query/Criteria/IsClass.php b/lib/Indexer/Model/Query/Criteria/IsClass.php deleted file mode 100644 index 4a540c44db..0000000000 --- a/lib/Indexer/Model/Query/Criteria/IsClass.php +++ /dev/null @@ -1,15 +0,0 @@ -type() === $this->type; - } -} diff --git a/lib/Indexer/Model/Query/Criteria/IsConstant.php b/lib/Indexer/Model/Query/Criteria/IsConstant.php deleted file mode 100644 index 804443d1f0..0000000000 --- a/lib/Indexer/Model/Query/Criteria/IsConstant.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ - private array $criterias; - - public function __construct(Criteria ...$criterias) - { - $this->criterias = $criterias; - } - - public function isSatisfiedBy(Record $record): bool - { - foreach ($this->criterias as $criteria) { - if (true === $criteria->isSatisfiedBy($record)) { - return true; - } - } - - return false; - } -} diff --git a/lib/Indexer/Model/Query/Criteria/ShortNameBeginsWith.php b/lib/Indexer/Model/Query/Criteria/ShortNameBeginsWith.php deleted file mode 100644 index 86503f0612..0000000000 --- a/lib/Indexer/Model/Query/Criteria/ShortNameBeginsWith.php +++ /dev/null @@ -1,27 +0,0 @@ -name) { - return false; - } - - return str_starts_with($record->shortName(), $this->name); - } -} diff --git a/lib/Indexer/Model/Query/Criteria/ShortNameContains.php b/lib/Indexer/Model/Query/Criteria/ShortNameContains.php deleted file mode 100644 index d981e33bd9..0000000000 --- a/lib/Indexer/Model/Query/Criteria/ShortNameContains.php +++ /dev/null @@ -1,23 +0,0 @@ -shortName(), $this->substr); - } -} diff --git a/lib/Indexer/Model/Query/Criteria/TrueCriteria.php b/lib/Indexer/Model/Query/Criteria/TrueCriteria.php deleted file mode 100644 index 1e84b49de7..0000000000 --- a/lib/Indexer/Model/Query/Criteria/TrueCriteria.php +++ /dev/null @@ -1,14 +0,0 @@ -index->has($prototype) ? $this->index->get($prototype) : null; - } -} diff --git a/lib/Indexer/Model/Query/FunctionQuery.php b/lib/Indexer/Model/Query/FunctionQuery.php deleted file mode 100644 index ab7a41c2e9..0000000000 --- a/lib/Indexer/Model/Query/FunctionQuery.php +++ /dev/null @@ -1,47 +0,0 @@ -index->has($prototype) ? $this->index->get($prototype) : null; - } - - /** - * @return Generator - */ - public function referencesTo(string $identifier): Generator - { - $record = $this->get($identifier); - foreach ($record->references() as $fileReference) { - $fileRecord = $this->index->get(FileRecord::fromPath($fileReference)); - assert($fileRecord instanceof FileRecord); - - foreach ($fileRecord->references()->to($record) as $functionReference) { - yield LocationConfidence::surely( - Location::fromPathAndOffsets( - TextDocumentUri::fromString($fileRecord->filePath()), - $functionReference->start(), - $functionReference->end() - ) - ); - } - } - } -} diff --git a/lib/Indexer/Model/Query/MemberQuery.php b/lib/Indexer/Model/Query/MemberQuery.php deleted file mode 100644 index a1e0184c5f..0000000000 --- a/lib/Indexer/Model/Query/MemberQuery.php +++ /dev/null @@ -1,85 +0,0 @@ -index->has($prototype)) { - return null; - } - - return $this->index->get($prototype); - } - - /** - * @param MemberRecord::TYPE_* $type - * @return Generator - */ - public function referencesTo(string $type, string $memberName, ?string $containerType = null): Generator - { - $record = $this->getByTypeAndName($type, $memberName); - - if (null === $record) { - return; - } - - assert($record instanceof MemberRecord); - - foreach ($record->references() as $fileReference) { - $fileRecord = $this->index->get(FileRecord::fromPath($fileReference)); - assert($fileRecord instanceof FileRecord); - - foreach ($fileRecord->references()->to($record) as $memberReference) { - if ($containerType && null === $memberReference->contaninerType()) { - $memberReference = $this->enhancer->enhance($fileRecord, $memberReference); - } - - $location = Location::fromPathAndOffsets( - $fileRecord->filePath() ?? '', - $memberReference->start(), - $memberReference->end() - ); - - if (null === $memberReference->contaninerType()) { - yield LocationConfidence::maybe($location); - continue; - } - - if ($containerType && $containerType !== $memberReference->contaninerType()) { - yield LocationConfidence::not($location); - continue; - } - - yield LocationConfidence::surely($location); - } - } - } - - private function getByTypeAndName(string $type, string $name): ?MemberRecord - { - return $this->get($type . '#' . $name); - } -} diff --git a/lib/Indexer/Model/Query/MemberReferenceRequest.php b/lib/Indexer/Model/Query/MemberReferenceRequest.php deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Indexer/Model/QueryClient.php b/lib/Indexer/Model/QueryClient.php deleted file mode 100644 index 897ee78a36..0000000000 --- a/lib/Indexer/Model/QueryClient.php +++ /dev/null @@ -1,65 +0,0 @@ -classQuery = new ClassQuery($index); - $this->functionQuery = new FunctionQuery($index); - $this->constantQuery = new ConstantQuery($index); - $this->fileQuery = new FileQuery($index); - $this->memberQuery = new MemberQuery($index, $enhancer); - $this->index = $index; - $this->enhancer = $enhancer; - } - - public function class(): ClassQuery - { - return $this->classQuery; - } - - public function function(): FunctionQuery - { - return $this->functionQuery; - } - - public function file(): FileQuery - { - return $this->fileQuery; - } - - public function member(): MemberQuery - { - return $this->memberQuery; - } - - public function constant(): ConstantQuery - { - return $this->constantQuery; - } -} diff --git a/lib/Indexer/Model/RealIndexAgent.php b/lib/Indexer/Model/RealIndexAgent.php deleted file mode 100644 index adc1ce55ef..0000000000 --- a/lib/Indexer/Model/RealIndexAgent.php +++ /dev/null @@ -1,41 +0,0 @@ -search; - } - - public function query(): QueryClient - { - return $this->query; - } - - public function indexer(): Indexer - { - return $this->indexer; - } - - public function index(): Index - { - return $this->index; - } - - public function access(): IndexAccess - { - return $this->index; - } -} diff --git a/lib/Indexer/Model/Record.php b/lib/Indexer/Model/Record.php deleted file mode 100644 index 6c33f51214..0000000000 --- a/lib/Indexer/Model/Record.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ - private array $implementations = []; - - /** - * @var array - */ - private array $implements = []; - - /** - * Type of "class": class, interface or trait, etc - */ - private ?string $type = null; - - public static function fromName(string $name): self - { - return new self($name); - } - - public function clearImplemented(): void - { - $this->implements = []; - } - - public function addImplementation(FullyQualifiedName $fqn): void - { - $this->implementations[(string)$fqn] = (string)$fqn; - } - - public function addImplements(FullyQualifiedName $fqn): void - { - $this->implements[(string)$fqn] = (string)$fqn; - } - - public function removeClass(FullyQualifiedName $implementedClass): void - { - foreach ($this->implementations as $key => $implementation) { - if ($implementation !== $implementedClass->__toString()) { - continue; - } - - unset($this->implementations[$key]); - } - } - - public function removeImplementation(FullyQualifiedName $name): bool - { - if (!isset($this->implementations[(string)$name])) { - return false; - } - unset($this->implementations[(string)$name]); - return true; - } - - /** - * @return array - */ - public function implementations(): array - { - return $this->implementations; - } - - /** - * @return array - */ - public function implements(): array - { - return $this->implements; - } - - public function type(): ?string - { - return $this->type; - } - - public function setType(string $type): self - { - $this->type = $type; - return $this; - } - - - public function recordType(): string - { - return self::RECORD_TYPE; - } - - public function withType(?string $type): ClassRecord - { - $clone = clone $this; - $clone->type = $type; - return $clone; - } -} diff --git a/lib/Indexer/Model/Record/ConstantRecord.php b/lib/Indexer/Model/Record/ConstantRecord.php deleted file mode 100644 index 8c7ab39c5a..0000000000 --- a/lib/Indexer/Model/Record/ConstantRecord.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ - private array $references = []; - - private function __construct(string $filePath) - { - $this->filePath = $filePath; - } - - public function __wakeup(): void - { - if (null === $this->filePath) { - throw new CorruptedRecord(sprintf( - 'Record was corrupted' - )); - } - } - - - public function recordType(): string - { - return self::RECORD_TYPE; - } - - public static function fromFileInfo(SplFileInfo $info): self - { - return new self($info->getPathname()); - } - - public static function fromPath(string $path): self - { - return new self($path); - } - - public function identifier(): string - { - return $this->filePath(); - } - - public function addReference(RecordReference $reference): self - { - $this->references[] = [ - $reference->type(), - $reference->identifier(), - $reference->start(), - $reference->contaninerType(), - $reference->flags(), - $reference->end(), - ]; - - return $this; - } - - public function references(): RecordReferences - { - return new RecordReferences($this, array_map(function (array $reference) { - return new RecordReference(...$reference); - }, $this->references)); - } - - public function removeReferencesToRecordType(string $type): self - { - $this->references = array_filter($this->references, function (array $reference) use ($type) { - return $reference[0] !== $type; - }); - return $this; - } -} diff --git a/lib/Indexer/Model/Record/FullyQualifiedReferenceTrait.php b/lib/Indexer/Model/Record/FullyQualifiedReferenceTrait.php deleted file mode 100644 index a91aff741c..0000000000 --- a/lib/Indexer/Model/Record/FullyQualifiedReferenceTrait.php +++ /dev/null @@ -1,77 +0,0 @@ -fqn = $fqn; - } - - public function __wakeup(): void - { - /** - * @phpstan-ignore-next-line - */ - if (null === $this->fqn) { - throw new CorruptedRecord(sprintf( - 'Record was corrupted' - )); - } - } - - public function setStart(ByteOffset $start): self - { - $this->start = $start->toInt(); - return $this; - } - - public function setEnd(ByteOffset $end): self - { - $this->end = $end->toInt(); - return $this; - } - - public function fqn(): FullyQualifiedName - { - return FullyQualifiedName::fromString($this->fqn); - } - - public function start(): ByteOffset - { - return ByteOffset::fromInt($this->start); - } - - public function end(): ByteOffset - { - return $this->end ? ByteOffset::fromInt($this->end) : $this->start(); - } - - public function identifier(): string - { - return $this->fqn; - } - - public function shortName(): string - { - $id = $this->fqn; - $offset = strrpos($id, '\\'); - - if (false !== $offset) { - $id = substr($id, $offset + 1); - } - - return $id; - } -} diff --git a/lib/Indexer/Model/Record/FunctionRecord.php b/lib/Indexer/Model/Record/FunctionRecord.php deleted file mode 100644 index dbd3d4e1e2..0000000000 --- a/lib/Indexer/Model/Record/FunctionRecord.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - public function references(): array; -} diff --git a/lib/Indexer/Model/Record/HasFileReferencesTrait.php b/lib/Indexer/Model/Record/HasFileReferencesTrait.php deleted file mode 100644 index 050ea1839b..0000000000 --- a/lib/Indexer/Model/Record/HasFileReferencesTrait.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ - private array $references = []; - - public function addReference(string $path): self - { - $this->references[$path] = true; - - return $this; - } - - public function removeReference(string $path): self - { - if (!isset($this->references[$path])) { - return $this; - } - - unset($this->references[$path]); - - return $this; - } - - /** - * @return array - */ - public function references(): array - { - return array_keys($this->references); - } -} diff --git a/lib/Indexer/Model/Record/HasFlags.php b/lib/Indexer/Model/Record/HasFlags.php deleted file mode 100644 index 16ab731f74..0000000000 --- a/lib/Indexer/Model/Record/HasFlags.php +++ /dev/null @@ -1,11 +0,0 @@ -flags = $flags; - - return $this; - } - - public function addFlag(int $flag): self - { - $this->flags = $this->flags | $flag; - - return $this; - } - - public function hasFlag(int $flag): bool - { - return (bool) ($this->flags & $flag); - } - - public function flags(): int - { - return $this->flags; - } -} diff --git a/lib/Indexer/Model/Record/HasFullyQualifiedName.php b/lib/Indexer/Model/Record/HasFullyQualifiedName.php deleted file mode 100644 index 5317828bab..0000000000 --- a/lib/Indexer/Model/Record/HasFullyQualifiedName.php +++ /dev/null @@ -1,10 +0,0 @@ -filePath = $uri->__toString(); - return $this; - } - - public function filePath(): ?string - { - return $this->filePath; - } -} diff --git a/lib/Indexer/Model/Record/HasShortName.php b/lib/Indexer/Model/Record/HasShortName.php deleted file mode 100644 index 7c4d620baa..0000000000 --- a/lib/Indexer/Model/Record/HasShortName.php +++ /dev/null @@ -1,8 +0,0 @@ -type = $type; - } - - public static function fromMemberReference(MemberReference $memberReference): self - { - return new self($memberReference->type(), $memberReference->memberName(), $memberReference->containerType()); - } - - public function recordType(): string - { - return self::RECORD_TYPE; - } - - public function identifier(): string - { - return $this->type . self::ID_DELIMITER . $this->memberName; - } - - public static function isIdentifier(string $identifier): bool - { - return count(explode(self::ID_DELIMITER, $identifier)) === 2; - } - - public static function fromIdentifier(string $identifier): self - { - if (!self::isIdentifier($identifier)) { - throw new RuntimeException(sprintf( - 'Invalid member identifier "%s", must be # e.g. "property#foobar"', - $identifier - )); - } - - $parts = explode(self::ID_DELIMITER, $identifier); - [$type, $memberName] = $parts; - - /** @phpstan-ignore-next-line */ - return new self($type, $memberName); - } - - public function memberName(): string - { - return $this->memberName; - } - - public function containerType(): ?string - { - return $this->containerType; - } - - /** - * @return MemberRecord::TYPE_* - */ - public function type(): string - { - return $this->type; - } - - public function shortName(): string - { - return $this->memberName; - } -} diff --git a/lib/Indexer/Model/RecordFactory.php b/lib/Indexer/Model/RecordFactory.php deleted file mode 100644 index 00ed566b5b..0000000000 --- a/lib/Indexer/Model/RecordFactory.php +++ /dev/null @@ -1,38 +0,0 @@ -flags = $flags; - } - - public function start(): int - { - return $this->start; - } - - public function end(): int - { - return $this->end ?? $this->start; - } - - public function identifier(): string - { - return $this->identifier; - } - - public function type(): string - { - return $this->type; - } - - public static function fromRecordAndOffsetAndContainerType( - Record $record, - int $start, - int $end, - ?string $containerType - ): self { - return new self( - $record->recordType(), - $record->identifier(), - $start, - $containerType, - 0, - $end, - ); - } - - public function withContainerType(string $type): self - { - return new self( - $this->type, - $this->identifier, - $this->start, - $type, - $this->flags, - $this->end, - ); - } - - public function contaninerType(): ?string - { - return $this->contaninerType; - } -} diff --git a/lib/Indexer/Model/RecordReferenceEnhancer.php b/lib/Indexer/Model/RecordReferenceEnhancer.php deleted file mode 100644 index cc2f6f3443..0000000000 --- a/lib/Indexer/Model/RecordReferenceEnhancer.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class RecordReferences implements IteratorAggregate -{ - /** - * @var array - */ - private array $references = []; - - /** - * @param array $references - */ - public function __construct( - private FileRecord $file, - array $references - ) { - foreach ($references as $reference) { - $this->add($reference); - } - } - - /** - * @return RecordReferences - */ - public function to(Record $record): RecordReferences - { - return new self($this->file, array_filter($this->references, function (RecordReference $reference) use ($record) { - return $reference->type() === $record->recordType() && $reference->identifier() === $record->identifier(); - })); - } - - /** - * @return Iterator - */ - public function getIterator(): Iterator - { - return new ArrayIterator($this->references); - } - - /** - * @return array - */ - public function toArray(): array - { - return $this->references; - } - - public function forContainerType(string $fullyQualifiedName): self - { - return new self($this->file, array_filter($this->references, function (RecordReference $reference) use ($fullyQualifiedName) { - return $fullyQualifiedName === $reference->contaninerType(); - })); - } - - public function file(): FileRecord - { - return $this->file; - } - - private function add(RecordReference $reference): void - { - $this->references[] = $reference; - } -} diff --git a/lib/Indexer/Model/RecordSerializer.php b/lib/Indexer/Model/RecordSerializer.php deleted file mode 100644 index ca6af9cc5a..0000000000 --- a/lib/Indexer/Model/RecordSerializer.php +++ /dev/null @@ -1,10 +0,0 @@ - - */ - public function search(Criteria $criteria): Generator; -} diff --git a/lib/Indexer/Model/SearchClient/HydratingSearchClient.php b/lib/Indexer/Model/SearchClient/HydratingSearchClient.php deleted file mode 100644 index 36d2820522..0000000000 --- a/lib/Indexer/Model/SearchClient/HydratingSearchClient.php +++ /dev/null @@ -1,25 +0,0 @@ -innerClient->search($criteria) as $record) { - yield $this->index->get($record); - } - } -} diff --git a/lib/Indexer/Model/SearchIndex.php b/lib/Indexer/Model/SearchIndex.php deleted file mode 100644 index 566418b7d3..0000000000 --- a/lib/Indexer/Model/SearchIndex.php +++ /dev/null @@ -1,12 +0,0 @@ - $recordTypes - */ - public function __construct( - private SearchIndex $innerIndex, - private array $recordTypes - ) { - } - - - public function search(Criteria $criteria): Generator - { - return $this->innerIndex->search($criteria); - } - - public function write(Record $record): void - { - if (!in_array($record->recordType(), $this->recordTypes)) { - return; - } - - $this->innerIndex->write($record); - } - - public function flush(): void - { - $this->innerIndex->flush(); - } - - public function remove(Record $record): void - { - $this->innerIndex->remove($record); - } -} diff --git a/lib/Indexer/Model/SearchIndex/SearchIncludeIndex.php b/lib/Indexer/Model/SearchIndex/SearchIncludeIndex.php deleted file mode 100644 index 4a54b7dc5c..0000000000 --- a/lib/Indexer/Model/SearchIndex/SearchIncludeIndex.php +++ /dev/null @@ -1,51 +0,0 @@ - $patterns - */ - public function __construct( - private SearchIndex $innerIndex, - private array $patterns - ) { - } - - public function search(Criteria $criteria): Generator - { - foreach ($this->innerIndex->search($criteria) as $record) { - if (!$record instanceof HasFullyQualifiedName) { - continue; - } - foreach ($this->patterns as $pattern) { - if (preg_match('{' . $pattern . '}', $record->fqn())) { - yield $record; - continue 2; - } - } - } - } - - public function write(Record $record): void - { - $this->innerIndex->write($record); - } - - public function remove(Record $record): void - { - $this->innerIndex->remove($record); - } - - public function flush(): void - { - $this->innerIndex->flush(); - } -} diff --git a/lib/Indexer/Model/SearchIndex/ValidatingSearchIndex.php b/lib/Indexer/Model/SearchIndex/ValidatingSearchIndex.php deleted file mode 100644 index 55a8a092e1..0000000000 --- a/lib/Indexer/Model/SearchIndex/ValidatingSearchIndex.php +++ /dev/null @@ -1,76 +0,0 @@ -innerIndex->search($criteria) as $result) { - - if (!$this->index->has($result)) { - $this->innerIndex->remove($result); - - $this->logger->debug(sprintf( - 'Record "%s" does not exist in index, removing from search', - $result->identifier() - )); - - continue; - } - - $record = $this->index->get($result); - - if (!$record instanceof HasPath) { - yield $result; - return; - } - - if (!file_exists($record->filePath() ?? '')) { - $this->innerIndex->remove($record); - - $this->logger->debug(sprintf( - 'Record "%s" references non-existing file, removing from search index', - $record->identifier() - )); - - continue; - } - - yield $result; - } - - $this->innerIndex->flush(); - } - - public function write(Record $record): void - { - $this->innerIndex->write($record); - } - - public function remove(Record $record): void - { - $this->innerIndex->remove($record); - } - - public function flush(): void - { - $this->innerIndex->flush(); - } -} diff --git a/lib/Indexer/Model/TestIndexAgent.php b/lib/Indexer/Model/TestIndexAgent.php deleted file mode 100644 index 65b4c5c73b..0000000000 --- a/lib/Indexer/Model/TestIndexAgent.php +++ /dev/null @@ -1,10 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest((string)file_get_contents(__DIR__ . '/Manifest/buildIndex.php.test')); - } - - #[DataProvider('provideIndexesClassLike')] - #[DataProvider('provideIndexesReferences')] - public function testIndexClass(string $source, string $name, Closure $assertions): void - { - $this->workspace()->loadManifest($source); - $index = $this->buildIndex(); - $class = $this->indexQuery($index)->class()->get($name); - - self::assertNotNull($class, 'Class was found'); - - $assertions($class); - } - - /** - * @return Generator - */ - public function provideIndexesClassLike(): Generator - { - yield 'attribute' => [ - <<workspace()->path('project/attribute.php')), $record->filePath()); - self::assertEquals('IamAnAttribute', $record->fqn()); - self::assertEquals(38, $record->start()->toInt()); - self::assertEquals(52, $record->end()->toInt()); - self::assertTrue($record->hasFlag(ClassRecord::FLAG_ATTRIBUTE)); - } - ]; - - yield 'class' => [ - "// File: project/test.php\nworkspace()->path('project/test.php')), $record->filePath()); - self::assertEquals('ThisClass', $record->fqn()); - self::assertEquals(12, $record->start()->toInt()); - self::assertEquals(21, $record->end()->toInt()); - self::assertEquals(ClassRecord::TYPE_CLASS, $record->type()); - } - ]; - - yield 'namespaced class' => [ - "// File: project/test.php\nfqn()); - } - ]; - - yield 'extended class has implementations' => [ - "// File: project/test.php\nimplementations()); - } - ]; - - yield 'namespaced extended abstract class has implementations' => [ - "// File: project/test.php\nimplementations()); - } - ]; - - yield 'interface referenced by alias from another namespace' => [ - <<<'EOT' - // File: project/test.php - implementations()); - } - ]; - - yield 'class implements' => [ - "// File: project/test.php\nimplements()); - } - ]; - - yield 'interface has class implementation' => [ - "// File: project/test.php\nimplementations()); - } - ]; - - yield 'namespaced interface has class implementation' => [ - "// File: project/test.php\nimplementations()); - } - ]; - - yield 'interface implements' => [ - "// File: project/test.php\nimplementations()); - } - ]; - - - yield 'namespaced interface implements' => [ - "// File: project/test.php\nimplementations()); - } - ]; - - yield 'interface has class implementations' => [ - "// File: project/test.php\nimplementations()); - } - ]; - - yield 'interface' => [ - "// File: project/test.php\nworkspace()->path('project/test.php')), $record->filePath()); - self::assertEquals('ThisInterface', $record->fqn()); - self::assertEquals(16, $record->start()->toInt()); - self::assertEquals(29, $record->end()->toInt()); - self::assertEquals(ClassRecord::TYPE_INTERFACE, $record->type()); - } - ]; - - yield 'namespaced interface' => [ - "// File: project/test.php\nfqn()); - } - ]; - - yield 'trait' => [ - "// File: project/test.php\nworkspace()->path('project/test.php')), $record->filePath()); - self::assertEquals('ThisTrait', $record->fqn()); - self::assertEquals(12, $record->start()->toInt()); - self::assertEquals(21, $record->end()->toInt()); - self::assertEquals(ClassRecord::TYPE_TRAIT, $record->type()); - } - ]; - - yield 'class uses trait' => [ - <<<'EOT' - // File: project/test1.php - implementations()); - } - ]; - - yield 'enum' => [ - "// File: project/test.php\nworkspace()->path('project/test.php')), $record->filePath()); - self::assertEquals('SomeEnum', $record->fqn()); - self::assertEquals(11, $record->start()->toInt()); - self::assertEquals(19, $record->end()->toInt()); - self::assertEquals(ClassRecord::TYPE_ENUM, $record->type()); - } - ]; - } - - /** - * @return Generator - */ - public static function provideIndexesReferences(): Generator - { - yield 'single reference' => [ - <<<'EOT' - // File: project/test1.php - references()); - } - ]; - - yield 'multiple references' => [ - <<<'EOT' - // File: project/test1.php - references()); - } - ]; - - yield 'incoming namespaced references' => [ - <<<'EOT' - // File: project/test1.php - references()); - } - ]; - - yield 'outgoing namespaced references' => [ - <<<'EOT' - // File: project/test1.php - references()); - } - ]; - - yield 'static call reference' => [ - <<<'EOT' - // File: project/test1.php - references()); - } - ]; - } - - #[DataProvider('provideIndexesFunctions')] - public function testIndexFunction(string $source, string $name, Closure $assertions): void - { - $this->workspace()->loadManifest($source); - $index = $this->buildIndex(); - $class = $this->indexQuery($index)->function()->get( - $name - ); - - self::assertNotNull($class, 'Function was found'); - - $assertions($class); - } - - /** - * @return Generator - */ - public function provideIndexesFunctions(): Generator - { - yield 'function' => [ - <<<'EOT' - // File: project/test1.php - references()); - } - ]; - - yield 'namespaced function' => [ - <<<'EOT' - // File: project/test1.php - references()); - self::assertNull($record->filePath()); - } - ]; - - yield 'declaration is indexed' => [ - <<<'EOT' - // File: project/test1.php - references()); - self::assertEquals((string)TextDocumentUri::fromString($this->workspace()->path('project/test1.php')), $record->filePath()); - } - ]; - } - - public function testInterfaceImplementations(): void - { - $index = $this->buildIndex(); - - $references = $this->indexQuery($index)->class()->implementing('Index'); - - self::assertCount(2, $references); - } - - public function testFunctions(): void - { - $index = $this->buildIndex(); - - $function = $this->indexQuery($index)->function()->get( - 'Hello\world' - ); - - self::assertInstanceOf(Record::class, $function); - } - - public function testChildClassImplementations(): void - { - $index = $this->buildIndex(); - - $references = $this->indexQuery($index)->class()->implementing( - 'AbstractClass' - ); - - self::assertCount(2, $references); - } - - public function testPicksUpNewFiles(): void - { - $index = $this->buildIndex(); - - $references = $this->indexQuery($index)->class()->implementing( - 'AbstractClass' - ); - self::assertCount(2, $references); - - $this->workspace()->put( - 'project/Foobar.php', - <<<'EOT' - buildIndex($index); - - $references = $this->indexQuery($index)->class()->implementing( - 'AbstractClass' - ); - - self::assertCount(3, $references); - } - - public function testRemovesExistingImplementationReferences(): void - { - $index = $this->buildIndex(); - - $references = $this->indexQuery($index)->class()->implementing( - 'AbstractClass' - ); - self::assertCount(2, $references); - - - $this->workspace()->put( - 'project/AbstractClassImplementation1.php', - <<<'EOT' - buildIndex($index); - - $references = $this->indexQuery($index)->class()->implementing( - 'AbstractClass' - ); - - self::assertCount(1, $references); - } - - public function testDoesNotRemoveExisting(): void - { - $this->workspace()->put( - 'project/0000.php', - <<<'EOT' - workspace()->put( - 'project/ZZZZ.php', - <<<'EOT' - buildIndex(); - - $references = $this->indexQuery($index)->class()->implementing( - 'AbstractClass' - ); - self::assertCount(4, $references); - - $this->workspace()->put( - 'project/0000.php', - <<<'EOT' - buildIndex($index); - - $references = $this->indexQuery($index)->class()->implementing( - 'AbstractClass' - ); - - self::assertCount(3, $references); - } -} diff --git a/lib/Indexer/Tests/Adapter/IndexTestCase.php b/lib/Indexer/Tests/Adapter/IndexTestCase.php deleted file mode 100644 index ebf2e40c1f..0000000000 --- a/lib/Indexer/Tests/Adapter/IndexTestCase.php +++ /dev/null @@ -1,25 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest((string)file_get_contents(__DIR__ . '/Manifest/buildIndex.php.test')); - } - - public function testBuild(): void - { - $agent = $this->indexAgent(); - $agent->indexer()->getJob()->run(); - $references = $foo = $agent->query()->class()->implementing( - 'Index' - ); - - self::assertCount(2, $references); - } -} diff --git a/lib/Indexer/Tests/Adapter/Manifest/buildIndex.php.test b/lib/Indexer/Tests/Adapter/Manifest/buildIndex.php.test deleted file mode 100644 index d7f927d3a9..0000000000 --- a/lib/Indexer/Tests/Adapter/Manifest/buildIndex.php.test +++ /dev/null @@ -1,48 +0,0 @@ -// File: composer.json -{ -"autoload": { - "psr-4": { - "": "project" - } -} -} -// File: project/Index.php -workspace()->reset(); - } - - public function testIndexPhar(): void - { - if (ini_get('phar.readonly') != 0) { - $this->markTestSkipped('PHAR is in readonly not set'); - } - - $this->workspace()->put('phar/index.php', 'workspace()->mkdir('repo'); - - // create phar - $phar = new Phar($this->workspace()->path('repo/index.phar'), 0, 'index.phar'); - $phar->buildFromDirectory($this->workspace()->path('phar')); - - $agent = $this->indexAgentBuilder('repo')->buildTestAgent(); - $agent->indexer()->getJob()->run(); - $hellos = iterator_to_array($agent->search()->search(Criteria::shortNameContains('Hello'))); - self::assertCount(1, $hellos); - } - - public function testIndexInvalidPhar(): void - { - $this->workspace()->put('repo/index.phar', 'indexAgentBuilder('repo')->buildTestAgent(); - $agent->indexer()->getJob()->run(); - $hellos = iterator_to_array($agent->search()->search(Criteria::shortNameContains('Hello'))); - self::assertCount(0, $hellos); - } -} diff --git a/lib/Indexer/Tests/Adapter/Php/FileSearchIndexTest.php b/lib/Indexer/Tests/Adapter/Php/FileSearchIndexTest.php deleted file mode 100644 index a38194c13e..0000000000 --- a/lib/Indexer/Tests/Adapter/Php/FileSearchIndexTest.php +++ /dev/null @@ -1,56 +0,0 @@ -workspace()->reset(); - $this->index = new FileSearchIndex($this->workspace()->path('search')); - } - - public function testWriteIndex(): void - { - $record = ClassRecord::fromName('Foobar'); - $this->index->write($record); - $this->index->flush(); - - $this->assertCount(1, $this->search('Foobar')); - } - - public function testWriteIndexMultipleTimesDoesNotIncreaseSearchResultNumber(): void - { - $record = ClassRecord::fromName('Foobar'); - $this->index->write($record); - $this->index->write($record); - $this->index->write($record); - $this->index->write($record); - $this->index->flush(); - - $this->assertCount(1, $this->search('Foobar')); - } - - public function testMultipleResultsForPartialMatch(): void - { - $record = ClassRecord::fromName('Foobar'); - $this->index->write($record); - $record = ClassRecord::fromName('Foostar'); - $this->index->write($record); - $this->index->flush(); - - $this->assertCount(2, $this->search('Foo')); - } - - private function search(string $query): array - { - return iterator_to_array($this->index->search(new ShortNameBeginsWith($query))); - } -} diff --git a/lib/Indexer/Tests/Adapter/Php/PhpIndexListerTest.php b/lib/Indexer/Tests/Adapter/Php/PhpIndexListerTest.php deleted file mode 100644 index 0eba464a3c..0000000000 --- a/lib/Indexer/Tests/Adapter/Php/PhpIndexListerTest.php +++ /dev/null @@ -1,31 +0,0 @@ -workspace()->reset(); - } - - public function testListsIndexes(): void - { - $this->workspace()->mkdir('index1'); - $this->workspace()->mkdir('index2'); - $this->workspace()->put('file1.txt', ''); - - $lister = $this->lister(); - - $infos = iterator_to_array($lister->list()); - self::assertCount(2, $infos); - } - - private function lister(): PhpIndexerLister - { - return (new PhpIndexerLister($this->workspace()->path())); - } -} diff --git a/lib/Indexer/Tests/Adapter/Php/SerializedIndexTest.php b/lib/Indexer/Tests/Adapter/Php/SerializedIndexTest.php deleted file mode 100644 index 0fe6d680d0..0000000000 --- a/lib/Indexer/Tests/Adapter/Php/SerializedIndexTest.php +++ /dev/null @@ -1,21 +0,0 @@ -workspace()->path('cache'), - new PhpSerializer(), - ), new FilesystemTextDocumentLocator()); - } -} diff --git a/lib/Indexer/Tests/Adapter/ReferenceFinder/IndexedImplementationFinderTest.php b/lib/Indexer/Tests/Adapter/ReferenceFinder/IndexedImplementationFinderTest.php deleted file mode 100644 index 3a0b71991c..0000000000 --- a/lib/Indexer/Tests/Adapter/ReferenceFinder/IndexedImplementationFinderTest.php +++ /dev/null @@ -1,180 +0,0 @@ -workspace()->reset(); - } - - #[DataProvider('provideClassLikes')] - #[DataProvider('provideClassMembers')] - public function testFinder(string $manifest, int $expectedLocationCount): void - { - $this->workspace()->loadManifest($manifest); - [ $source, $offset ] = ExtractOffset::fromSource($this->workspace()->getContents('project/subject.php')); - $this->workspace()->put('project/subject.php', $source); - - $index = $this->buildIndex(); - - $implementationFinder = new IndexedImplementationFinder( - $this->indexQuery($index), - $this->createReflector() - ); - - $locations = $implementationFinder->findImplementations( - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt((int)$offset) - ); - - self::assertCount($expectedLocationCount, $locations); - } - - /** - * @return Generator - */ - public static function provideClassLikes(): Generator - { - yield 'interface implementations' => [ - <<<'EOT' - // File: project/subject.php - oInterface {} - // File: project/class.php - [ - <<<'EOT' - // File: project/subject.php - o {} - // File: project/class.php - [ - <<<'EOT' - // File: project/subject.php - o {} - // File: project/class.php - [ - <<<'EOT' - // File: project/subject.php - o {} - // File: project/class.php - - */ - public static function provideClassMembers(): Generator - { - yield 'none' => [ - <<<'EOT' - // File: project/subject.php - his(); - } - EOT - , - 0 - ]; - - yield 'interface member' => [ - <<<'EOT' - // File: project/subject.php - his(); - } - // File: project/class.php - [ - <<<'EOT' - // File: project/subject.php - his(); - } - // File: project/class.php - [ - <<<'EOT' - // File: project/subject.php - d<>oThis(); - } - // File: project/class.php - ' ' ' ' ' ' ' ' 'workspace()->put('project/Foobar.php', 'workspace()->put('project/Barfoo.php', 'workspace()->put('project/Barfoo.php', 'indexAgent(); - $agent->indexer()->getJob()->run(); - $searcher = new IndexedNameSearcher($agent->search()); - - $results = iterator_to_array($searcher->search('\Foo')); - - self::assertCount(2, $results, 'Returns both root class name and namespace match'); - } - - public function testSearcher(): void - { - $this->workspace()->put('project/Foobar.php', 'indexAgent(); - $agent->indexer()->getJob()->run(); - $searcher = new IndexedNameSearcher($agent->search()); - - foreach ($searcher->search('Foo') as $result) { - assert($result instanceof NameSearchResult); - self::assertEquals('Foobar', $result->name()->head()->__toString()); - self::assertNotNull($result->uri()); - self::assertStringContainsString('Foobar.php', $result->uri()->__toString()); - } - } - - public function testSearcherForInterface(): void - { - $this->workspace()->put('project/Foobar.php', 'indexAgent(); - $agent->indexer()->getJob()->run(); - $searcher = new IndexedNameSearcher($agent->search()); - - foreach ($searcher->search('Foo', NameSearcherType::INTERFACE) as $result) { - assert($result instanceof NameSearchResult); - self::assertEquals('Foobar', $result->name()->head()->__toString()); - self::assertNotNull($result->uri()); - self::assertStringContainsString('Foobar.php', $result->uri()->__toString()); - return; - } - - $this->fail('Could not find interace'); - } - - public function testSearcherForEnum(): void - { - $this->workspace()->put('project/Foobar.php', 'indexAgent(); - $agent->indexer()->getJob()->run(); - $searcher = new IndexedNameSearcher($agent->search()); - - foreach ($searcher->search('Foo', NameSearcherType::ENUM) as $result) { - assert($result instanceof NameSearchResult); - self::assertEquals('Foobar', $result->name()->head()->__toString()); - self::assertNotNull($result->uri()); - self::assertStringContainsString('Foobar.php', $result->uri()->__toString()); - return; - } - - $this->fail('Could not find enum'); - } - - public function testSearcherForTrait(): void - { - $this->workspace()->put('project/Foobar.php', 'indexAgent(); - $agent->indexer()->getJob()->run(); - $searcher = new IndexedNameSearcher($agent->search()); - - foreach ($searcher->search('Foo', NameSearcherType::TRAIT) as $result) { - assert($result instanceof NameSearchResult); - self::assertEquals('Foobar', $result->name()->head()->__toString()); - self::assertNotNull($result->uri()); - self::assertStringContainsString('Foobar.php', $result->uri()->__toString()); - return; - } - - $this->fail('Could not find trait'); - } - - /** - * @param NameSearcherType::* $type - * @param string[] $expectedResultPaths - */ - #[DataProvider('provideWorkspaceToSearchAttributes')] - public function testSearcherForAttribute(string $query, string $type, array $expectedResultPaths): void - { - foreach (self::ATTR_WORKSPACE as $path => $contents) { - $this->workspace()->put($path, $contents); - } - - $agent = $this->indexAgent(); - $agent->indexer()->getJob()->run(); - $searcher = new IndexedNameSearcher($agent->search()); - - $resultPaths = []; - $offset = 1 + mb_strlen($this->workspace()->path()); - foreach ($searcher->search($query, $type) as $result) { - assert($result instanceof NameSearchResult); - self::assertNotNull($result->uri()); - $resultPaths[] = mb_substr($result->uri()->path(), $offset); - } - - self::assertEqualsCanonicalizing($expectedResultPaths, $resultPaths); - } - - /** - * @return Generator}> - */ - public static function provideWorkspaceToSearchAttributes(): Generator - { - yield 'not targeted attributes' => [ - 'query' => 'Ba', - 'type' => NameSearcherType::ATTRIBUTE, - 'expectedResultPaths' => [ - 'project/Baf.php', - 'project/Bacc.php', - 'project/Bap.php', - 'project/Bar.php', - 'project/Baj.php', - 'project/Bam.php', - 'project/Attribute/Bak.php', - ], - ]; - - yield 'class attributes' => [ - 'query' => 'Ba', - 'type' => NameSearcherType::ATTRIBUTE_TARGET_CLASS, - 'expectedResultPaths' => [ - 'project/Baj.php', - 'project/Bam.php', - ], - ]; - - yield 'property attributes' => [ - 'query' => 'Ba', - 'type' => NameSearcherType::ATTRIBUTE_TARGET_PROPERTY, - 'expectedResultPaths' => [ - 'project/Bar.php', - 'project/Bam.php', - ], - ]; - - yield 'promoted property attributes' => [ - 'query' => 'Ba', - 'type' => NameSearcherType::ATTRIBUTE_TARGET_PROMOTED_PROPERTY, - 'expectedResultPaths' => [ - 'project/Bar.php', - 'project/Bap.php', - 'project/Bam.php', - ], - ]; - - yield 'method attributes' => [ - 'query' => 'Ba', - 'type' => NameSearcherType::ATTRIBUTE_TARGET_METHOD, - 'expectedResultPaths' => [ - 'project/Attribute/Bak.php', - 'project/Bam.php', - ], - ]; - - yield 'parameter attributes' => [ - 'query' => 'Ba', - 'type' => NameSearcherType::ATTRIBUTE_TARGET_PARAMETER, - 'expectedResultPaths' => [ - 'project/Bap.php', - 'project/Bam.php', - ], - ]; - - yield 'class constant attributes' => [ - 'query' => 'Ba', - 'type' => NameSearcherType::ATTRIBUTE_TARGET_CLASS_CONSTANT, - 'expectedResultPaths' => [ - 'project/Bacc.php', - 'project/Bam.php', - ], - ]; - - yield 'function attributes' => [ - 'query' => 'Ba', - 'type' => NameSearcherType::ATTRIBUTE_TARGET_FUNCTION, - 'expectedResultPaths' => [ - 'project/Baf.php', - 'project/Bam.php', - ], - ]; - } -} diff --git a/lib/Indexer/Tests/Adapter/ReferenceFinder/IndexedReferenceFinderTest.php b/lib/Indexer/Tests/Adapter/ReferenceFinder/IndexedReferenceFinderTest.php deleted file mode 100644 index 1609ea1dd4..0000000000 --- a/lib/Indexer/Tests/Adapter/ReferenceFinder/IndexedReferenceFinderTest.php +++ /dev/null @@ -1,360 +0,0 @@ -workspace()->reset(); - } - - #[DataProvider('provideClasses')] - #[DataProvider('provideTraits')] - #[DataProvider('provideFunctions')] - #[DataProvider('provideMembers')] - #[DataProvider('provideUnknown')] - public function testFinder(string $manifest, int $expectedConfirmed, ?int $expectedTotal = 0): void - { - $expectedTotal = $expectedTotal ?: $expectedConfirmed; - $this->workspace()->reset(); - $this->workspace()->loadManifest($manifest); - [ $source, $offset ] = ExtractOffset::fromSource($this->workspace()->getContents('project/subject.php')); - $this->workspace()->put('project/subject.php', $source); - - $this->indexAgent()->indexer()->getJob()->run(); - - $referenceFinder = new IndexedReferenceFinder( - $this->indexAgent()->query(), - $this->createReflector(), - ); - - $locations = $referenceFinder->findReferences( - TextDocumentBuilder::create($source)->build(), - ByteOffset::fromInt((int)$offset) - ); - - $locations = iterator_to_array($locations); - - $sureLocations = array_filter($locations, function (PotentialLocation $location) { - return $location->isSurely(); - }); - - self::assertCount($expectedConfirmed, $sureLocations, 'Total confirmed'); - self::assertCount($expectedTotal, $locations, 'Total expected'); - } - - /** - * @return Generator - */ - public static function provideClasses(): Generator - { - yield 'single class' => [ - <<<'EOT' - // File: project/subject.php - o(); - EOT - , - 1 - ]; - - yield 'class references' => [ - <<<'EOT' - // File: project/subject.php - o {} - // File: project/class1.php - [ - <<<'EOT' - // File: project/subject.php - o {} - // File: project/class1.php - [ - <<<'EOT' - // File: project/subject.php - r {} - // File: project/AnotherInterface.php - - */ - public static function provideTraits(): Generator - { - yield 'single trait' => [ - <<<'EOT' - // File: project/trait.php - r; }; - EOT - , - 1 - ]; - - yield 'implementation' => [ - <<<'EOT' - // File: project/trait.php - r; }; - - // File: project/other.php - - */ - public static function provideFunctions(): Generator - { - yield 'function references' => [ - <<<'EOT' - // File: project/subject.php - llo_world() {} - // File: project/class1.php - - */ - public static function provideMembers(): Generator - { - yield 'show new object expressions when finding references on __construct except for superclass' => [ - <<<'EOT' - // File: project/Bar.php - onstruct() {} } - // File: project/subject.php - [ - <<<'EOT' - // File: project/Bar.php - onstruct() {} } - // File: project/subject.php - [ - <<<'EOT' - // File: project/subject.php - ar() {} - // File: project/class1.php - [ - <<<'EOT' - // File: project/subject.php - ar() {} - // File: project/class1.php - [ - <<<'EOT' - // File: project/subject.php - b<>ar(); - - // File: project/class1.php - [ - <<<'EOT' - // File: project/subject.php - b<>ar(); - - // File: project/subject1.php - bar(); - - // File: project/class1.php - [ - <<<'EOT' - // File: project/subject.php - met<>hod(); - - // File: project/subject1.php - method(); - - // File: project/class1.php - [ - <<<'EOT' - // File: project/foobar.php - aticProp = 5; - - // File: project/class1.php - [ - <<<'EOT' - // File: project/foobar.php - onstruct() {} public static function bar() {} } - - // File: project/class1.php - [ - <<<'EOT' - // File: project/foobar.php - onstruct() {} public static function bar() {} } - - // File: project/class1.php - - */ - public static function provideUnknown(): Generator - { - yield 'variable' => [ - <<<'EOT' - // File: project/subject.php - sd; - EOT - , - 0 - ]; - } -} diff --git a/lib/Indexer/Tests/Adapter/Tolerant/Indexer/ClassLikeDeclarationIndexerTest.php b/lib/Indexer/Tests/Adapter/Tolerant/Indexer/ClassLikeDeclarationIndexerTest.php deleted file mode 100644 index 1062d162f9..0000000000 --- a/lib/Indexer/Tests/Adapter/Tolerant/Indexer/ClassLikeDeclarationIndexerTest.php +++ /dev/null @@ -1,176 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest($manifest); - - $agent = $this->indexAgentBuilder('src') - ->setIndexers([ - new ClassDeclarationIndexer(), - ])->buildAgent(); - - $agent->indexer()->getJob()->run(); - - self::assertCount($expectedCount, $agent->query()->class()->implementing($fqn)); - } - - /** - * @return Generator - */ - public static function provideImplementations(): Generator - { - yield 'no implementations' => [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n $expectedRecords - */ - #[DataProvider('provideSearch')] - public function testSearch(string $manifest, string $search, array $expectedRecords): void - { - $this->workspace()->reset(); - $this->workspace()->loadManifest($manifest); - $agent = $this->runIndexer( - [ - new ClassDeclarationIndexer(), - new EnumDeclarationIndexer(), - new InterfaceDeclarationIndexer(), - new TraitDeclarationIndexer(), - ], - 'src' - ); - $foundRecords = $agent->search()->search(new ShortNameBeginsWith($search)); - - if (empty($expectedRecords)) { - self::assertCount(0, iterator_to_array($foundRecords)); - return; - } - - foreach ($expectedRecords as $record) { - foreach ($foundRecords as $foundRecord) { - assert($foundRecord instanceof ClassRecord); - if ($foundRecord->identifier() === $record->identifier()) { - self::assertEquals($record->filePath(), $foundRecord->filePath()); - continue 2; - } - } - - throw new RuntimeException(sprintf( - 'Record "%s" not found', - $record->identifier() - )); - } - - $this->addToAssertionCount(1); - } - - /** - * @return Generator - */ - public function provideSearch(): Generator - { - yield 'no results' => [ - "// File: src/file1.php\n [ - "// File: src/file1.php\nsetFilePath($this->workspacePath('src/file1.php'))] - ]; - - yield 'namespaced match' => [ - "// File: src/file1.php\nsetFilePath($this->workspacePath('src/file1.php'))] - ]; - - yield 'gh-2098: does not index reserved class name' => [ - file_get_contents(__DIR__ . '/fixture/gh-2098.test'), - 'Query', - [], - ]; - } - - #[DataProvider('provideInvalidClasses')] - public function testInvalidClass(string $manifest, string $exectedMessage): void - { - $this->workspace()->reset(); - $this->workspace()->loadManifest($manifest); - - $logger = $this->prophesize(LoggerInterface::class); - - $logger->warning(Argument::containingString($exectedMessage))->shouldBeCalled(); - - $agent = $this->indexAgentBuilder('src') - ->setIndexers([ - new ClassDeclarationIndexer(), - ])->setLogger($logger->reveal())->buildAgent(); - - $agent->indexer()->getJob()->run(); - $this->addToAssertionCount(1); - } - - /** - * @return Generator - */ - public static function provideInvalidClasses(): Generator - { - yield 'no class name' => [ - "// File: src/file1.php\nworkspace()->path($path)); - } -} diff --git a/lib/Indexer/Tests/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexerTest.php b/lib/Indexer/Tests/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexerTest.php deleted file mode 100644 index 325204882b..0000000000 --- a/lib/Indexer/Tests/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexerTest.php +++ /dev/null @@ -1,101 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest($manifest); - $agent = $this->runIndexer(new ClassLikeReferenceIndexer(), 'src'); - - $counts = [ - LocationConfidence::CONFIDENCE_NOT => 0, - LocationConfidence::CONFIDENCE_MAYBE => 0, - LocationConfidence::CONFIDENCE_SURELY => 0, - ]; - - foreach ($agent->query()->class()->referencesTo($fqn) as $locationConfidence) { - $counts[$locationConfidence->__toString()]++; - } - - self::assertEquals(array_combine(array_keys($counts), $expectedCounts), $counts); - } - - /** - * @return Generator - */ - public static function provideClasses(): Generator - { - yield 'single ref' => [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\nworkspace()->reset(); - $this->workspace()->loadManifest($manifest); - - $agent = $this->indexAgentBuilder('src') - ->setIndexers([ - new ConstantDeclarationIndexer() - ])->buildAgent(); - - $agent->indexer()->getJob()->run(); - - $assertion($agent); - } - - /** - * @return Generator - */ - public static function provideDeclaration(): Generator - { - yield 'no implementations' => [ - "// File: src/file1.php\nquery()->constant()->get('FOOBAR') - ); - } - ]; - yield 'const 1' => [ - "// File: src/file1.php\nquery()->constant()->get('FOOBAR') - ); - - self::assertCount(1, iterator_to_array( - $agent->search()->search( - Criteria::and( - Criteria::isConstant(), - Criteria::fqnBeginsWith('FOOBAR') - ) - ) - )); - } - ]; - yield 'declare 1' => [ - "// File: src/file1.php\nquery()->constant()->get('FOOBAR') - ); - - self::assertCount(1, iterator_to_array( - $agent->search()->search( - Criteria::and( - Criteria::isConstant(), - Criteria::fqnBeginsWith('FOOBAR') - ) - ) - )); - } - ]; - - yield 'a define creates only one constant' => [ - "// File: src/file1.php\nquery()->constant()->get('FOOBAR') - ); - - self::assertCount(1, iterator_to_array( - $agent->search()->search( - Criteria::and( - Criteria::isConstant(), - Criteria::fqnBeginsWith('FOOBAR') - ) - ) - )); - } - ]; - - yield 'namespaced define' => [ - "// File: src/file1.php\nquery()->constant()->get('Barfoo\FOOBAR') - ); - - self::assertCount(1, iterator_to_array( - $agent->search()->search( - Criteria::and( - Criteria::isConstant(), - Criteria::fqnBeginsWith('Barfoo') - ) - ) - )); - } - ]; - } -} diff --git a/lib/Indexer/Tests/Adapter/Tolerant/Indexer/MemberIndexerTest.php b/lib/Indexer/Tests/Adapter/Tolerant/Indexer/MemberIndexerTest.php deleted file mode 100644 index 0157b74859..0000000000 --- a/lib/Indexer/Tests/Adapter/Tolerant/Indexer/MemberIndexerTest.php +++ /dev/null @@ -1,154 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest($manifest); - - $agent = $this->runIndexer(new MemberIndexer(), 'src'); - - $memberRecord = $agent->index()->get(MemberRecord::fromMemberReference($memberReference)); - assert($memberRecord instanceof MemberRecord); - - $counts = [ - LocationConfidence::CONFIDENCE_NOT => 0, - LocationConfidence::CONFIDENCE_MAYBE => 0, - LocationConfidence::CONFIDENCE_SURELY => 0, - ]; - - foreach ($agent->query()->member()->referencesTo( - $memberReference->type(), - $memberReference->memberName(), - $memberReference->containerType() - ) as $locationCondidence) { - $counts[$locationCondidence->__toString()]++; - } - - self::assertEquals(array_combine(array_keys($counts), $expectedCounts), $counts); - } - - /** - * @return Generator - */ - public static function provideStaticAccess(): Generator - { - yield 'single ref' => [ - "// File: src/file1.php\n 1 same name method with different container type and specified search type' => [ - "// File: src/file1.php\n 1 same name method with different container type and no specified search type' => [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n 'value']);\$object = json_decode(\$json);echo \$object->{'some#hash'};", - MemberReference::create(MemberRecord::TYPE_METHOD, 'Foobar', 'bar'), - [ 0, 0, 0 ] - ]; - } - - /** - * @return Generator - */ - public static function provideInstanceAccess(): Generator - { - yield 'method call with wrong container type' => [ - "// File: src/file1.php\nhello();", - MemberReference::create(MemberRecord::TYPE_METHOD, 'Barfoo', 'hello'), - [ 1, 0, 0 ], - ]; - - yield 'method call' => [ - "// File: src/file1.php\nhello();", - MemberReference::create(MemberRecord::TYPE_METHOD, 'Foobar', 'hello'), - [ 0, 1, 0 ], - ]; - - yield 'property access' => [ - "// File: src/file1.php\nhello;", - MemberReference::create(MemberRecord::TYPE_PROPERTY, 'Foobar', 'hello'), - [ 0, 1, 0 ], - ]; - - yield 'resolvable property instance container type' => [ - "// File: src/file1.php\nhello;", - MemberReference::create(MemberRecord::TYPE_PROPERTY, 'Foobar', 'hello'), - [ 0, 0, 1 ], - ]; - } -} diff --git a/lib/Indexer/Tests/Adapter/Tolerant/Indexer/TraitUseClauseIndexerTest.php b/lib/Indexer/Tests/Adapter/Tolerant/Indexer/TraitUseClauseIndexerTest.php deleted file mode 100644 index b10ace995b..0000000000 --- a/lib/Indexer/Tests/Adapter/Tolerant/Indexer/TraitUseClauseIndexerTest.php +++ /dev/null @@ -1,57 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest($manifest); - $agent = $this->runIndexer(new TraitUseClauseIndexer(), 'src'); - self::assertEquals($expectedCount, count($agent->query()->class()->implementing($fqn))); - } - - /** - * @return Generator - */ - public static function provideImplementations(): Generator - { - yield 'use trait (basic)' => [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n [ - "// File: src/file1.php\n|TolerantIndexer $indexer - */ - protected function runIndexer(array|TolerantIndexer $indexer, string $path): TestIndexAgent - { - // run the indexer twice - the results should not be affected - $this->doRunIndexer($indexer, $path); - return $this->doRunIndexer($indexer, $path); - } - - /** - * @param list|TolerantIndexer $indexer - */ - private function doRunIndexer(array|TolerantIndexer $indexer, string $path): TestIndexAgent - { - $indexer = is_array($indexer) ? $indexer : [$indexer]; - $agent = $this->indexAgentBuilder('src') - ->setIndexers((array)$indexer)->buildTestAgent(); - - $agent->indexer()->getJob()->run(); - - return $agent; - } -} diff --git a/lib/Indexer/Tests/Adapter/Worse/WorseRecordReferenceEnhancerTest.php b/lib/Indexer/Tests/Adapter/Worse/WorseRecordReferenceEnhancerTest.php deleted file mode 100644 index e2f92b7091..0000000000 --- a/lib/Indexer/Tests/Adapter/Worse/WorseRecordReferenceEnhancerTest.php +++ /dev/null @@ -1,63 +0,0 @@ -workspace()->reset(); - $this->workspace()->put('test.php', $source); - $reflector = ReflectorBuilder::create()->enableContextualSourceLocation()->build(); - $enhancer = new WorseRecordReferenceEnhancer( - $reflector, - new NullLogger(), - new FilesystemTextDocumentLocator(), - ); - $fileRecord = FileRecord::fromPath($this->workspace()->path('test.php')); - $reference = new RecordReference(MemberRecord::RECORD_TYPE, 'foobar', (int)$offset, end: (int)$offset); - $reference = $enhancer->enhance($fileRecord, $reference); - self::assertEquals($expectedType, $reference->contaninerType()); - } - - /** - * @return Generator - */ - public static function provideEnhance(): Generator - { - yield [ - <<<'EOT' - b<>ar(); - EOT - , - 'Foo\Foobar', - ]; - } -} diff --git a/lib/Indexer/Tests/Benchmark/ClassRecordShortNameBench.php b/lib/Indexer/Tests/Benchmark/ClassRecordShortNameBench.php deleted file mode 100644 index 51d0656562..0000000000 --- a/lib/Indexer/Tests/Benchmark/ClassRecordShortNameBench.php +++ /dev/null @@ -1,28 +0,0 @@ -record = ClassRecord::fromName('Barfoo\\Foobar'); - } - - /** - * @BeforeMethods({"createClassRecord"}) - */ - public function benchShortName(): void - { - $this->record->shortName(); - } -} diff --git a/lib/Indexer/Tests/Benchmark/IndexedReferenceFinderBench.php b/lib/Indexer/Tests/Benchmark/IndexedReferenceFinderBench.php deleted file mode 100644 index 51b7430102..0000000000 --- a/lib/Indexer/Tests/Benchmark/IndexedReferenceFinderBench.php +++ /dev/null @@ -1,45 +0,0 @@ -workspace()->reset(); - $this->workspace()->put('SyliusSpec.php', (string)file_get_contents(__DIR__ . '/fixture/SyliusSpec.test')); - $agent = IndexAgentBuilder::create( - $this->workspace()->path('.index'), - $this->workspace()->path(), - )->buildAgent(); - $agent->indexer()->getJob()->run(); - - $this->document = TextDocumentBuilder::fromUri($this->workspace()->path('SyliusSpec.php'))->build(); - $reflector = ReflectorBuilder::create()->addSource($this->document->__toString())->build(); - $this->finder = new IndexedReferenceFinder($agent->query(), $reflector); - } - - public function benchBareFileSearch(): void - { - foreach ($this->finder->findReferences($this->document, ByteOffset::fromInt(6009)) as $reference) { - } - } - - - protected function workspace(): Workspace - { - return Workspace::create(__DIR__ . '/../Workspace'); - } -} diff --git a/lib/Indexer/Tests/Benchmark/SearchBench.php b/lib/Indexer/Tests/Benchmark/SearchBench.php deleted file mode 100644 index 022b4a15d8..0000000000 --- a/lib/Indexer/Tests/Benchmark/SearchBench.php +++ /dev/null @@ -1,68 +0,0 @@ -search = new FileSearchIndex($indexPath . '/cache/search'); - } - - public function createFullFileSearch(): void - { - $indexPath = __DIR__ . '/../../cache'; - $this->search = IndexAgentBuilder::create( - $indexPath, - __DIR__ .'/../../' - ) - ->buildAgent()->search(); - } - - /** - * @BeforeMethods({"createBareFileSearch"}) - * @ParamProviders({"provideSearches"}) - */ - public function benchBareFileSearch(array $params): void - { - foreach ($this->search->search(new ShortNameBeginsWith($params['search'])) as $result) { - } - } - - /** - * @BeforeMethods({"createFullFileSearch"}) - * F - * @ParamProviders({"provideSearches"}) - */ - public function benchFullFileSearch(array $params): void - { - foreach ($this->search->search(new ShortNameBeginsWith($params['search'])) as $result) { - } - } - - public function provideSearches() - { - yield 'A' => [ - 'search' => 'A', - ]; - - yield 'Request' => [ - 'search' => 'Request', - ]; - } -} diff --git a/lib/Indexer/Tests/Benchmark/fixture/SyliusSpec.test b/lib/Indexer/Tests/Benchmark/fixture/SyliusSpec.test deleted file mode 100644 index 591dcc97ae..0000000000 --- a/lib/Indexer/Tests/Benchmark/fixture/SyliusSpec.test +++ /dev/null @@ -1,2734 +0,0 @@ -beConstructedWith( - $metadata, - $requestConfigurationFactory, - $viewHandler, - $repository, - $factory, - $newResourceFactory, - $manager, - $singleResourceProvider, - $resourcesCollectionProvider, - $resourceFormFactory, - $redirectHandler, - $flashHelper, - $authorizationChecker, - $eventDispatcher, - $stateMachine, - $resourceUpdateHandler, - $resourceDeleteHandler - ); - - $this->setContainer($container); - } - - public function it_throws_a_403_exception_if_user_is_unauthorized_to_view_a_single_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker - ): void { - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::SHOW)->willReturn('sylius.product.show'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.show')->willReturn(false); - - $this - ->shouldThrow(new AccessDeniedException()) - ->during('showAction', [$request]) - ; - } - - public function it_throws_a_404_exception_if_resource_is_not_found_based_on_configuration( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider - ): void { - $metadata->getHumanizedName()->willReturn('product'); - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::SHOW)->willReturn('sylius.product.show'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.show')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn(null); - - $this - ->shouldThrow(new NotFoundHttpException('The "product" has not been found')) - ->during('showAction', [$request]) - ; - } - - public function it_returns_a_response_for_html_view_of_a_single_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - EventDispatcherInterface $eventDispatcher, - ContainerInterface $container, - Environment $twig, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::SHOW)->willReturn('sylius.product.show'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.show')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::SHOW . '.html')->willReturn('@SyliusShop/Product/show.html.twig'); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $expectedContext = [ - 'configuration' => $configuration, - 'metadata' => $metadata, - 'resource' => $resource, - 'product' => $resource, - ]; - - $twig->render('@SyliusShop/Product/show.html.twig', $expectedContext)->willReturn('view'); - - $eventDispatcher->dispatch(ResourceActions::SHOW, $configuration, $resource)->shouldBeCalled(); - - $twig->render('@SyliusShop/Product/show.html.twig', $expectedContext)->shouldBeCalled(); - - $this->showAction($request); - } - - public function it_returns_a_response_for_non_html_view_of_single_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - ViewHandlerInterface $viewHandler, - EventDispatcherInterface $eventDispatcher, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::SHOW)->willReturn('sylius.product.show'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.show')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $configuration->isHtmlRequest()->willReturn(false); - - $eventDispatcher->dispatch(ResourceActions::SHOW, $configuration, $resource)->shouldBeCalled(); - - $expectedView = View::create($resource); - - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->showAction($request)->shouldReturn($response); - } - - public function it_throws_a_403_exception_if_user_is_unauthorized_to_view_an_index_of_resources( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker - ): void { - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::INDEX)->willReturn('sylius.product.index'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.index')->willReturn(false); - - $this - ->shouldThrow(new AccessDeniedException()) - ->during('indexAction', [$request]) - ; - } - - public function it_returns_a_response_for_html_view_of_paginated_resources( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - ResourcesCollectionProviderInterface $resourcesCollectionProvider, - EventDispatcherInterface $eventDispatcher, - ResourceInterface $resource1, - ResourceInterface $resource2, - ContainerInterface $container, - Environment $twig, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - $metadata->getPluralName()->willReturn('products'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::INDEX)->willReturn('sylius.product.index'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.index')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::INDEX . '.html')->willReturn('@SyliusShop/Product/index.html.twig'); - $resourcesCollectionProvider->get($configuration, $repository)->willReturn([$resource1, $resource2]); - - $eventDispatcher->dispatchMultiple(ResourceActions::INDEX, $configuration, [$resource1, $resource2])->shouldBeCalled(); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $expectedContext = [ - 'configuration' => $configuration, - 'metadata' => $metadata, - 'resources' => [$resource1, $resource2], - 'products' => [$resource1, $resource2], - ]; - - $twig->render('@SyliusShop/Product/index.html.twig', $expectedContext)->willReturn('view'); - - $twig->render('@SyliusShop/Product/index.html.twig', $expectedContext)->shouldBeCalled(); - - $this->indexAction($request); - } - - public function it_throws_a_403_exception_if_user_is_unauthorized_to_create_a_new_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker - ): void { - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(false); - - $this - ->shouldThrow(new AccessDeniedException()) - ->during('createAction', [$request]) - ; - } - - public function it_returns_a_html_response_for_creating_new_resource_form( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Form $form, - FormView $formView, - ContainerInterface $container, - Environment $twig, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(false); - $event->getResponse()->willReturn(null); - - $request->isMethod('POST')->willReturn(false); - $form->createView()->willReturn($formView); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $expectedContext = [ - 'configuration' => $configuration, - 'metadata' => $metadata, - 'resource' => $newResource, - 'product' => $newResource, - 'form' => $formView, - ]; - - $twig->render('@SyliusShop/Product/create.html.twig', $expectedContext)->willReturn('view'); - - $form->handleRequest($request)->shouldBeCalled(); - $twig->render('@SyliusShop/Product/create.html.twig', $expectedContext)->shouldBeCalled(); - - $this->createAction($request); - } - - public function it_returns_a_html_response_for_invalid_form_during_resource_creation( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Form $form, - FormView $formView, - ContainerInterface $container, - Environment $twig, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(false); - $event->getResponse()->willReturn(null); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(false); - $form->createView()->willReturn($formView); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $expectedContext = [ - 'configuration' => $configuration, - 'metadata' => $metadata, - 'resource' => $newResource, - 'product' => $newResource, - 'form' => $formView, - ]; - - $twig->render('@SyliusShop/Product/create.html.twig', $expectedContext)->willReturn('view'); - - $twig->render('@SyliusShop/Product/create.html.twig', $expectedContext)->shouldBeCalled(); - - $this->createAction($request); - } - - public function it_returns_a_html_response_for_not_submitted_form_during_resource_creation( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Form $form, - FormView $formView, - ContainerInterface $container, - Environment $twig, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(false); - $event->getResponse()->willReturn(null); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(false); - $form->createView()->willReturn($formView); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $expectedContext = [ - 'configuration' => $configuration, - 'metadata' => $metadata, - 'resource' => $newResource, - 'product' => $newResource, - 'form' => $formView, - ]; - - $twig->render('@SyliusShop/Product/create.html.twig', $expectedContext)->willReturn('view'); - - $twig->render('@SyliusShop/Product/create.html.twig', $expectedContext)->shouldBeCalled(); - - $this->createAction($request); - } - - public function it_returns_a_non_html_response_for_invalid_form_during_resource_creation( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - Form $form, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(false); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(false); - - $expectedView = View::create($form, 400); - - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->createAction($request)->shouldReturn($response); - } - - public function it_returns_a_non_html_response_for_not_submitted_form_during_resource_creation( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - Form $form, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(false); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(false); - - $expectedView = View::create($form, 400); - - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->createAction($request)->shouldReturn($response); - } - - public function it_does_not_create_the_resource_and_redirects_to_index_for_html_requests_stopped_via_events( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - RepositoryInterface $repository, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - Form $form, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($newResource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(true); - - $flashHelper->addFlashFromEvent($configuration, $event)->shouldBeCalled(); - - $event->getResponse()->willReturn(null); - - $repository->add($newResource)->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource)->shouldNotBeCalled(); - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - - $redirectHandler->redirectToIndex($configuration, $newResource)->willReturn($redirectResponse); - - $this->createAction($request)->shouldReturn($redirectResponse); - } - - public function it_does_not_create_the_resource_and_return_response_for_html_requests_stopped_via_events( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - RepositoryInterface $repository, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - Form $form, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($newResource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(true); - - $flashHelper->addFlashFromEvent($configuration, $event)->shouldBeCalled(); - - $event->hasResponse()->willReturn(true); - $event->getResponse()->willReturn($response); - - $repository->add($newResource)->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource)->shouldNotBeCalled(); - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - - $this->createAction($request)->shouldReturn($response); - } - - public function it_redirects_to_newly_created_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - RepositoryInterface $repository, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - StateMachineInterface $stateMachine, - Form $form, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - ResourceControllerEvent $postEvent, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - $configuration->hasStateMachine()->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($newResource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $stateMachine->apply($configuration, $newResource)->shouldBeCalled(); - - $repository->add($newResource)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($postEvent); - - $postEvent->getResponse()->willReturn(null); - - $flashHelper->addSuccessFlash($configuration, ResourceActions::CREATE, $newResource)->shouldBeCalled(); - $redirectHandler->redirectToResource($configuration, $newResource)->willReturn($redirectResponse); - - $this->createAction($request)->shouldReturn($redirectResponse); - } - - public function it_uses_response_from_post_create_event_if_defined( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - RepositoryInterface $repository, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - StateMachineInterface $stateMachine, - Form $form, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - ResourceControllerEvent $postEvent, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - $configuration->hasStateMachine()->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($newResource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $stateMachine->apply($configuration, $newResource)->shouldBeCalled(); - - $repository->add($newResource)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($postEvent); - $flashHelper->addSuccessFlash($configuration, ResourceActions::CREATE, $newResource)->shouldBeCalled(); - - $postEvent->hasResponse()->willReturn(true); - $postEvent->getResponse()->willReturn($redirectResponse); - - $this->createAction($request)->shouldReturn($redirectResponse); - } - - public function it_returns_a_non_html_response_for_correctly_created_resources( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - RepositoryInterface $repository, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - StateMachineInterface $stateMachine, - Form $form, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - $configuration->hasStateMachine()->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(false); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($newResource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $stateMachine->apply($configuration, $newResource)->shouldBeCalled(); - - $repository->add($newResource)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource)->shouldBeCalled(); - - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - - $expectedView = View::create($newResource, 201); - - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->createAction($request)->shouldReturn($response); - } - - public function it_does_not_create_the_resource_and_throws_http_exception_for_non_html_requests_stopped_via_event( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - RepositoryInterface $repository, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - Form $form, - Request $request, - ResourceControllerEvent $event - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(false); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(true); - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($newResource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($event); - $event->isStopped()->willReturn(true); - $event->getMessage()->willReturn('You cannot add a new product right now.'); - $event->getErrorCode()->willReturn(500); - - $repository->add($newResource)->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource)->shouldNotBeCalled(); - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - - $this - ->shouldThrow(new HttpException(500, 'You cannot add a new product right now.')) - ->during('createAction', [$request]) - ; - } - - public function it_throws_a_403_exception_if_user_is_unauthorized_to_edit_a_single_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker - ): void { - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(false); - - $this - ->shouldThrow(new AccessDeniedException()) - ->during('updateAction', [$request]) - ; - } - - public function it_throws_a_404_exception_if_resource_to_update_is_not_found_based_on_configuration( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider - ): void { - $metadata->getHumanizedName()->willReturn('product'); - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn(null); - - $this - ->shouldThrow(new NotFoundHttpException('The "product" has not been found')) - ->during('updateAction', [$request]) - ; - } - - public function it_returns_a_html_response_for_updating_resource_form( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - ResourceFormFactoryInterface $resourceFormFactory, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Form $form, - FormView $formView, - ContainerInterface $container, - Environment $twig, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->hasStateMachine()->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - $event->getResponse()->willReturn(null); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('GET'); - - $form->handleRequest($request)->willReturn($form); - $form->createView()->willReturn($formView); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $expectedContext = [ - 'configuration' => $configuration, - 'metadata' => $metadata, - 'resource' => $resource, - 'product' => $resource, - 'form' => $formView, - ]; - - $twig->render('@SyliusShop/Product/update.html.twig', $expectedContext)->willReturn('view'); - - $twig->render('@SyliusShop/Product/update.html.twig', $expectedContext)->shouldBeCalled(); - - $this->updateAction($request); - } - - public function it_returns_a_html_response_for_invalid_form_during_resource_update( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - ResourceFormFactoryInterface $resourceFormFactory, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Form $form, - FormView $formView, - ContainerInterface $container, - Environment $twig, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - $event->getResponse()->willReturn(null); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('PUT'); - - $form->handleRequest($request)->willReturn($form); - - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(false); - $form->createView()->willReturn($formView); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $expectedContext = [ - 'configuration' => $configuration, - 'metadata' => $metadata, - 'resource' => $resource, - 'product' => $resource, - 'form' => $formView, - ]; - - $twig->render('@SyliusShop/Product/update.html.twig', $expectedContext)->willReturn('view'); - - $twig->render('@SyliusShop/Product/update.html.twig', $expectedContext)->shouldBeCalled(); - - $this->updateAction($request); - } - - public function it_returns_a_html_response_for_not_submitted_form_during_resource_update( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - ResourceFormFactoryInterface $resourceFormFactory, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Form $form, - FormView $formView, - ContainerInterface $container, - Environment $twig, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - $event->getResponse()->willReturn(null); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('PUT'); - - $form->handleRequest($request)->willReturn($form); - - $form->isSubmitted()->willReturn(false); - $form->createView()->willReturn($formView); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $expectedContext = [ - 'configuration' => $configuration, - 'metadata' => $metadata, - 'resource' => $resource, - 'product' => $resource, - 'form' => $formView, - ]; - - $twig->render('@SyliusShop/Product/update.html.twig', $expectedContext)->willReturn('view'); - - $twig->render('@SyliusShop/Product/update.html.twig', $expectedContext)->shouldBeCalled(); - - $this->updateAction($request); - } - - public function it_returns_a_non_html_response_for_invalid_form_during_resource_update( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - ResourceFormFactoryInterface $resourceFormFactory, - Form $form, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isHtmlRequest()->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->isMethod('PATCH')->willReturn(true); - $request->getMethod()->willReturn('PATCH'); - - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(false); - - $expectedView = View::create($form, 400); - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->updateAction($request)->shouldReturn($response); - } - - public function it_returns_a_non_html_response_for_not_submitted_form_during_resource_update( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - ResourceFormFactoryInterface $resourceFormFactory, - Form $form, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isHtmlRequest()->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->isMethod('PATCH')->willReturn(true); - $request->getMethod()->willReturn('PATCH'); - - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(false); - - $expectedView = View::create($form, 400); - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->updateAction($request)->shouldReturn($response); - } - - public function it_does_not_update_the_resource_and_redirects_to_resource_for_html_request_if_stopped_via_event( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ObjectManager $manager, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - ResourceFormFactoryInterface $resourceFormFactory, - Form $form, - EventDispatcherInterface $eventDispatcher, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - ResourceControllerEvent $event, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('PUT'); - - $form->handleRequest($request)->willReturn($form); - - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($resource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(true); - $event->getResponse()->willReturn(null); - $flashHelper->addFlashFromEvent($configuration, $event)->shouldBeCalled(); - - $manager->flush()->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(Argument::any())->shouldNotBeCalled(); - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - - $redirectHandler->redirectToResource($configuration, $resource)->willReturn($redirectResponse); - - $this->updateAction($request)->shouldReturn($redirectResponse); - } - - public function it_redirects_to_updated_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - ResourceFormFactoryInterface $resourceFormFactory, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - Form $form, - ResourceControllerEvent $preEvent, - ResourceControllerEvent $postEvent, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->hasStateMachine()->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('PUT'); - - $form->handleRequest($request)->willReturn($form); - - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($resource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($preEvent); - $preEvent->isStopped()->willReturn(false); - - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($postEvent); - - $postEvent->getResponse()->willReturn(null); - - $flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource)->shouldBeCalled(); - $redirectHandler->redirectToResource($configuration, $resource)->willReturn($redirectResponse); - - $this->updateAction($request)->shouldReturn($redirectResponse); - } - - public function it_uses_response_from_post_update_event_if_defined( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - ResourceFormFactoryInterface $resourceFormFactory, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - Form $form, - ResourceControllerEvent $preEvent, - ResourceControllerEvent $postEvent, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->hasStateMachine()->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('PUT'); - - $form->handleRequest($request)->willReturn($form); - - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($resource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($preEvent); - $preEvent->isStopped()->willReturn(false); - - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldBeCalled(); - $flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($postEvent); - - $postEvent->hasResponse()->willReturn(true); - $postEvent->getResponse()->willReturn($redirectResponse); - - $redirectHandler->redirectToResource($configuration, $resource)->shouldNotBeCalled(); - - $this->updateAction($request)->shouldReturn($redirectResponse); - } - - public function it_uses_response_from_initialize_create_event_if_defined( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - RedirectHandlerInterface $redirectHandler, - FactoryInterface $factory, - NewResourceFactoryInterface $newResourceFactory, - ResourceInterface $newResource, - ResourceFormFactoryInterface $resourceFormFactory, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $initializeEvent, - Form $form, - ContainerInterface $container, - Environment $twig, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::CREATE)->willReturn('sylius.product.create'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.create')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); - - $newResourceFactory->create($configuration, $factory)->willReturn($newResource); - $resourceFormFactory->create($configuration, $newResource)->willReturn($form); - - $request->isMethod('POST')->willReturn(false); - $form->createView()->shouldNotBeCalled(); - - $eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $configuration, $newResource)->willReturn($initializeEvent); - $initializeEvent->hasResponse()->willReturn(true); - $initializeEvent->getResponse()->willReturn($response); - - $eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource)->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource)->shouldNotBeCalled(); - $redirectHandler->redirectToResource($configuration, $newResource)->shouldNotBeCalled(); - - $container->has('templating')->willReturn(false); - $container->has('twig')->willReturn(true); - $container->get('twig')->willReturn($twig); - - $twig->render(Argument::cetera())->willReturn('view'); - - $twig->render(Argument::cetera())->shouldNotBeCalled(); - $form->handleRequest($request)->shouldBeCalled(); - - $this->createAction($request); - } - - public function it_uses_response_from_initialize_update_event_if_defined( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - ResourceFormFactoryInterface $resourceFormFactory, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - Form $form, - ResourceControllerEvent $initializeEvent, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->hasStateMachine()->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->getMethod()->willReturn('GET'); - - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(false); - $form->isValid()->willReturn(false); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->shouldNotBeCalled(); - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldNotBeCalled(); - $flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource)->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->shouldNotBeCalled(); - $redirectHandler->redirectToResource($configuration, $resource)->shouldNotBeCalled(); - - $eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($initializeEvent); - $initializeEvent->hasResponse()->willReturn(true); - $initializeEvent->getResponse()->willReturn($response); - - $this->updateAction($request)->shouldReturn($response); - } - - public function it_returns_a_non_html_response_for_correctly_updated_resource( - MetadataInterface $metadata, - ParameterBagInterface $parameterBag, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - ViewHandlerInterface $viewHandler, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - ResourceFormFactoryInterface $resourceFormFactory, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - ResourceControllerEvent $event, - Form $form, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isHtmlRequest()->willReturn(false); - $configuration->hasStateMachine()->willReturn(false); - - $configuration->getParameters()->willReturn($parameterBag); - $parameterBag->get('return_content', false)->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('PUT'); - - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($resource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->shouldBeCalled(); - - $expectedView = View::create(null, 204); - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->updateAction($request)->shouldReturn($response); - } - - public function it_does_not_update_the_resource_throws_a_http_exception_for_non_html_requests_stopped_via_event( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ObjectManager $manager, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - ResourceFormFactoryInterface $resourceFormFactory, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Form $form, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isHtmlRequest()->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('PUT'); - - $form->handleRequest($request)->willReturn($form); - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($resource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(true); - $event->getMessage()->willReturn('Cannot update this channel.'); - $event->getErrorCode()->willReturn(500); - - $manager->flush()->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(Argument::any())->shouldNotBeCalled(); - - $this - ->shouldThrow(new HttpException(500, 'Cannot update this channel.')) - ->during('updateAction', [$request]) - ; - } - - public function it_applies_state_machine_transition_to_updated_resource_if_configured( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - ResourceFormFactoryInterface $resourceFormFactory, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - Form $form, - ResourceControllerEvent $preEvent, - ResourceControllerEvent $postEvent, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->hasStateMachine()->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->getTemplate(ResourceActions::UPDATE)->willReturn('@SyliusShop/Product/update.html.twig'); - - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resourceFormFactory->create($configuration, $resource)->willReturn($form); - - $request->isMethod('PATCH')->willReturn(false); - $request->getMethod()->willReturn('PUT'); - - $form->handleRequest($request)->willReturn($form); - - $form->isSubmitted()->willReturn(true); - $form->isValid()->willReturn(true); - $form->getData()->willReturn($resource); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($preEvent); - $preEvent->isStopped()->willReturn(false); - - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($postEvent); - - $postEvent->getResponse()->willReturn(null); - - $flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource)->shouldBeCalled(); - $redirectHandler->redirectToResource($configuration, $resource)->willReturn($redirectResponse); - - $this->updateAction($request)->shouldReturn($redirectResponse); - } - - public function it_throws_a_403_exception_if_user_is_unauthorized_to_delete_multiple_resources( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker - ): void { - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::BULK_DELETE)->willReturn('sylius.product.bulk_delete'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.bulk_delete')->willReturn(false); - - $this - ->shouldThrow(new AccessDeniedException()) - ->during('bulkDeleteAction', [$request]) - ; - } - - public function it_deletes_multiple_resources_and_redirects_to_index_for_html_request( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - ResourcesCollectionProviderInterface $resourcesCollectionProvider, - ResourceInterface $firstResource, - ResourceInterface $secondResource, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $firstPreEvent, - ResourceControllerEvent $secondPreEvent, - ResourceControllerEvent $firstPostEvent, - ResourceControllerEvent $secondPostEvent, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::BULK_DELETE)->willReturn('sylius.product.bulk_delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('bulk_delete', 'xyz'))->willReturn(true); - - $eventDispatcher - ->dispatchMultiple(ResourceActions::BULK_DELETE, $configuration, [$firstResource, $secondResource]) - ->shouldBeCalled() - ; - - $authorizationChecker->isGranted($configuration, 'sylius.product.bulk_delete')->willReturn(true); - $resourcesCollectionProvider->get($configuration, $repository)->willReturn([$firstResource, $secondResource]); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher - ->dispatchPreEvent(ResourceActions::DELETE, $configuration, $firstResource) - ->willReturn($firstPreEvent) - ; - $firstPreEvent->isStopped()->willReturn(false); - - $resourceDeleteHandler->handle($firstResource, $repository)->shouldBeCalled(); - - $eventDispatcher - ->dispatchPostEvent(ResourceActions::DELETE, $configuration, $firstResource) - ->willReturn($firstPostEvent) - ; - $firstPostEvent->getResponse()->willReturn(null); - - $eventDispatcher - ->dispatchPreEvent(ResourceActions::DELETE, $configuration, $secondResource) - ->willReturn($secondPreEvent) - ; - $secondPreEvent->isStopped()->willReturn(false); - - $resourceDeleteHandler->handle($secondResource, $repository)->shouldBeCalled(); - - $eventDispatcher - ->dispatchPostEvent(ResourceActions::DELETE, $configuration, $secondResource) - ->willReturn($secondPostEvent) - ; - $secondPostEvent->getResponse()->willReturn(null); - - $flashHelper->addSuccessFlash($configuration, ResourceActions::BULK_DELETE)->shouldBeCalled(); - - $redirectHandler->redirectToIndex($configuration)->willReturn($redirectResponse); - - $this->bulkDeleteAction($request)->shouldReturn($redirectResponse); - } - - public function it_throws_a_403_exception_if_user_is_unauthorized_to_delete_a_single_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker - ): void { - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(false); - - $this - ->shouldThrow(new AccessDeniedException()) - ->during('deleteAction', [$request]) - ; - } - - public function it_throws_a_404_exception_if_resource_for_deletion_is_not_found_based_on_configuration( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider - ): void { - $metadata->getHumanizedName()->willReturn('product'); - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn(null); - - $this - ->shouldThrow(new NotFoundHttpException('The "product" has not been found')) - ->during('deleteAction', [$request]) - ; - } - - public function it_deletes_a_resource_and_redirects_to_index_by_for_html_request( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - ResourceControllerEvent $postEvent, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resource->getId()->willReturn(1); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $resourceDeleteHandler->handle($resource, $repository)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($postEvent); - - $postEvent->getResponse()->willReturn(null); - - $flashHelper->addSuccessFlash($configuration, ResourceActions::DELETE, $resource)->shouldBeCalled(); - $redirectHandler->redirectToIndex($configuration, $resource)->willReturn($redirectResponse); - - $this->deleteAction($request)->shouldReturn($redirectResponse); - } - - public function it_uses_response_from_post_delete_event_if_defined( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - ResourceControllerEvent $postEvent, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resource->getId()->willReturn(1); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $resourceDeleteHandler->handle($resource, $repository)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($postEvent); - - $flashHelper->addSuccessFlash($configuration, ResourceActions::DELETE, $resource)->shouldBeCalled(); - - $postEvent->hasResponse()->willReturn(true); - $postEvent->getResponse()->willReturn($redirectResponse); - - $this->deleteAction($request)->shouldReturn($redirectResponse); - } - - public function it_does_not_delete_a_resource_and_redirects_to_index_for_html_requests_stopped_via_event( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resource->getId()->willReturn(1); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(true); - $event->getResponse()->willReturn(null); - - $resourceDeleteHandler->handle($resource, $repository)->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource)->shouldNotBeCalled(); - $flashHelper->addSuccessFlash($configuration, ResourceActions::DELETE, $resource)->shouldNotBeCalled(); - - $flashHelper->addFlashFromEvent($configuration, $event)->shouldBeCalled(); - $redirectHandler->redirectToIndex($configuration, $resource)->willReturn($redirectResponse); - - $this->deleteAction($request)->shouldReturn($redirectResponse); - } - - public function it_does_not_delete_a_resource_and_uses_response_from_event_if_defined( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resource->getId()->willReturn(1); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(true); - - $flashHelper->addFlashFromEvent($configuration, $event)->shouldBeCalled(); - - $event->hasResponse()->willReturn(true); - $event->getResponse()->willReturn($redirectResponse); - - $resourceDeleteHandler->handle($resource, $repository)->shouldNotBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource)->shouldNotBeCalled(); - $flashHelper->addSuccessFlash($configuration, ResourceActions::DELETE, $resource)->shouldNotBeCalled(); - - $redirectHandler->redirectToIndex($configuration, $resource)->shouldNotBeCalled(); - - $this->deleteAction($request)->shouldReturn($redirectResponse); - } - - public function it_does_not_correctly_delete_a_resource_and_returns_500_for_not_html_response( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resource->getId()->willReturn(1); - - $configuration->isHtmlRequest()->willReturn(false); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $resourceDeleteHandler->handle($resource, $repository)->willThrow(new DeleteHandlingException()); - - $eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource)->shouldNotBeCalled(); - - $expectedView = View::create(null, 500); - - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->deleteAction($request)->shouldReturn($response); - } - - public function it_deletes_a_resource_and_returns_204_for_non_html_requests( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - ViewHandlerInterface $viewHandler, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resource->getId()->willReturn(1); - - $configuration->isHtmlRequest()->willReturn(false); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $resourceDeleteHandler->handle($resource, $repository)->shouldBeCalled(); - $eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource)->shouldBeCalled(); - - $expectedView = View::create(null, 204); - - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->deleteAction($request)->shouldReturn($response); - } - - public function it_does_not_delete_a_resource_and_throws_http_exception_for_non_html_requests_stopped_via_event( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resource->getId()->willReturn(1); - - $configuration->isHtmlRequest()->willReturn(false); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(true); - $event->getMessage()->willReturn('Cannot delete this product.'); - $event->getErrorCode()->willReturn(500); - - $resourceDeleteHandler->handle($resource, $repository)->shouldNotBeCalled(); - - $eventDispatcher->dispatchPostEvent(Argument::any())->shouldNotBeCalled(); - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - $flashHelper->addFlashFromEvent(Argument::any())->shouldNotBeCalled(); - - $this - ->shouldThrow(new HttpException(500, 'Cannot delete this product.')) - ->during('deleteAction', [$request]) - ; - } - - public function it_throws_a_403_exception_if_csrf_token_is_invalid_during_delete_action( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - ResourceDeleteHandlerInterface $resourceDeleteHandler, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::DELETE)->willReturn('sylius.product.delete'); - $request->request = new ParameterBag(['_csrf_token' => 'xyz']); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.delete')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - $resource->getId()->willReturn(1); - - $configuration->isHtmlRequest()->willReturn(true); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource)->willReturn($event); - $event->isStopped()->shouldNotBeCalled(); - - $resourceDeleteHandler->handle($resource, $repository)->shouldNotBeCalled(); - - $eventDispatcher->dispatchPostEvent(Argument::any())->shouldNotBeCalled(); - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - $flashHelper->addFlashFromEvent(Argument::any())->shouldNotBeCalled(); - - $this - ->shouldThrow(new HttpException(403, 'Invalid csrf token.')) - ->during('deleteAction', [$request]) - ; - } - - public function it_throws_a_403_exception_if_user_is_unauthorized_to_apply_state_machine_transition_on_resource( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker - ): void { - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(false); - - $this - ->shouldThrow(new AccessDeniedException()) - ->during('applyStateMachineTransitionAction', [$request]) - ; - } - - public function it_throws_a_404_exception_if_resource_is_not_found_when_trying_to_apply_state_machine_transition( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - Request $request, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider - ): void { - $metadata->getHumanizedName()->willReturn('product'); - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn(null); - - $this - ->shouldThrow(new NotFoundHttpException('The "product" has not been found')) - ->during('applyStateMachineTransitionAction', [$request]) - ; - } - - public function it_does_not_apply_state_machine_transition_on_resource_if_not_applicable_and_returns_400_bad_request( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - ObjectManager $objectManager, - StateMachineInterface $stateMachine, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - $request->get('_csrf_token')->willReturn('xyz'); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $resource->getId()->willReturn('1'); - - $configuration->isHtmlRequest()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $stateMachine->can($configuration, $resource)->willReturn(false); - - $stateMachine->apply($configuration, $resource)->shouldNotBeCalled(); - $objectManager->flush()->shouldNotBeCalled(); - - $eventDispatcher->dispatchPostEvent(Argument::any())->shouldNotBeCalled(); - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - $flashHelper->addFlashFromEvent(Argument::any())->shouldNotBeCalled(); - - $this - ->shouldThrow(new BadRequestHttpException()) - ->during('applyStateMachineTransitionAction', [$request]) - ; - } - - public function it_applies_state_machine_transition_to_resource_and_redirects_for_html_request( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - StateMachineInterface $stateMachine, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - ResourceControllerEvent $event, - ResourceControllerEvent $postEvent, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - $request->get('_csrf_token')->willReturn('xyz'); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $resource->getId()->willReturn('1'); - - $configuration->isHtmlRequest()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $stateMachine->can($configuration, $resource)->willReturn(true); - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldBeCalled(); - - $flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource)->shouldBeCalled(); - - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($postEvent); - - $postEvent->getResponse()->willReturn(null); - - $redirectHandler->redirectToResource($configuration, $resource)->willReturn($redirectResponse); - - $this->applyStateMachineTransitionAction($request)->shouldReturn($redirectResponse); - } - - public function it_uses_response_from_post_apply_state_machine_transition_event_if_defined( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - FlashHelperInterface $flashHelper, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - StateMachineInterface $stateMachine, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - ResourceControllerEvent $event, - ResourceControllerEvent $postEvent, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - $request->get('_csrf_token')->willReturn('xyz'); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $resource->getId()->willReturn('1'); - - $configuration->isHtmlRequest()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $stateMachine->can($configuration, $resource)->willReturn(true); - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldBeCalled(); - - $flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource)->shouldBeCalled(); - - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($postEvent); - - $postEvent->hasResponse()->willReturn(true); - $postEvent->getResponse()->willReturn($redirectResponse); - - $this->applyStateMachineTransitionAction($request)->shouldReturn($redirectResponse); - } - - public function it_does_not_apply_state_machine_transition_on_resource_and_redirects_for_html_requests_stopped_via_event( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - StateMachineInterface $stateMachine, - ObjectManager $manager, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - RedirectHandlerInterface $redirectHandler, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - Request $request, - Response $redirectResponse - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - $request->get('_csrf_token')->willReturn('xyz'); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $resource->getId()->willReturn('1'); - - $configuration->isHtmlRequest()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(true); - - $manager->flush()->shouldNotBeCalled(); - $stateMachine->apply($resource)->shouldNotBeCalled(); - - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->shouldNotBeCalled(); - $flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource)->shouldNotBeCalled(); - - $event->getResponse()->willReturn(null); - - $flashHelper->addFlashFromEvent($configuration, $event)->shouldBeCalled(); - $redirectHandler->redirectToResource($configuration, $resource)->willReturn($redirectResponse); - - $this->applyStateMachineTransitionAction($request)->shouldReturn($redirectResponse); - } - - public function it_does_not_apply_state_machine_transition_on_resource_and_return_event_response_for_html_requests_stopped_via_event( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - StateMachineInterface $stateMachine, - ObjectManager $manager, - RepositoryInterface $repository, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - CsrfTokenManagerInterface $csrfTokenManager, - ContainerInterface $container, - ResourceControllerEvent $event, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isCsrfProtectionEnabled()->willReturn(true); - $request->get('_csrf_token')->willReturn('xyz'); - - $container->has('security.csrf.token_manager')->willReturn(true); - $container->get('security.csrf.token_manager')->willReturn($csrfTokenManager); - $csrfTokenManager->isTokenValid(new CsrfToken('1', 'xyz'))->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $resource->getId()->willReturn('1'); - - $configuration->isHtmlRequest()->willReturn(true); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(true); - - $manager->flush()->shouldNotBeCalled(); - $stateMachine->apply($resource)->shouldNotBeCalled(); - - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->shouldNotBeCalled(); - $flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource)->shouldNotBeCalled(); - - $flashHelper->addFlashFromEvent($configuration, $event)->shouldBeCalled(); - - $event->hasResponse()->willReturn(true); - $event->getResponse()->willReturn($response); - - $this->applyStateMachineTransitionAction($request)->shouldReturn($response); - } - - public function it_applies_state_machine_transition_on_resource_and_returns_200_for_non_html_requests( - MetadataInterface $metadata, - ParameterBagInterface $parameterBag, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - ViewHandlerInterface $viewHandler, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - StateMachineInterface $stateMachine, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - ResourceControllerEvent $event, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->getParameters()->willReturn($parameterBag); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isCsrfProtectionEnabled()->willReturn(false); - - $parameterBag->get('return_content', true)->willReturn(true); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $configuration->isHtmlRequest()->willReturn(false); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $stateMachine->can($configuration, $resource)->willReturn(true); - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldBeCalled(); - - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->shouldBeCalled(); - - $expectedView = View::create($resource, 200); - - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->applyStateMachineTransitionAction($request)->shouldReturn($response); - } - - public function it_applies_state_machine_transition_on_resource_and_returns_204_for_non_html_requests_if_additional_option_added( - MetadataInterface $metadata, - ParameterBagInterface $parameterBag, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - ViewHandlerInterface $viewHandler, - RepositoryInterface $repository, - ObjectManager $manager, - SingleResourceProviderInterface $singleResourceProvider, - AuthorizationCheckerInterface $authorizationChecker, - EventDispatcherInterface $eventDispatcher, - StateMachineInterface $stateMachine, - ResourceUpdateHandlerInterface $resourceUpdateHandler, - RequestConfiguration $configuration, - ResourceInterface $resource, - ResourceControllerEvent $event, - Request $request, - Response $response - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->getParameters()->willReturn($parameterBag); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isCsrfProtectionEnabled()->willReturn(false); - - $parameterBag->get('return_content', true)->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $configuration->isHtmlRequest()->willReturn(false); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(false); - - $stateMachine->can($configuration, $resource)->willReturn(true); - $resourceUpdateHandler->handle($resource, $configuration, $manager)->shouldBeCalled(); - - $eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource)->shouldBeCalled(); - - $expectedView = View::create(null, 204); - - $viewHandler->handle($configuration, Argument::that($this->getViewComparingCallback($expectedView)))->willReturn($response); - - $this->applyStateMachineTransitionAction($request)->shouldReturn($response); - } - - public function it_does_not_apply_state_machine_transition_resource_and_throws_http_exception_for_non_html_requests_stopped_via_event( - MetadataInterface $metadata, - RequestConfigurationFactoryInterface $requestConfigurationFactory, - RequestConfiguration $configuration, - AuthorizationCheckerInterface $authorizationChecker, - RepositoryInterface $repository, - ObjectManager $objectManager, - StateMachineInterface $stateMachine, - SingleResourceProviderInterface $singleResourceProvider, - ResourceInterface $resource, - FlashHelperInterface $flashHelper, - EventDispatcherInterface $eventDispatcher, - ResourceControllerEvent $event, - Request $request - ): void { - $metadata->getApplicationName()->willReturn('sylius'); - $metadata->getName()->willReturn('product'); - - $requestConfigurationFactory->create($metadata, $request)->willReturn($configuration); - $configuration->hasPermission()->willReturn(true); - $configuration->getPermission(ResourceActions::UPDATE)->willReturn('sylius.product.update'); - $configuration->isCsrfProtectionEnabled()->willReturn(false); - - $authorizationChecker->isGranted($configuration, 'sylius.product.update')->willReturn(true); - $singleResourceProvider->get($configuration, $repository)->willReturn($resource); - - $configuration->isHtmlRequest()->willReturn(false); - - $eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource)->willReturn($event); - $event->isStopped()->willReturn(true); - $event->getMessage()->willReturn('Cannot approve this product.'); - $event->getErrorCode()->willReturn(500); - - $stateMachine->apply($configuration, $resource)->shouldNotBeCalled(); - $objectManager->flush()->shouldNotBeCalled(); - - $eventDispatcher->dispatchPostEvent(Argument::any())->shouldNotBeCalled(); - $flashHelper->addSuccessFlash(Argument::any())->shouldNotBeCalled(); - $flashHelper->addFlashFromEvent(Argument::any())->shouldNotBeCalled(); - - $this - ->shouldThrow(new HttpException(500, 'Cannot approve this product.')) - ->during('applyStateMachineTransitionAction', [$request]) - ; - } - - private function getViewComparingCallback(View $expectedView) - { - return function ($value) use ($expectedView) { - if (!$value instanceof View) { - return false; - } - - // Need to unwrap phpspec's Collaborators to ensure proper comparison. - $this->unwrapViewData($expectedView); - $this->nullifyDates($value); - $this->nullifyDates($expectedView); - - return - $expectedView->getStatusCode() === $value->getStatusCode() && - $expectedView->getHeaders() === $value->getHeaders() && - $expectedView->getFormat() === $value->getFormat() && - $expectedView->getData() === $value->getData() - ; - }; - } - - private function unwrapViewData(View $view): void - { - $view->setData($this->unwrapIfCollaborator($view->getData())); - } - - private function unwrapIfCollaborator($value) - { - if (null === $value) { - return null; - } - - if ($value instanceof Collaborator) { - return $value->getWrappedObject(); - } - - if (is_array($value)) { - foreach ($value as $key => $childValue) { - $value[$key] = $this->unwrapIfCollaborator($childValue); - } - } - - return $value; - } - - private function nullifyDates(View $view): void - { - $headers = $view->getHeaders(); - unset($headers['date']); - $view->setHeaders($headers); - } -} diff --git a/lib/Indexer/Tests/Extension/Command/IndexBuildCommandTest.php b/lib/Indexer/Tests/Extension/Command/IndexBuildCommandTest.php deleted file mode 100644 index 4e4229da53..0000000000 --- a/lib/Indexer/Tests/Extension/Command/IndexBuildCommandTest.php +++ /dev/null @@ -1,28 +0,0 @@ -workspace()->reset(); - } - public function testRefreshIndex(): void - { - $this->initProject(); - - $process = new Process([ - PHP_BINARY, - __DIR__ . '/../../bin/console', - 'index:build', - ], $this->workspace()->path()); - $process->mustRun(); - - self::assertEquals(0, $process->getExitCode()); - self::assertTrue($this->workspace()->exists('cache')); - } -} diff --git a/lib/Indexer/Tests/Extension/Command/IndexCleanCommandTest.php b/lib/Indexer/Tests/Extension/Command/IndexCleanCommandTest.php deleted file mode 100644 index 8d07ddef99..0000000000 --- a/lib/Indexer/Tests/Extension/Command/IndexCleanCommandTest.php +++ /dev/null @@ -1,118 +0,0 @@ - $command - */ - #[DataProvider('provideAllIndexClean')] - public function testCleanIndexWithAllInput(array $command, ?string $input): void - { - $this->initProject(); - self::assertFalse($this->workspace()->exists('cache')); - - $process = new Process([PHP_BINARY, ...$command], $this->workspace()->path(), null, $input); - $process->mustRun(); - - self::assertEquals(0, $process->getExitCode()); - self::assertFalse($this->workspace()->exists('project')); - self::assertFalse($this->workspace()->exists('vendor')); - } - - /** - * @return Generator> - */ - public static function provideAllIndexClean(): Generator - { - yield 'interactive version' => [ - [ self::CONSOLE_PATH, 'index:clean'], - IndexCleanCommand::OPT_CLEAN_ALL - ]; - yield 'non-interactive version' => [ - [ self::CONSOLE_PATH, 'index:clean', IndexCleanCommand::OPT_CLEAN_ALL, '--no-interaction'], - null - ]; - yield 'cleaning index 1 and 2' => [ - [self::CONSOLE_PATH, 'index:clean'], - "1\n1" - ]; - yield 'cleaning multiple indexes non-interactive' => [ - [self::CONSOLE_PATH, 'index:clean', 'project','vendor', '--no-interaction'], - null - ]; - } - - /** - * @param array $command - */ - #[DataProvider('provideCleanSpecificIndex')] - public function testCleanIndexWithSpecificInput(array $command, ?string $input): void - { - $this->initProject(); - - $process = new Process([PHP_BINARY, ...$command], $this->workspace()->path(), null, $input); - $process->mustRun(); - - self::assertEquals(0, $process->getExitCode()); - self::assertFalse($this->workspace()->exists('project')); - self::assertTrue($this->workspace()->exists('vendor')); - } - - /** - * @return Generator> - */ - public static function provideCleanSpecificIndex(): Generator - { - yield 'interactive version' => [ - [ self::CONSOLE_PATH, 'index:clean'], - '1' - ]; - yield 'non-interactive version' => [ - [ self::CONSOLE_PATH, 'index:clean', 'project', '--no-interaction'], - null - ]; - yield 'non-interactive version with index name' => [ - [ self::CONSOLE_PATH, 'index:clean', 'project', '--no-interaction'], - null - ]; - } - - /** - * @param array $arguments - */ - #[DataProvider('provideDoNotRemoveAnything')] - public function testCleanDoesNotRemoveIndexWithoutInput(array $arguments): void - { - $this->initProject(); - - $process = new Process([PHP_BINARY, ...$arguments], $this->workspace()->path(), null, null); - $process->mustRun(); - - self::assertEquals(0, $process->getExitCode()); - self::assertTrue($this->workspace()->exists('project')); - self::assertTrue($this->workspace()->exists('vendor')); - } - - /** - * @return Generator>> - */ - public static function provideDoNotRemoveAnything(): Generator - { - yield 'it deletes nothing on empty input' => [ - [ self::CONSOLE_PATH, 'index:clean'], - ]; - yield 'it deletes nothing on no interactive' => [ - [ self::CONSOLE_PATH, 'index:clean', '--no-interaction'], - ]; - } -} diff --git a/lib/Indexer/Tests/Extension/Command/IndexOptimiseCommandTest.php b/lib/Indexer/Tests/Extension/Command/IndexOptimiseCommandTest.php deleted file mode 100644 index 1d629d1331..0000000000 --- a/lib/Indexer/Tests/Extension/Command/IndexOptimiseCommandTest.php +++ /dev/null @@ -1,38 +0,0 @@ -workspace()->reset(); - } - - public function testRefreshIndex(): void - { - $this->initProject(); - - // create an index - $process = new Process([ - PHP_BINARY, - __DIR__ . '/../../bin/console', - 'index:build', - ], $this->workspace()->path()); - $process->mustRun(); - - // optimise the index - $process = new Process([ - PHP_BINARY, - __DIR__ . '/../../bin/console', - 'index:optimise', - ], $this->workspace()->path()); - $process->mustRun(); - - self::assertEquals(0, $process->getExitCode()); - self::assertStringContainsString('optimisations done', $process->getOutput()); - } -} diff --git a/lib/Indexer/Tests/Extension/Command/IndexQueryCommandTest.php b/lib/Indexer/Tests/Extension/Command/IndexQueryCommandTest.php deleted file mode 100644 index 68b956fca2..0000000000 --- a/lib/Indexer/Tests/Extension/Command/IndexQueryCommandTest.php +++ /dev/null @@ -1,48 +0,0 @@ -initProject(); - - $process = new Process([ - PHP_BINARY, - __DIR__ . '/../../bin/console', - 'index:query', - $query - ], $this->workspace()->path()); - $process->mustRun(); - self::assertEquals(0, $process->getExitCode()); - } - - /** - * @return Generator - */ - public static function provideQuery(): Generator - { - yield 'method' => [ - 'method#testQueryIndex', - ]; - yield 'constant' => [ - 'constant#RECORD_TYPE', - ]; - yield 'property' => [ - 'property#workspace', - ]; - yield 'class' => [ - __CLASS__, - ]; - yield 'sprintf' => [ - 'sprintf', - ]; - } -} diff --git a/lib/Indexer/Tests/Extension/Command/IndexSearchCommandTest.php b/lib/Indexer/Tests/Extension/Command/IndexSearchCommandTest.php deleted file mode 100644 index e1e2f9f1fc..0000000000 --- a/lib/Indexer/Tests/Extension/Command/IndexSearchCommandTest.php +++ /dev/null @@ -1,57 +0,0 @@ - $args - */ - #[DataProvider('provideQuery')] - public function testQueryIndex(array $args = []): void - { - $this->initProject(); - - $process = new Process(array_merge([ - PHP_BINARY, - __DIR__ . '/../../bin/console', - 'index:search', - ], $args), $this->workspace()->path()); - $process->mustRun(); - self::assertEquals(0, $process->getExitCode()); - } - - /** - * @return Generator}> - */ - public static function provideQuery(): Generator - { - yield 'all' => [ - [ - '--limit=1' - ] - ]; - - yield 'classes' => [ - [ - '--is-class', - '--is-function', - '--short-name=Foo', - '--short-name-begins=Foo', - '--fqn-begins=Foo', - '--limit=1' - ] - ]; - - yield 'constant' => [ - [ - '--is-constant', - ] - ]; - } -} diff --git a/lib/Indexer/Tests/Extension/IndexerExtensionTest.php b/lib/Indexer/Tests/Extension/IndexerExtensionTest.php deleted file mode 100644 index 91514830b4..0000000000 --- a/lib/Indexer/Tests/Extension/IndexerExtensionTest.php +++ /dev/null @@ -1,117 +0,0 @@ -initProject(); - } - - public function testReturnsImplementationFinder(): void - { - $container = $this->container(); - $finder = $container->get(ReferenceFinderExtension::SERVICE_IMPLEMENTATION_FINDER); - self::assertInstanceOf(ChainImplementationFinder::class, $finder); - } - - public function testReturnsReferenceFinder(): void - { - $container = $this->container(); - $finder = $container->get(ReferenceFinder::class); - self::assertInstanceOf(ChainReferenceFinder::class, $finder); - } - - public function testBuildIndex(): void - { - $container = $this->container(); - $indexer = $container->get(Indexer::class); - $this->assertInstanceOf(Indexer::class, $indexer); - $indexer->getJob()->run(); - } - - public function testIndexDirtyFile(): void - { - $container = $this->container(); - $indexer = $container->get(Indexer::class); - $this->assertInstanceOf(Indexer::class, $indexer); - assert($indexer instanceof Indexer); - $this->workspace()->put('foo', 'asd'); - $indexer->indexDirty( - TextDocumentBuilder::create('uri($this->workspace()->path('foo'))->build() - ); - - $files = iterator_to_array($indexer->getJob()->generator()); - $lastFile = array_pop($files); - self::assertEquals($this->workspace()->path('foo'), $lastFile, 'Dirty file was included in job'); - - $files = iterator_to_array($indexer->getJob()->generator()); - $lastFile = array_pop($files); - self::assertNotEquals($this->workspace()->path('foo'), $lastFile, 'Dirty file was not included again'); - } - - public function testRpcHandler(): void - { - $container = $this->container(); - $handler = $container->get(RpcExtension::SERVICE_REQUEST_HANDLER); - assert($handler instanceof RequestHandler); - $request = Request::fromNameAndParameters('index', []); - $response = $handler->handle($request); - self::assertInstanceOf(EchoResponse::class, $response); - self::assertMatchesRegularExpression('{Indexed [0-9]+ files}', $response->message()); - } - - public function testThrowsExceptionIfEnabledWatcherDoesntExist(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Unknown watchers "foobar" specified, available watchers: '); - $container = $this->container([ - IndexerExtension::PARAM_ENABLED_WATCHERS => ['foobar'], - ]); - $container->get(Watcher::class); - } - - public function testUseNullWatcherIfEnabledWatchersIsEmpty(): void - { - $container = $this->container([ - IndexerExtension::PARAM_ENABLED_WATCHERS => [], - ]); - self::assertInstanceOf(NullWatcher::class, $container->get(Watcher::class)); - } - - public function testSourceLocator(): void - { - $this->initProject(); - - $container = $this->container(); - $indexer = $container->get(Indexer::class); - assert($indexer instanceof Indexer); - $indexer->reset(); - $indexer->getJob()->run(); - $reflector = $container->get(WorseReflectionExtension::SERVICE_REFLECTOR); - assert($reflector instanceof Reflector); - $class = $reflector->reflectClass('ClassWithWrongName'); - self::assertInstanceOf(ReflectionClass::class, $class); - } -} diff --git a/lib/Indexer/Tests/IntegrationTestCase.php b/lib/Indexer/Tests/IntegrationTestCase.php deleted file mode 100644 index 6fbb769239..0000000000 --- a/lib/Indexer/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,143 +0,0 @@ -workspace()->loadManifest((string)file_get_contents(__DIR__ . '/Adapter/Manifest/buildIndex.php.test')); - $process = new Process([ - 'composer', 'install' - ], $this->workspace()->path('/')); - $process->mustRun(); - } - - protected function indexAgent(): TestIndexAgent - { - return $this->indexAgentBuilder()->buildTestAgent(); - } - - protected function indexAgentBuilder(string $path = 'project'): IndexAgentBuilder - { - return IndexAgentBuilder::create( - $this->workspace()->path('repo'), - $this->workspace()->path($path), - )->setReferenceEnhancer( - new WorseRecordReferenceEnhancer( - $this->createReflector(), - $this->createLogger(), - new FilesystemTextDocumentLocator(), - ) - ); - } - - protected function buildIndex(?Index $index = null): Index - { - $agent = $this->indexAgent(); - $agent->indexer()->getJob()->run(); - - return $agent->index(); - } - - protected function createReflector(): Reflector - { - return ReflectorBuilder::create()->addLocator( - new StubSourceLocator( - ReflectorBuilder::create()->build(), - $this->workspace()->path('/'), - $this->workspace()->path('/') - ) - )->build(); - } - - protected function indexQuery(Index $index): QueryClient - { - return new QueryClient( - $index, - new WorseRecordReferenceEnhancer( - $this->createReflector(), - $this->createLogger(), - new FilesystemTextDocumentLocator(), - ) - ); - } - - protected function container(array $config = []): Container - { - $key = serialize($config); - static $container = []; - - if (isset($container[$key])) { - return $container[$key]; - } - - $container[$key] = PhpactorContainer::fromExtensions( - [ - ConsoleExtension::class, - IndexerExtension::class, - FilePathResolverExtension::class, - LoggingExtension::class, - SourceCodeFilesystemExtension::class, - WorseReflectionExtension::class, - ClassToFileExtension::class, - RpcExtension::class, - ComposerAutoloaderExtension::class, - ReferenceFinderExtension::class, - ], - array_merge([ - FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ . '/../', - FilePathResolverExtension::PARAM_PROJECT_ROOT => $this->workspace()->path(), - IndexerExtension::PARAM_INDEX_PATH => $this->workspace()->path('/cache'), - LoggingExtension::PARAM_ENABLED=> true, - LoggingExtension::PARAM_PATH=> 'php://stderr', - WorseReflectionExtension::PARAM_ENABLE_CACHE=> false, - WorseReflectionExtension::PARAM_STUB_DIR => $this->workspace()->path(), - ], $config) - ); - - return $container[$key]; - } - - private function createLogger(): LoggerInterface - { - return new class() extends AbstractLogger { - public function log($level, $message, array $context = []): void - { - fwrite(STDOUT, sprintf("[%s] %s\n", $level, $message)); - } - }; - } -} diff --git a/lib/Indexer/Tests/Unit/Adapter/Filesystem/FilesystemFileListProviderTest.php b/lib/Indexer/Tests/Unit/Adapter/Filesystem/FilesystemFileListProviderTest.php deleted file mode 100644 index c5c28e64fc..0000000000 --- a/lib/Indexer/Tests/Unit/Adapter/Filesystem/FilesystemFileListProviderTest.php +++ /dev/null @@ -1,64 +0,0 @@ - - */ - private ObjectProphecy $index; - - protected function setUp(): void - { - $this->filesystem = new SimpleFilesystem(FilePath::fromString($this->workspace()->path())); - $this->provider = new FilesystemFileListProvider($this->filesystem); - $this->workspace()->reset(); - $this->index = $this->prophesize(Index::class); - } - - public function testProvidesSingleFile(): void - { - $this->workspace()->put('foo.php', 'provider->provideFileList($index, $this->workspace()->path('foo.php')); - self::assertEquals(1, $list->count()); - } - - public function testProvidesFromFilesystemRoot(): void - { - $this->workspace()->put('foo.php', 'workspace()->put('bar.php', 'provider->provideFileList($index); - - self::assertEquals(2, $list->count()); - } - - public function testDoesNotUseCacheIfSubPathProvided(): void - { - $this->workspace()->put('foo.php', 'index->isFresh()->shouldNotBeCalled(); - - $list = $this->provider->provideFileList($this->index->reveal(), $this->workspace()->path()); - - self::assertEquals(1, $list->count()); - } -} diff --git a/lib/Indexer/Tests/Unit/Adapter/Php/Serialized/FileRepositoryTest.php b/lib/Indexer/Tests/Unit/Adapter/Php/Serialized/FileRepositoryTest.php deleted file mode 100644 index ac8e94f61a..0000000000 --- a/lib/Indexer/Tests/Unit/Adapter/Php/Serialized/FileRepositoryTest.php +++ /dev/null @@ -1,89 +0,0 @@ -createFileRepository(); - $repo->put(ClassRecord::fromName('Foobar')); - $repo->put(ClassRecord::fromName('Barfoo')); - $repo->flush(); - - /** @var list */ - $records = iterator_to_array($repo->iterator()); - self::assertCount(2, $records); - self::assertEquals('class', $records[array_key_first($records)]->recordType()); - } - - public function testResetRemovesTheIndex(): void - { - $repo = $this->createFileRepository(); - $this->workspace()->put('index/something.cache', 'foo'); - $this->workspace()->put('index/something/else/some.cache', 'bar'); - - self::assertFileExists($this->workspace()->path('index/something.cache')); - self::assertFileExists($this->workspace()->path('index/something/else/some.cache')); - - $repo->reset(); - - self::assertFileDoesNotExist($this->workspace()->path('index/something.cache')); - self::assertFileDoesNotExist($this->workspace()->path('index/something/else/some.cache')); - } - - public function testRemovesClassRecord(): void - { - $repo = $this->createFileRepository(); - $repo->put(ClassRecord::fromName('Foobar')); - $repo->flush(); - self::assertNotNull($repo->get(ClassRecord::fromName('Foobar'))); - $repo->remove(ClassRecord::fromName('Foobar')); - self::assertNull($repo->get(ClassRecord::fromName('Foobar'))); - } - - public function testLogsCorruptedRecordError(): void - { - $serialized = $this->prophesize(RecordSerializer::class); - $logger = $this->prophesize(LoggerInterface::class); - - $serialized->deserialize(Argument::any())->willThrow( - new CorruptedRecord('no') - ); - - $serialized->serialize(Argument::any())->willReturn('foo'); - - $repo = new FileRepository( - $this->workspace()->path('index'), - $serialized->reveal(), - $logger->reveal() - ); - - $repo->put(ClassRecord::fromName('Foo')); - $repo->flush(); - $repo->get(ClassRecord::fromName('Foo')); - - $logger->warning(Argument::containingString('corrupted'))->shouldHaveBeenCalled(); - } - - private function createFileRepository(): FileRepository - { - return new FileRepository( - $this->workspace()->path('index'), - new PhpSerializer() - ); - } -} diff --git a/lib/Indexer/Tests/Unit/Adapter/Php/Serialized/SerializedIndexTest.php b/lib/Indexer/Tests/Unit/Adapter/Php/Serialized/SerializedIndexTest.php deleted file mode 100644 index 7b8d1621f6..0000000000 --- a/lib/Indexer/Tests/Unit/Adapter/Php/Serialized/SerializedIndexTest.php +++ /dev/null @@ -1,101 +0,0 @@ -workspace()->path(), - new PhpSerializer() - ); - $index = new SerializedIndex( - $repo, - new FilesystemTextDocumentLocator(), - ); - $info = new SplFileInfo($this->workspace()->path('no')); - Assert::assertFalse($index->isFresh($info), 'File doesn\'t exist, so its not fresh'); - } - - public function testOptimizeWillRemoveRecordsWithNonExistingFiles(): void - { - $this->workspace()->reset(); - $this->workspace()->put('hello.php', 'workspace()->put('ref1.php', 'workspace()->put('ref2.php', 'createRepo(); - $index = $this->createIndex($repo); - $index->write( - ClassRecord::fromName('Foobar')->setFilePath( - TextDocumentUri::fromString($this->workspace()->path('hello.php')) - ), - ); - $index->write( - ClassRecord::fromName('Barfoo')->setFilePath( - TextDocumentUri::fromString($this->workspace()->path('goodbye.php')) - ), - ); - $repo->flush(); - - iterator_to_array($index->optimise(false)); - - self::assertTrue($index->has(ClassRecord::fromName('Foobar'))); - self::assertFalse($index->has(ClassRecord::fromName('Barfoo'))); - } - - public function testOptimizeWillRemoveReferencesToNonExistingFiles(): void - { - $this->workspace()->reset(); - $this->workspace()->put('hello.php', 'workspace()->put('ref1.php', 'workspace()->put('ref2.php', 'createRepo(); - $index = $this->createIndex($repo); - $index->write( - ClassRecord::fromName('Foobar')->setFilePath( - TextDocumentUri::fromString($this->workspace()->path('hello.php')) - )->addReference( - $this->workspace()->path('ref1.php'), - )->addReference( - $this->workspace()->path('ref2.php'), - )->addReference( - $this->workspace()->path('ref3.php'), - ), - ); - $repo->flush(); - - iterator_to_array($index->optimise(false)); - - $record = $index->get(ClassRecord::fromName('Foobar')); - self::assertEquals([ - $this->workspace()->path('ref1.php'), - $this->workspace()->path('ref2.php'), - ], $record->references()); - } - - private function createRepo(): FileRepository - { - return new FileRepository($this->workspace()->path(), new PhpSerializer()); - } - - private function createIndex(FileRepository $repo): SerializedIndex - { - return new SerializedIndex( - $repo, - new FilesystemTextDocumentLocator(), - ); - } -} diff --git a/lib/Indexer/Tests/Unit/Adapter/ReferenceFinder/Util/ContainerTypeResolverTest.php b/lib/Indexer/Tests/Unit/Adapter/ReferenceFinder/Util/ContainerTypeResolverTest.php deleted file mode 100644 index 59ee1e7e5c..0000000000 --- a/lib/Indexer/Tests/Unit/Adapter/ReferenceFinder/Util/ContainerTypeResolverTest.php +++ /dev/null @@ -1,102 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest(implode("\n", $manifest)); - $source = $this->workspace()->getContents('test.php'); - [$source, $offset] = ExtractOffset::fromSource($source); - - $type = (new ContainerTypeResolver($this->createReflector()))->resolveDeclaringContainerType( - /** @phpstan-ignore-next-line */ - $memberType, - $memberName, - $containerType - ); - - self::assertEquals($expectedType, $type); - } - - /** - * @return Generator - */ - public static function provideResolve(): Generator - { - yield 'no container type' => [ - ["// File: test.php\n"], - 'method', - 'foobar', - null, - null - ]; - - yield 'declaring container type' => [ - ["// File: test.php\n [ - [ - "// File: test.php\n [ - [ - "// File: test.php\n [ - [ - "// File: test.php\n [ - [ - "// File: test.php\nexpectException(SourceNotFound::class); - $index = new InMemoryIndex(); - $locator = $this->createLocator($index); - $locator->locate(Name::fromString('Foobar')); - } - - public function testThrowsExceptionIfFileDoesNotExist(): void - { - $this->expectException(SourceNotFound::class); - $this->expectExceptionMessage('does not exist'); - $record = ClassRecord::fromName('Foobar') - ->setType('class') - ->setStart(ByteOffset::fromInt(0)) - ->setEnd(ByteOffset::fromInt(0)) - ->setFilePath(TextDocumentUri::fromString('/nope.php')); - - $index = new InMemoryIndex(); - $index->write($record); - $locator = $this->createLocator($index); - $locator->locate(Name::fromString('Foobar')); - } - - public function testReturnsSourceCode(): void - { - $record = ClassRecord::fromName('Foobar') - ->setType('class') - ->setStart(ByteOffset::fromInt(0)) - ->setEnd(ByteOffset::fromInt(10)) - ->setFilePath(TextDocumentUri::fromString(__FILE__)); - - $index = new InMemoryIndex(); - $index->write($record); - $locator = $this->createLocator($index); - $sourceCode = $locator->locate(Name::fromString('Foobar')); - $this->assertEquals(Path::canonicalize(__FILE__), $sourceCode->uri()?->path()); - } - - private function createLocator(InMemoryIndex $index): IndexerClassSourceLocator - { - return new IndexerClassSourceLocator($index); - } -} diff --git a/lib/Indexer/Tests/Unit/Adapter/Worse/IndexerFunctionSourceLocatorTest.php b/lib/Indexer/Tests/Unit/Adapter/Worse/IndexerFunctionSourceLocatorTest.php deleted file mode 100644 index cb55ab6ced..0000000000 --- a/lib/Indexer/Tests/Unit/Adapter/Worse/IndexerFunctionSourceLocatorTest.php +++ /dev/null @@ -1,56 +0,0 @@ -expectException(SourceNotFound::class); - $index = new InMemoryIndex(); - $locator = $this->createLocator($index); - $locator->locate(Name::fromString('Foobar')); - } - - public function testThrowsExceptionIfFileDoesNotExist(): void - { - $this->expectException(SourceNotFound::class); - $this->expectExceptionMessage('does not exist'); - $record = new FunctionRecord( - FullyQualifiedName::fromString('Foobar') - ); - $record->setFilePath(TextDocumentUri::fromString('/nope.php')); - $index = new InMemoryIndex(); - $index->write($record); - $locator = $this->createLocator($index); - $locator->locate(Name::fromString('Foobar')); - } - - public function testReturnsSourceCode(): void - { - $record = new FunctionRecord( - FullyQualifiedName::fromString('Foobar') - ); - $record->setFilePath(TextDocumentUri::fromString(__FILE__)); - $index = new InMemoryIndex(); - $index->write($record); - $locator = $this->createLocator($index); - $sourceCode = $locator->locate(Name::fromString('Foobar')); - $this->assertEquals(Path::canonicalize(__FILE__), $sourceCode->uri()?->path()); - } - - private function createLocator(InMemoryIndex $index): IndexerFunctionSourceLocator - { - return new IndexerFunctionSourceLocator($index); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/FileListProvider/DirtyFileListProviderTest.php b/lib/Indexer/Tests/Unit/Model/FileListProvider/DirtyFileListProviderTest.php deleted file mode 100644 index fd0d23155a..0000000000 --- a/lib/Indexer/Tests/Unit/Model/FileListProvider/DirtyFileListProviderTest.php +++ /dev/null @@ -1,73 +0,0 @@ -createProvider(); - $this->workspace()->put(self::EXAMPLE_FILE_1, ''); - $this->workspace()->put(self::EXAMPLE_FILE_2, ''); - $tracker->markDirty(TextDocumentUri::fromString($this->workspace()->path(self::EXAMPLE_FILE_1))); - $tracker->markDirty(TextDocumentUri::fromString($this->workspace()->path(self::EXAMPLE_FILE_2))); - - $files = $tracker->provideFileList(new InMemoryIndex([])); - - self::assertCount(2, $files); - } - - public function testReleasedDirtyFilesAreNoLongerTracked(): void - { - $tracker = $this->createProvider(); - $this->workspace()->put(self::EXAMPLE_FILE_1, ''); - $tracker->markDirty(TextDocumentUri::fromString($this->workspace()->path(self::EXAMPLE_FILE_1))); - - self::assertFileExists($this->workspace()->path('dirty')); - - $files = $tracker->provideFileList(new InMemoryIndex([])); - self::assertCount(1, $files); - - $files = $tracker->provideFileList(new InMemoryIndex([])); - self::assertCount(0, $files); - self::assertFileDoesNotExist($this->workspace()->path('dirty')); - } - - public function testDoNotDuplicate(): void - { - $tracker = $this->createProvider(); - $this->workspace()->put(self::EXAMPLE_FILE_1, ''); - $this->workspace()->put(self::EXAMPLE_FILE_2, ''); - $tracker->markDirty(TextDocumentUri::fromString($this->workspace()->path(self::EXAMPLE_FILE_1))); - $tracker->markDirty(TextDocumentUri::fromString($this->workspace()->path(self::EXAMPLE_FILE_2))); - $tracker->markDirty(TextDocumentUri::fromString($this->workspace()->path(self::EXAMPLE_FILE_2))); - $tracker->markDirty(TextDocumentUri::fromString($this->workspace()->path(self::EXAMPLE_FILE_2))); - - $files = $tracker->provideFileList(new InMemoryIndex([])); - - self::assertCount(2, $files); - } - - public function testNonExistingFile(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Dirty index'); - $tracker = $this->createProvider('foobar/no'); - $tracker->markDirty(TextDocumentUri::fromString($this->workspace()->path(self::EXAMPLE_FILE_1))); - } - - private function createProvider(string $path = 'dirty'): DirtyFileListProvider - { - $tracker = new DirtyFileListProvider($this->workspace()->path($path)); - return $tracker; - } -} diff --git a/lib/Indexer/Tests/Unit/Model/MemoryUsageTest.php b/lib/Indexer/Tests/Unit/Model/MemoryUsageTest.php deleted file mode 100644 index 0d8c6f58e8..0000000000 --- a/lib/Indexer/Tests/Unit/Model/MemoryUsageTest.php +++ /dev/null @@ -1,78 +0,0 @@ -memoryLimit(); - - // the result is system dependent - $this->addToAssertionCount(1); - } - - public function testMemoryUsage(): void - { - self::assertIsInt(MemoryUsage::create()->memoryUsage()); - } - - #[DataProvider('provideFormat')] - public function testFormat(string $limit, int $usage, string $expected): void - { - self::assertEquals($expected, MemoryUsage::createFromLimitAndUsage($limit, $usage)->memoryUsageFormatted()); - } - - /** - * @return Generator - */ - public static function provideFormat(): Generator - { - yield 'infinite memory' => [ - '-1', - 0, - '0/∞ mb' - ]; - - yield [ - '1048576', - 0, - '0/1 mb' - ]; - - yield [ - '1000000', - 1000000, - '1/1 mb' - ]; - - yield [ - '1000K', - 1000000, - '1/1 mb' - ]; - - yield [ - '1M', - 1000000, - '1/1 mb' - ]; - - yield [ - '100M', - 1000000, - '1/100 mb' - ]; - - yield [ - '1G', - 1000000, - '1/1,000 mb' - ]; - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/AndCriteriaTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/AndCriteriaTest.php deleted file mode 100644 index 8a7d0b03eb..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/AndCriteriaTest.php +++ /dev/null @@ -1,30 +0,0 @@ -isSatisfiedBy(ClassRecord::fromName('foo'))); - } - - public function testNotAllTrue(): void - { - self::assertFalse(Criteria::and( - new TrueCriteria(), - new FalseCriteria(), - new TrueCriteria() - )->isSatisfiedBy(ClassRecord::fromName('foo'))); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/ExactShortNameTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/ExactShortNameTest.php deleted file mode 100644 index d699264dc2..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/ExactShortNameTest.php +++ /dev/null @@ -1,41 +0,0 @@ -isSatisfiedBy($record)); - } - - public function testMatches(): void - { - $record = ClassRecord::fromName('Foobar\\Barfoo'); - self::assertTrue((new ExactShortName('Barfoo'))->isSatisfiedBy($record)); - } - - public function testNotMatches(): void - { - $record = ClassRecord::fromName('Foobar\\Bazfoo'); - self::assertFalse((new ExactShortName('Barfoo'))->isSatisfiedBy($record)); - } - - public function testNotMatchesPartial(): void - { - $record = ClassRecord::fromName('Foobar\\Barfoos'); - self::assertFalse((new ExactShortName('Barfoo'))->isSatisfiedBy($record)); - } - - public function testMatchesGlobal(): void - { - $record = ClassRecord::fromName('Barfoo'); - self::assertTrue((new ExactShortName('Barfoo'))->isSatisfiedBy($record)); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/FileAbsolutePathBeginsWithTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/FileAbsolutePathBeginsWithTest.php deleted file mode 100644 index 463e7c83e0..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/FileAbsolutePathBeginsWithTest.php +++ /dev/null @@ -1,33 +0,0 @@ -setFilePath(TextDocumentUri::fromString('/foobar')); - self::assertFalse(Criteria::fileAbsolutePathBeginsWith('/baz')->isSatisfiedBy($record)); - } - - public function testBeginsWith(): void - { - $record = ClassRecord::fromName('Foobar\\Barfoo')->setFilePath(TextDocumentUri::fromString('/foobar/bazboo/baz.php')); - self::assertTrue(Criteria::fileAbsolutePathBeginsWith('/foobar')->isSatisfiedBy($record)); - } - - public function testBeginsWithTrailingSlash(): void - { - $record = ClassRecord::fromName('Foobar\\Barfoo') - ->setFilePath(TextDocumentUri::fromString('/foobar/bazboo/baz.php')); - - self::assertTrue( - Criteria::fileAbsolutePathBeginsWith('/foobar/')->isSatisfiedBy($record) - ); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/FqnBeginsWithTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/FqnBeginsWithTest.php deleted file mode 100644 index b7e5a4b805..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/FqnBeginsWithTest.php +++ /dev/null @@ -1,46 +0,0 @@ -isSatisfiedBy($record)); - } - - public function testMatchesExact(): void - { - $record = ClassRecord::fromName('Foobar\\Barfoo'); - self::assertTrue(Criteria::fqnBeginsWith('Foobar\\Barfoo')->isSatisfiedBy($record)); - } - - public function testNotMatches(): void - { - $record = ClassRecord::fromName('Foobar\\Bazfoo'); - self::assertFalse(Criteria::fqnBeginsWith('Barfoo')->isSatisfiedBy($record)); - } - - public function testMatchesPartialBeginingWith(): void - { - $record = ClassRecord::fromName('Foobar\\Barfoos'); - self::assertTrue(Criteria::fqnBeginsWith('Foo')->isSatisfiedBy($record)); - } - - public function testNotMatchesPartialEndsWith(): void - { - $record = ClassRecord::fromName('Foobar\\abBarfoo'); - self::assertFalse(Criteria::fqnBeginsWith('Barfoo')->isSatisfiedBy($record)); - } - - public function testMatchesGlobal(): void - { - $record = ClassRecord::fromName('Barfoo'); - self::assertTrue(Criteria::fqnBeginsWith('Barfoo')->isSatisfiedBy($record)); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsClassTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsClassTest.php deleted file mode 100644 index 29cb029a39..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsClassTest.php +++ /dev/null @@ -1,17 +0,0 @@ -isSatisfiedBy(ClassRecord::fromName('foobar'))); - self::assertFalse(Criteria::isClass()->isSatisfiedBy(FunctionRecord::fromName('foobar'))); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsFunctionTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsFunctionTest.php deleted file mode 100644 index 365e848b7c..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsFunctionTest.php +++ /dev/null @@ -1,17 +0,0 @@ -isSatisfiedBy(ClassRecord::fromName('foobar'))); - self::assertTrue(Criteria::isFunction()->isSatisfiedBy(FunctionRecord::fromName('foobar'))); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsMemberTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsMemberTest.php deleted file mode 100644 index df93a7d8b8..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/IsMemberTest.php +++ /dev/null @@ -1,17 +0,0 @@ -isSatisfiedBy(ClassRecord::fromName('foobar'))); - self::assertTrue(Criteria::isMember()->isSatisfiedBy(MemberRecord::fromIdentifier('method#barfoo'))); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/OrCriteriaTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/OrCriteriaTest.php deleted file mode 100644 index 0602226414..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/OrCriteriaTest.php +++ /dev/null @@ -1,30 +0,0 @@ -isSatisfiedBy(ClassRecord::fromName('foo'))); - } - - public function testOneTrueReturnsTrue(): void - { - self::assertTrue(Criteria::or( - new TrueCriteria(), - new FalseCriteria(), - new TrueCriteria() - )->isSatisfiedBy(ClassRecord::fromName('foo'))); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/ShortNameBeginsWithTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/ShortNameBeginsWithTest.php deleted file mode 100644 index 239103a5ea..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/ShortNameBeginsWithTest.php +++ /dev/null @@ -1,47 +0,0 @@ -isSatisfiedBy($record)); - } - - public function testMatchesExact(): void - { - $record = ClassRecord::fromName('Foobar\\Barfoo'); - self::assertTrue((new ShortNameBeginsWith('Barfoo'))->isSatisfiedBy($record)); - } - - public function testNotMatches(): void - { - $record = ClassRecord::fromName('Foobar\\Bazfoo'); - self::assertFalse((new ShortNameBeginsWith('Barfoo'))->isSatisfiedBy($record)); - } - - public function testMatchesPartialBeginingWith(): void - { - $record = ClassRecord::fromName('Foobar\\Barfoos'); - self::assertTrue((new ShortNameBeginsWith('Barfoo'))->isSatisfiedBy($record)); - } - - public function testNotMatchesPartialEndsWith(): void - { - $record = ClassRecord::fromName('Foobar\\abBarfoo'); - self::assertFalse((new ShortNameBeginsWith('Barfoo'))->isSatisfiedBy($record)); - } - - public function testMatchesGlobal(): void - { - $record = ClassRecord::fromName('Barfoo'); - self::assertTrue((new ShortNameBeginsWith('Barfoo'))->isSatisfiedBy($record)); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Query/Criteria/ShortNameContainsTest.php b/lib/Indexer/Tests/Unit/Model/Query/Criteria/ShortNameContainsTest.php deleted file mode 100644 index 34903c9ddc..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Query/Criteria/ShortNameContainsTest.php +++ /dev/null @@ -1,35 +0,0 @@ -isSatisfiedBy( - MemberRecord::fromIdentifier('method#foobar') - ) - ); - self::assertTrue( - Criteria::shortNameContains('ooba')->isSatisfiedBy( - MemberRecord::fromIdentifier('method#foobar') - ) - ); - self::assertTrue( - Criteria::shortNameContains('OoBa')->isSatisfiedBy( - MemberRecord::fromIdentifier('method#foobar') - ), - 'Case insensitive' - ); - self::assertFalse( - Criteria::shortNameContains('foobar')->isSatisfiedBy( - MemberRecord::fromIdentifier('method#barfoo') - ) - ); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/Record/MemberRecordTest.php b/lib/Indexer/Tests/Unit/Model/Record/MemberRecordTest.php deleted file mode 100644 index 43b950bfa4..0000000000 --- a/lib/Indexer/Tests/Unit/Model/Record/MemberRecordTest.php +++ /dev/null @@ -1,24 +0,0 @@ -expectException(RuntimeException::class); - $this->expectExceptionMessage('Invalid member identifier'); - MemberRecord::fromIdentifier('member'); - } - - public function testExceptionOnInvalidType(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Invalid member type'); - MemberRecord::fromIdentifier('asd#member'); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/SearchIndex/FilteredSearchIndexTest.php b/lib/Indexer/Tests/Unit/Model/SearchIndex/FilteredSearchIndexTest.php deleted file mode 100644 index b678e95851..0000000000 --- a/lib/Indexer/Tests/Unit/Model/SearchIndex/FilteredSearchIndexTest.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ - private ObjectProphecy $innerIndex; - - private FilteredSearchIndex $index; - - protected function setUp(): void - { - $this->innerIndex = $this->prophesize(SearchIndex::class); - $this->index = new FilteredSearchIndex($this->innerIndex->reveal(), [ClassRecord::RECORD_TYPE]); - } - - public function testDecoration(): void - { - $this->innerIndex->search(new ShortNameBeginsWith('foobar'))->willYield([ClassRecord::fromName('Foobar')])->shouldBeCalled(); - $this->innerIndex->flush()->shouldBeCalled(); - $this->index->search(new ShortNameBeginsWith('foobar')); - $this->index->flush(); - } - - public function testWritesRecordThatIsAllowed(): void - { - $this->innerIndex->write(ClassRecord::fromName('FOOBAR'))->shouldBeCalled(); - $this->index->write(ClassRecord::fromName('FOOBAR')); - } - - public function testDoesNotWriteRecordsNotAllowed(): void - { - $this->innerIndex->write(Argument::any())->shouldNotBeCalled(); - $this->index->write(FunctionRecord::fromName('FOOBAR')); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/SearchIndex/SearchIncludeIndexTest.php b/lib/Indexer/Tests/Unit/Model/SearchIndex/SearchIncludeIndexTest.php deleted file mode 100644 index c00239ffdb..0000000000 --- a/lib/Indexer/Tests/Unit/Model/SearchIndex/SearchIncludeIndexTest.php +++ /dev/null @@ -1,30 +0,0 @@ -search(Criteria::or( - Criteria::fqnBeginsWith('Foo'), - Criteria::fqnBeginsWith('Baz'), - ))); - self::assertCount(3, $records); - } -} diff --git a/lib/Indexer/Tests/Unit/Model/SearchIndex/ValidatingSearchIndexTest.php b/lib/Indexer/Tests/Unit/Model/SearchIndex/ValidatingSearchIndexTest.php deleted file mode 100644 index 9b826e26a9..0000000000 --- a/lib/Indexer/Tests/Unit/Model/SearchIndex/ValidatingSearchIndexTest.php +++ /dev/null @@ -1,87 +0,0 @@ -innerSearchIndex = new InMemorySearchIndex(); - $this->index = new InMemoryIndex(); - $this->searchIndex = new ValidatingSearchIndex( - $this->innerSearchIndex, - $this->index, - new NullLogger() - ); - } - - public function testWillRemoveResultIfNotExistIndex(): void - { - $record = ClassRecord::fromName('Foobar'); - $this->innerSearchIndex->write($record); - - self::assertSearchCount(0, $this->searchIndex->search(new ShortNameBeginsWith('Foobar'))); - self::assertFalse($this->innerSearchIndex->has($record)); - } - - public function testYieldsRecordsWithoutAPath(): void - { - $record = MemberRecord::fromIdentifier('method#foo'); - $this->index->write($record); - $this->innerSearchIndex->write($record); - - self::assertSearchCount(1, $this->searchIndex->search(new ShortNameBeginsWith('foo'))); - } - - public function testRemovesFromIndexIfFileDoesNotExist(): void - { - $record = ClassRecord::fromName('Foobar') - ->setFilePath($this->workspacePath('nope.php')); - - $this->index->write($record); - $this->innerSearchIndex->write($record); - - self::assertSearchCount(0, $this->searchIndex->search(new ShortNameBeginsWith('Foobar'))); - self::assertFalse($this->innerSearchIndex->has($record)); - } - - public function testYieldsSearchResultIfFileExists(): void - { - $this->workspace()->put('yep.php', 'foo'); - $record = ClassRecord::fromName('Foobar') - ->setFilePath($this->workspacePath('yep.php')); - - $this->index->write($record); - $this->innerSearchIndex->write($record); - - self::assertSearchCount(1, $this->searchIndex->search(new ShortNameBeginsWith('Foobar'))); - self::assertTrue($this->innerSearchIndex->has($record)); - } - - private static function assertSearchCount(int $int, Generator $generator): void - { - self::assertEquals($int, count(iterator_to_array($generator))); - } - - private function workspacePath(string $string): TextDocumentUri - { - return TextDocumentUri::fromString($this->workspace()->path($string)); - } -} diff --git a/lib/Indexer/Tests/Unit/Util/PhpNameMatcherTest.php b/lib/Indexer/Tests/Unit/Util/PhpNameMatcherTest.php deleted file mode 100644 index 23ef87a750..0000000000 --- a/lib/Indexer/Tests/Unit/Util/PhpNameMatcherTest.php +++ /dev/null @@ -1,16 +0,0 @@ - __DIR__ . '/../', - IndexerExtension::PARAM_INDEX_PATH => __DIR__ . '/../Workspace/cache', - IndexerExtension::PARAM_EXCLUDE_PATTERNS => ['cache'], - IndexerExtension::PARAM_ENABLED_WATCHERS => ['watchman', 'find'], - WorseReflectionExtension::PARAM_ENABLE_CACHE => true, - LoggingExtension::PARAM_ENABLED => false, - LoggingExtension::PARAM_LEVEL => 'debug', - LoggingExtension::PARAM_PATH=> 'php://stdout', -]); - -$application = new Application(); -$application->setCommandLoader( - $container->get(ConsoleExtension::SERVICE_COMMAND_LOADER) -); -$application->run(); diff --git a/lib/Indexer/Util/Filesystem.php b/lib/Indexer/Util/Filesystem.php deleted file mode 100644 index 6f6e9eebc4..0000000000 --- a/lib/Indexer/Util/Filesystem.php +++ /dev/null @@ -1,67 +0,0 @@ -getType(), ['socket', 'file', 'link'])) { - unlink($path); - return; - } - } - - $files = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::CHILD_FIRST - ); - - foreach ($files as $file) { - self::removeDir($file->getPathName()); - } - - rmdir($path); - } - - public static function formatSize(int $byteCount): string - { - if ($byteCount === 0) { - return '0'; - } - $unitIndex = floor(log($byteCount, 1024)); - $units = ['', 'K', 'M', 'G', 'T', 'P']; - - return sprintf('%.2f %s', $byteCount / pow(1024, $unitIndex), $units[$unitIndex]); - } - - /** - * Returns the size of a path (recursively) and returns the size of the path in bytes. - */ - public static function sizeOfPath(string $path): int - { - $files = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::CHILD_FIRST - ); - - $size = 0; - foreach ($files as $file) { - $size += $file->getSize(); - } - - return $size; - } -} diff --git a/lib/Indexer/Util/PhpNameMatcher.php b/lib/Indexer/Util/PhpNameMatcher.php deleted file mode 100644 index 0692ae2b38..0000000000 --- a/lib/Indexer/Util/PhpNameMatcher.php +++ /dev/null @@ -1,17 +0,0 @@ -qualifiedName->__toString(); - } - - public static function fromArray(array $parts): self - { - return new self(QualifiedName::fromArray($parts)); - } - - public static function fromString(string $string): self - { - return new self(QualifiedName::fromString($string)); - } - - public static function fromQualifiedName(QualifiedName $qualfifiedName): self - { - return new self($qualfifiedName); - } - - /** - * Reutrn the last element of the name (e.g. the class's short name) - */ - public function head(): QualifiedName - { - return $this->qualifiedName->head(); - } - - /** - * Return the "namespace" portion of the name. - * - * @return FullyQualifiedName - */ - public function tail(): Name - { - return new self($this->qualifiedName->tail()); - } - - /** - * @return FullyQualifiedName - */ - public function prepend(Name $name): Name - { - return new self($this->qualifiedName->prepend($name)); - } - - /** - * @return FullyQualifiedName - */ - public function append(Name $name): Name - { - return new self($this->qualifiedName->append($name)); - } - - public function isDescendantOf(Name $name): bool - { - return $this->qualifiedName->isDescendantOf($name); - } - - public function toArray(): array - { - return $this->qualifiedName->toArray(); - } - - public function count(): int - { - return $this->qualifiedName->count(); - } -} diff --git a/lib/Name/Name.php b/lib/Name/Name.php deleted file mode 100644 index dedc0ad600..0000000000 --- a/lib/Name/Name.php +++ /dev/null @@ -1,24 +0,0 @@ - $segment) { - $seg = array_shift($search); - if ($segment === $seg) { - continue; - } - - return [$segment, $index === count($fqn) - 1]; - } - - return [null, false]; - } - - public static function join(string ...$segments): string - { - return implode('\\', array_map(fn (string $s) => self::normalize($s), $segments)); - } - - public static function toFullyQualified(string $name): string - { - if (str_starts_with($name, '\\')) { - return $name; - } - return '\\' . $name; - } - - public static function namespace(string $fqn): string - { - $shortNamePos = strrpos($fqn, '\\'); - if (false === $shortNamePos) { - return $fqn; - } - return substr($fqn, 0, $shortNamePos); - } - - private static function normalize(string $name): string - { - // trim? - return ltrim($name, '\\'); - } -} diff --git a/lib/Name/Names.php b/lib/Name/Names.php deleted file mode 100644 index f817d5cb27..0000000000 --- a/lib/Name/Names.php +++ /dev/null @@ -1,33 +0,0 @@ -names = $names; - } - - public static function fromNames(array $array) - { - return new self(...$array); - } - - - public function count(): int - { - return count($this->names); - } - - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->names); - } -} diff --git a/lib/Name/QualifiedName.php b/lib/Name/QualifiedName.php deleted file mode 100644 index e69ec887cb..0000000000 --- a/lib/Name/QualifiedName.php +++ /dev/null @@ -1,95 +0,0 @@ -parts = $parts; - } - - public function __toString(): string - { - return implode(self::NAMESPACE_SEPARATOR, $this->parts); - } - - public static function fromArray(array $parts): QualifiedName - { - return new self($parts); - } - - public static function fromString(string $string): self - { - return new self(array_filter(explode(self::NAMESPACE_SEPARATOR, $string))); - } - - public function toFullyQualifiedName(): FullyQualifiedName - { - return FullyQualifiedName::fromQualifiedName($this); - } - - public function head(): QualifiedName - { - $parts = $this->parts; - return new self([array_pop($parts)]); - } - - /** - * @return QualifiedName - */ - public function tail(): Name - { - $parts = $this->parts; - array_pop($parts); - return new self($parts); - } - - public function isDescendantOf(Name $name): bool - { - return array_slice($this->parts, 0, $name->count()) === $name->toArray(); - } - - /** - * @return string[] - */ - public function toArray(): array - { - return $this->parts; - } - - public function count(): int - { - return count($this->parts); - } - - /** - * @return QualifiedName - */ - public function prepend(Name $name): Name - { - $parts = $this->parts; - array_unshift($parts, ...$name->toArray()); - return new self($parts ?? []); - } - - /** - * @return QualifiedName - */ - public function append(Name $name): Name - { - $parts = $this->parts; - $parts = array_merge($parts, $name->toArray()); - return new self($parts); - } -} diff --git a/lib/Name/Tests/Unit/AbstractQualifiedNameTestCase.php b/lib/Name/Tests/Unit/AbstractQualifiedNameTestCase.php deleted file mode 100644 index 963f2b0029..0000000000 --- a/lib/Name/Tests/Unit/AbstractQualifiedNameTestCase.php +++ /dev/null @@ -1,150 +0,0 @@ -assertEquals($expected, $this->createFromArray($parts)); - } - - public static function provideCreateFromArray() - { - yield [ - ['Hello'], - 'Hello' - ]; - - yield [ - ['Hello', 'Goodbye'], - 'Hello\\Goodbye' - ]; - } - - #[DataProvider('provideCreateFromString')] - public function testCreateFromString(string $string, string $expected): void - { - $this->assertEquals($expected, $this->createFromString($string)); - } - - public static function provideCreateFromString() - { - yield [ - '\\Hello', - 'Hello' - ]; - - yield [ - 'Hello\\', - 'Hello' - ]; - - yield [ - 'Hello', - 'Hello' - ]; - - yield [ - 'Hello\\Goodbye', - 'Hello\\Goodbye' - ]; - } - - public function testThrowsExceptionIfNameIsEmpty(): void - { - $this->expectException(InvalidName::class); - QualifiedName::fromString(''); - } - - public function testHead(): void - { - $original = $this->createFromArray([ - 'Foobar', - 'Barfoo' - ]); - $this->assertEquals( - 'Barfoo', - $original->head()->__toString() - ); - ; - $this->assertEquals('Foobar\\Barfoo', $original->__toString()); - } - - public function testTail(): void - { - $original = $this->createFromArray([ - 'Foobar', - 'Barbar', - 'Barfoo' - ]); - $this->assertEquals( - 'Foobar\\Barbar', - $original->tail()->__toString() - ); - ; - $this->assertEquals('Foobar\\Barbar\\Barfoo', $original->__toString()); - } - - public function testIsDescendantOf(): void - { - $one = $this->createFromString('One\\Two'); - $this->assertTrue( - $this->createFromString('One\\Two\\Three')->isDescendantOf($one) - ); - $this->assertFalse( - $this->createFromString('One\\Four\\Three')->isDescendantOf($one) - ); - } - - public function testIsCountable(): void - { - $this->assertCount(3, $this->createFromArray(['1', '2', '3'])); - $this->assertCount(1, $this->createFromArray(['1'])); - } - - public function testPrepend(): void - { - $one = $this->createFromString('Three\\Four'); - $two = $this->createFromString('One\\Two'); - $this->assertEquals('One\\Two\\Three\\Four', $one->prepend($two)->__toString()); - } - - public function testAppend(): void - { - $one = $this->createFromString('Three\\Four'); - $two = $this->createFromString('One\\Two'); - $this->assertEquals('One\\Two\\Three\\Four', $two->append($one)->__toString()); - } - - public function testToArray(): void - { - $this->assertEquals( - ['One', 'Two'], - $this->createFromString('One\\Two')->toArray() - ); - } - - /** - * @return Name - */ - protected function createFromArray(array $parts) - { - return QualifiedName::fromArray($parts); - } - - /** - * @return Name - */ - protected function createFromString(string $string) - { - return QualifiedName::fromString($string); - } -} diff --git a/lib/Name/Tests/Unit/FullyQualifiedNameTest.php b/lib/Name/Tests/Unit/FullyQualifiedNameTest.php deleted file mode 100644 index 79b72dc731..0000000000 --- a/lib/Name/Tests/Unit/FullyQualifiedNameTest.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ - public static function provideRelativeTo(): Generator - { - yield [ - 'Foo', - 'Foo', - '', - ]; - yield [ - 'Foo', - 'Foo\Bar', - 'Bar', - ]; - yield [ - 'Foo\Bar', - 'Foo\Bar', - '', - ]; - yield [ - 'Foo\Bar\F', - 'Foo\Bar\Foobar', - 'Foobar', - ]; - yield [ - 'Foo', - 'Foo\Bar\Foobar', - 'Bar\Foobar', - ]; - } - - /** - * @param array{string,bool} $expected - */ - #[DataProvider('provideSegmentAtSearch')] - public function testSegmentAtSearch(string $fqn, string $search, array $expected): void - { - self::assertEquals($expected, NameUtil::childSegmentAtSearch($fqn, $search)); - } - /** - * @return Generator - */ - public static function provideSegmentAtSearch(): Generator - { - yield [ - 'Foo', - 'Foo', - [null, false], - ]; - - yield [ - 'Foo\Bar', - 'Foo', - ['Bar', true], - ]; - - yield [ - 'Foo\Bar', - 'Foo\Bar', - [null, false], - ]; - yield [ - 'Foo\Bar\Foobar', - 'Foo\Bar\F', - ['Foobar', true], - ]; - yield [ - 'Foo\Bar\Foobar\Bar\Baz', - 'Foo\Bar', - ['Foobar', false], - ]; - yield [ - 'Foo\Bar\Foobar\Bar\Baz', - 'Foo\Bar\\', - ['Foobar', false], - ]; - yield [ - 'Foo\Bar\Foobar\Bar\Baz', - 'Foo', - ['Bar', false], - ]; - yield [ - '\Foo\Bar\Foobar\Bar\Baz', - 'Foo', - ['Bar', false], - ]; - yield [ - 'Foo\Bar\Foobar\Bar\Baz', - '\Foo\Bar', - ['Foobar', false], - ]; - } -} diff --git a/lib/Name/Tests/Unit/NamesTest.php b/lib/Name/Tests/Unit/NamesTest.php deleted file mode 100644 index a23ca532b2..0000000000 --- a/lib/Name/Tests/Unit/NamesTest.php +++ /dev/null @@ -1,29 +0,0 @@ -assertEquals( - FullyQualifiedName::fromString('Foobar\\Barfoo'), - $this->createFromString('Foobar\\Barfoo')->toFullyQualifiedName() - ); - } - - protected function createFromArray(array $parts) - { - return QualifiedName::fromArray($parts); - } - - protected function createFromString(string $string): QualifiedName - { - return QualifiedName::fromString($string); - } -} diff --git a/lib/PathFinder/Exception/NoMatchingSourceException.php b/lib/PathFinder/Exception/NoMatchingSourceException.php deleted file mode 100644 index 4498697fb1..0000000000 --- a/lib/PathFinder/Exception/NoMatchingSourceException.php +++ /dev/null @@ -1,9 +0,0 @@ - $destinations - */ - private function __construct( - private string $basePath, - private array $destinations - ) { - } - - /** - * @param array $destinations - */ - public static function fromDestinations(array $destinations): PathFinder - { - return new self('', array_map(function (string $pattern) { - return Pattern::fromPattern($pattern); - }, $destinations)); - } - - /** - * @param array $destinations - */ - public static function fromAbsoluteDestinations(string $basePath, array $destinations): PathFinder - { - return new self($basePath, array_map(function (string $pattern) { - return Pattern::fromPattern($pattern); - }, $destinations)); - } - - /** - * Return a hash map of destination names to paths representing - * paths which relate to the given file path. - * - * @throws NoMatchingSourceException - * @return array - */ - public function destinationsFor(string $filePath): array - { - if ($this->basePath !== '') { - $filePath = Path::makeRelative($filePath, $this->basePath); - } - - $destinations = []; - $sourcePattern = $this->findSourcePattern($filePath); - - foreach ($this->destinations as $name => $pattern) { - assert($pattern instanceof Pattern); - if ($pattern === $sourcePattern) { - continue; - } - - $tokens = $sourcePattern->tokens($filePath); - $destinations[$name] = $pattern->replaceTokens($tokens); - } - - return $destinations; - } - - private function findSourcePattern(string $filePath): Pattern - { - foreach ($this->destinations as $name => $pattern) { - assert($pattern instanceof Pattern); - if ($pattern->fits($filePath)) { - return $pattern; - } - } - - throw new NoMatchingSourceException(sprintf( - 'Could not find matching source pattern for "%s", known patterns: "%s"', - $filePath, - implode('", "', array_map(function (Pattern $pattern) { - return $pattern->toString(); - }, $this->destinations)) - )); - } -} diff --git a/lib/PathFinder/Pattern.php b/lib/PathFinder/Pattern.php deleted file mode 100644 index 63dadc9cda..0000000000 --- a/lib/PathFinder/Pattern.php +++ /dev/null @@ -1,97 +0,0 @@ -}'; - - /** - * @param array $tokenNames - */ - public function __construct( - private string $regex, - private string $pattern, - private array $tokenNames - ) { - } - - public static function fromPattern(string $pattern): self - { - preg_match_all(self::TOKEN_REGEX, $pattern, $matches); - - [$tokens, $tokenNames] = $matches; - - $regex = $pattern; - foreach (array_values($matches[0]) as $index => $token) { - $greedy = $index + 1 !== count($tokenNames); - $regex = strtr($regex, [$token => sprintf('(?%s%s+)', $token, $greedy ? '[^/]' : '.')]); - } - - if (empty($tokenNames)) { - throw new NoPlaceHoldersException(sprintf( - 'File pattern "%s" does not contain any ', - $pattern - )); - } - - return new self(sprintf('{%s$}', $regex), $pattern, $tokenNames); - } - - public function fits(string $filePath): bool - { - return (bool)preg_match($this->regex, Path::canonicalize($filePath)); - } - - /** - * @return array - */ - public function tokens(string $filePath): array - { - $filePath = Path::canonicalize($filePath); - - if (!preg_match($this->regex, $filePath, $matches)) { - throw new RuntimeException(sprintf( - 'Error occurred performing regex on filepath "%s" with regex "%s"', - $filePath, - $this->regex - )); - } - - return array_intersect_key($matches, (array)array_combine($this->tokenNames, $this->tokenNames)); - } - - /** - * @param array $tokens - */ - public function replaceTokens(array $tokens): string - { - return $this->cleanRemainingTokens($this->replaceTokensWithValues($tokens)); - } - - public function toString(): string - { - return $this->pattern; - } - - /** - * @param array $tokens - */ - private function replaceTokensWithValues(array $tokens): string - { - return strtr($this->pattern, (array)array_combine(array_map(function (string $key) { - return '<' . $key . '>'; - }, array_keys($tokens)), array_values($tokens))); - } - - private function cleanRemainingTokens(string $filePath): string - { - return strtr($filePath, (array)array_combine(array_map(function (string $tokenName) { - return '<' . $tokenName . '>'; - }, $this->tokenNames), array_fill(0, count($this->tokenNames), ''))); - } -} diff --git a/lib/PathFinder/Tests/Unit/PathFinderTest.php b/lib/PathFinder/Tests/Unit/PathFinderTest.php deleted file mode 100644 index ffb46fd58d..0000000000 --- a/lib/PathFinder/Tests/Unit/PathFinderTest.php +++ /dev/null @@ -1,219 +0,0 @@ -destinationsFor($path); - - $this->assertEquals($expectedTargets, $targets); - } - - /** - * @return Generator,string,array}> - */ - public static function provideTeleport(): Generator - { - yield 'no available targets' => [ - [ - 'target1' => 'lib/.php', - ], - 'lib/MyFile.php', - [ - ], - ]; - - yield 'one available target' => [ - [ - 'target1' => 'lib/.php', - 'target2' => 'tests/Test.php', - ], - 'lib/MyFile.php', - [ - 'target2' => 'tests/MyFileTest.php', - ], - ]; - - yield 'multiple matching targets' => [ - [ - 'target1' => 'lib/.php', - 'target2' => 'tests/Test.php', - 'target3' => 'benchmarks/Bench.php', - ], - 'lib/MyFile.php', - [ - 'target2' => 'tests/MyFileTest.php', - 'target3' => 'benchmarks/MyFileBench.php', - ], - ]; - - yield 'composite path' => [ - [ - 'target1' => 'lib/.php', - 'target2' => 'tests/Test.php', - ], - 'lib/Foobar/Barfoo/MyFile.php', - [ - 'target2' => 'tests/Foobar/Barfoo/MyFileTest.php', - ], - ]; - - yield 'absolute path' => [ - [ - 'target1' => 'lib/.php', - 'target2' => 'tests/Test.php', - ], - '/home/daniel/lib/Foobar/Barfoo/MyFile.php', - [ - 'target2' => 'tests/Foobar/Barfoo/MyFileTest.php', - ], - ]; - - yield 'relative path' => [ - [ - 'target1' => 'lib/.php', - 'target2' => 'tests/Test.php', - ], - '/home/daniel/lib/Foobar/../Foobar/Barfoo/MyFile.php', - [ - 'target2' => 'tests/Foobar/Barfoo/MyFileTest.php', - ], - ]; - - yield 'from unit test' => [ - [ - 'target1' => 'lib/.php', - 'target2' => 'tests/Unit/Test.php', - ], - 'tests/Unit/MyFileTest.php', - [ - 'target1' => 'lib/MyFile.php', - ], - ]; - - yield 'multiple segments 1' => [ - [ - 'target1' => 'lib//.php', - 'target2' => 'tests//Unit/Test.php', - ], - 'tests/ModuleOne/Unit/MyFileTest.php', - [ - 'target1' => 'lib/ModuleOne/MyFile.php', - ], - ]; - - yield 'multiple segments 2' => [ - [ - 'target1' => 'lib//.php', - 'target2' => 'tests//Unit/Test.php', - ], - 'lib/ModuleOne/MyFile.php', - [ - 'target2' => 'tests/ModuleOne/Unit/MyFileTest.php', - ], - ]; - - yield 'multiple segments with containing multiple elements' => [ - [ - 'target1' => 'lib//.php', - 'target2' => 'tests//Unit/Test.php', - ], - 'lib/ModuleOne/Model/Abstractor/MyFile.php', - [ - 'target2' => 'tests/ModuleOne/Unit/Model/Abstractor/MyFileTest.php', - ], - ]; - - yield 'mixed multiple segments' => [ - [ - 'target1' => 'lib//.php', - 'target2' => 'tests//Unit/Test.php', - ], - 'lib/ModuleOne/Model/Abstractor/MyFile.php', - [ - 'target2' => 'tests/Model/Abstractor/MyFile/Unit/ModuleOneTest.php', - ], - ]; - - yield 'multiple with missing placeholders' => [ - [ - 'target1' => 'lib//.php', - 'target2' => 'tests/Unit/Test.php', - ], - 'lib/ModuleOne/Model/Abstractor/MyFile.php', - [ - 'target2' => 'tests/Unit/Model/Abstractor/MyFileTest.php', - ], - ]; - - yield 'multiple with non-correlating placeholders' => [ - [ - 'target1' => 'lib//.php', - 'target2' => 'tests/Unit/Test.php', - ], - 'lib/ModuleOne/Model/Abstractor/MyFile.php', - [ - 'target2' => 'tests/Unit/Test.php', - ], - ]; - - yield 'one available target with directories with identical name' => [ - [ - 'target1' => 'src/.php', - 'target2' => 'tests/Test.php', - ], - self::PROJECT_ROOT . '/src/MyFile.php', - [ - 'target2' => 'tests/MyFileTest.php', - ], - ]; - - yield 'jump back to one available target with directories with identical name' => [ - [ - 'target1' => 'src/.php', - 'target2' => 'tests/Test.php', - ], - self::PROJECT_ROOT . '/tests/MyFileTest.php', - [ - 'target1' => 'src/MyFile.php', - ], - ]; - } - - public function testNoMatchingTarget(): void - { - $this->expectException(NoMatchingSourceException::class); - $this->expectExceptionMessage('Could not find matching source pattern for "/lib/Foo.php", known patterns: "/soos//boos.php"'); - - $teleport = PathFinder::fromDestinations([ - 'soos' => '/soos//boos.php', - ]); - - $teleport->destinationsFor('/lib/Foo.php'); - } - - public function testDestinationWithNoKernel(): void - { - $this->expectException(NoPlaceHoldersException::class); - $this->expectExceptionMessage('File pattern "/soos/boos.php" does not contain any '); - - $teleport = PathFinder::fromDestinations([ - 'soos' => '/soos/boos.php', - ]); - - $teleport->destinationsFor('/lib/Foo.php'); - } -} diff --git a/lib/Phpactor.php b/lib/Phpactor.php deleted file mode 100644 index b419a555d4..0000000000 --- a/lib/Phpactor.php +++ /dev/null @@ -1,465 +0,0 @@ -getErrorOutput() : new NullOutput(); - $config = []; - - $projectRoot = getcwd(); - - if ($input->hasParameterOption([ '--working-dir', '-d' ])) { - $projectRoot = $input->getParameterOption([ '--working-dir', '-d' ]); - } - - if (!is_string($projectRoot)) { - throw new RuntimeException(sprintf( - 'Unexpected type for project root, expected string got: %s', - get_debug_type($projectRoot) - )); - } - - $commandName = $input->getFirstArgument(); - - $trustPath = (new Xdg())->getHomeDataDir() . '/phpactor/trust.json'; - $trust = Trust::load($trustPath); - - $loader = ConfigLoaderBuilder::create() - ->enableJsonDeserializer('json') - ->enableYamlDeserializer('yaml') - ->addXdgCandidate('phpactor', 'phpactor.json', 'json') - ->addXdgCandidate('phpactor', 'phpactor.yml', 'yaml'); - - $projectCandidates = [ - [$projectRoot . '/.phpactor.json', 'json'], - [$projectRoot . '/.phpactor.yml', 'yaml'], - ]; - - $trusted = $trust->isTrusted($projectRoot); - if ($trusted === true) { - foreach ($projectCandidates as [$path, $type]) { - $loader = $loader->addCandidate($path, $type); - } - } - - $loader = $loader->loader(); - $config = $loader->load(); - $config[CoreExtension::PARAM_COMMAND] = $input->getFirstArgument(); - $config[CoreExtension::PARAM_PROJECT_CONFIG_CANDIDATES] = array_column($projectCandidates, 0); - - $config[CoreExtension::PARAM_TRUST] = $trust; - $config[CoreExtension::PARAM_TRUSTED] = $trusted; - - if ($phpactorBin) { - $config[LanguageServerExtension::PARAM_PHPACTOR_BIN] = $phpactorBin; - } - $config[FilePathResolverExtension::PARAM_APPLICATION_ROOT] = self::resolveApplicationRoot(); - $config = array_merge([ IndexerExtension::PARAM_STUB_PATHS => [] ], $config); - $config[IndexerExtension::PARAM_STUB_PATHS][] = self::resolveApplicationRoot() . '/vendor/jetbrains/phpstorm-stubs'; - $config = self::configureLanguageServer($config); - - if ($input->hasParameterOption([ '--working-dir', '-d' ])) { - $config[FilePathResolverExtension::PARAM_PROJECT_ROOT] = $projectRoot; - } - - if ($input->hasParameterOption('--config-extra')) { - $rawJson = $input->getParameterOption('--config-extra'); - if (!is_string($rawJson)) { - throw new RuntimeException(sprintf( - 'Expected string for config-extra, got: %s', - gettype($rawJson) - )); - } - $extraConfig = json_decode($rawJson, true); - if (!is_array($extraConfig)) { - throw new RuntimeException(sprintf( - 'Invalid JSON passed as config-extra: %s', - $rawJson - )); - } - $config = array_merge($config, $extraConfig); - } - - if (!isset($config[CoreExtension::PARAM_XDEBUG_DISABLE]) || $config[CoreExtension::PARAM_XDEBUG_DISABLE]) { - $xdebug = new XdebugHandler('PHPACTOR'); - $xdebug->check(); - unset($xdebug); - } - - $trusted = $trust->isTrusted($projectRoot); - if (!$trusted) { - foreach ($projectCandidates as [$candidate, $_]) { - if (file_exists($candidate)) { - if ($commandName !== 'rpc') { - $errorOutput->writeln(sprintf( - 'Local config "%s" found but it\'s in an untrusted directory, ' . - 'run `phpactor config:trust` if you want it to be loaded', - basename($candidate), - )); - } - } - } - } - - - /** @var list $extensionNames */ - $extensionNames = [ - CoreExtension::class, - ClassToFileExtraExtension::class, - ClassToFileExtension::class, - ClassMoverExtension::class, - MainClassMoverExtension::class, - CodeTransformExtension::class, - CodeTransformExtraExtension::class, - CompletionExtraExtension::class, - CompletionWorseExtension::class, - CompletionExtension::class, - CompletionRpcExtension::class, - NavigationExtension::class, - ContextMenuExtension::class, - RpcExtension::class, - SourceCodeFilesystemExtraExtension::class, - SourceCodeFilesystemExtension::class, - WorseReflectionExtension::class, - WorseReflectionExtraExtension::class, - WorseReflectionAnalyseExtension::class, - FilePathResolverExtension::class, - LoggingExtension::class, - ComposerAutoloaderExtension::class, - ConsoleExtension::class, - WorseReferenceFinderExtension::class, - ReferenceFinderRpcExtension::class, - ReferenceFinderExtension::class, - PhpExtension::class, - ConfigurationExtension::class, - ComposerInspectorExtension::class, - LanguageServerExtension::class, - LanguageServerCompletionExtension::class, - LanguageServerReferenceFinderExtension::class, - LanguageServerWorseReflectionExtension::class, - LanguageServerIndexerExtension::class, - LanguageServerHoverExtension::class, - LanguageServerEvaluatableExpressionExtension::class, - LanguageServerInlineValueExtension::class, - LanguageServerBridgeExtension::class, - LanguageServerCodeTransformExtension::class, - LanguageServerSymbolProviderExtension::class, - LanguageServerSelectionRangeExtension::class, - LanguageServerDiagnosticsExtension::class, - LanguageServerRenameExtension::class, - LanguageServerRenameWorseExtension::class, - LanguageServerConfigurationExtension::class, - IndexerExtension::class, - ObjectRendererExtension::class, - - LanguageServerPhpstanExtension::class, - LanguageServerPhpstanSuggestExtension::class, - LanguageServerPsalmExtension::class, - LanguageServerPsalmSuggestExtension::class, - LanguageServerMagoExtension::class, - LanguageServerMagoSuggestExtension::class, - LanguageServerPhpCsFixerExtension::class, - LanguageServerPhpCsFixerSuggestExtension::class, - LanguageServerHighlightExtension::class, - PhpCodeSnifferExtension::class, - PhpCodeSnifferSuggestExtension::class, - - LanguageServerBlackfireExtension::class, - - ProphecyExtension::class, - OpenTelemetryExtension::class, - ProphecySuggestExtension::class, - - BehatExtension::class, - BehatSuggestExtension::class, - - SymfonyExtension::class, - SymfonySuggestExtension::class, - PHPUnitExtension::class, - ]; - - if (class_exists(DebugExtension::class)) { - $extensionNames[] = DebugExtension::class; - } - - $container = new PhpactorContainer(); - - $container->register('config_loader.candidates', function () use ($loader) { - return $loader->candidates(); - }); - - $masterSchema = new Resolver(true); - $extensions = []; - foreach ($extensionNames as $extensionClass) { - $schema = new Resolver(); - - if (!class_exists($extensionClass)) { - $errorOutput->writeln(sprintf('Extension "%s" does not exist', $extensionClass). "\n"); - continue; - } - - $extension = new $extensionClass(); - if (!$extension instanceof Extension) { - throw new RuntimeException(sprintf( - 'Phpactor extension "%s" must implement interface "%s"', - get_class($extension), - Extension::class - )); - } - - // This is duplicated in ExtensionDocumentor we should not - // continue to add behavior like this here and should extract - // this and other special logic. - if ($extension instanceof OptionalExtension) { - (function (string $key) use ($schema): void { - $schema->setDefaults([$key => false]); - $schema->setTypes([$key => 'boolean']); - })(sprintf('%s.enabled', $extension->name())); - } - - $extension->configure($schema); - $extensions[] = $extension; - $masterSchema = $masterSchema->merge($schema); - } - $masterSchema->setDefaults([ - PhpactorContainer::PARAM_EXTENSION_CLASSES => $extensionNames, - - // enable the LSP watchern - IndexerExtension::PARAM_ENABLED_WATCHERS => ['lsp', 'inotify', 'find', 'php'] - ]); - $config = $masterSchema->resolve($config); - - // > method configure container - foreach ($extensions as $extension) { - if ($extension instanceof OptionalExtension) { - if (false === ($config[sprintf('%s.enabled', $extension->name())] ?? false)) { - continue; - } - } - $extension->load($container); - if ($extension instanceof BootableExtension) { - $extension->boot($container); - } - } - - if (isset($config[CoreExtension::PARAM_MIN_MEMORY_LIMIT])) { - self::updateMinMemory($config[CoreExtension::PARAM_MIN_MEMORY_LIMIT]); - } - - foreach ($masterSchema->errors()->errors() as $error) { - // do not polute STDERR for RPC, for some reason the VIM plugin reads also - // STDERR and possibly other RPC clients too - if ($commandName !== 'rpc') { - if ($output instanceof ConsoleOutputInterface) { - $output->getErrorOutput()->writeln(sprintf('%s...', substr((string)$error, 0, 100))); - } - } - } - - $container->register(ResolverErrors::class, fn () => $masterSchema->errors()); - - return $container->build($config); - } - - /** - * If the path is relative we need to use the current working path - * because otherwise it will be the script path, which is wrong in the - * context of a PHAR. - */ - public static function normalizePath(string $path): string - { - return Path::makeAbsolute($path, (string)getcwd()); - } - - public static function relativizePath(string $path): string - { - if (Path::isBasePath((string)getcwd(), $path)) { - return Path::makeRelative($path, (string)getcwd()); - } - - return $path; - } - - public static function isFile(string $string): bool - { - $containsInvalidNamespaceChars = (bool) preg_match('{[\.\*/]}', $string); - - if ($containsInvalidNamespaceChars) { - return true; - } - - return file_exists($string); - } - - public static function version(): string - { - return Cast::toString(InstalledVersions::getVersion('phpactor/phpactor')); - } - - /** - * Optimize Phpactor for the language server (these settings will apply - * only to LanguageServer sessions). - * - * @param array $config - * @return array - */ - private static function configureLanguageServer(array $config): array - { - $config[LanguageServerExtension::PARAM_SESSION_PARAMETERS] = [ - LanguageServerExtension::PARAM_METHOD_ALIAS_MAP => [ - 'indexer/reindex' => 'phpactor/indexer/reindex', - 'session/dumpConfig' => 'phpactor/debug/config', - 'service/running' => 'phpactor/service/running', - 'system/status' => 'phpactor/stats', - ], - WorseReflectionExtension::PARAM_ENABLE_CONTEXT_LOCATION => false, - ClassToFileExtension::PARAM_BRUTE_FORCE_CONVERSION => false, - - // these completors are not appropriate for the language server SCF - // is a brute force, blocking completor. the declared completors - // use the functions declared in the Phpactor runtime and not the - // project. - 'completion_worse.completor.scf_class.enabled' => false, - 'completion_worse.completor.declared_class.enabled' => false, - 'completion_worse.completor.declared_constant.enabled' => false, - 'completion_worse.completor.declared_function.enabled' => false, - ]; - - return $config; - } - - private static function resolveApplicationRoot(): string - { - $paths = [ __DIR__ . '/..', __DIR__ .'/../../../..' ]; - - foreach ($paths as $path) { - if (is_dir($path.'/vendor')) { - return Path::canonicalize($path); - } - } - - throw new RuntimeException(sprintf('Could not resolve application root, tried "%s"', implode('", "', $paths))); - } - - /** - * Update the PHP memory limit according to the configured minimum - * (borrowed from Composer) - */ - private static function updateMinMemory(int $minimumMemoryLimit): void - { - $memoryInBytes = function ($value) { - $unit = strtolower(substr($value, -1, 1)); - $value = (int) $value; - switch ($unit) { - case 'g': - $value *= 1024; - // no break (cumulative multiplier) - case 'm': - $value *= 1024; - // no break (cumulative multiplier) - case 'k': - $value *= 1024; - } - - return $value; - }; - - $memoryLimit = trim((string)ini_get('memory_limit')); - if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < $minimumMemoryLimit) { - @ini_set('memory_limit', (string)$minimumMemoryLimit); - } - } -} diff --git a/lib/ReferenceFinder/ChainDefinitionLocationProvider.php b/lib/ReferenceFinder/ChainDefinitionLocationProvider.php deleted file mode 100644 index 22bb2c87db..0000000000 --- a/lib/ReferenceFinder/ChainDefinitionLocationProvider.php +++ /dev/null @@ -1,58 +0,0 @@ -add($provider); - } - } - - public function locateDefinition(TextDocument $document, ByteOffset $byteOffset): TypeLocations - { - $messages = []; - foreach ($this->providers as $provider) { - try { - return $provider->locateDefinition($document, $byteOffset); - } catch (UnsupportedDocument $unsupported) { - $this->logger->debug(sprintf( - 'Document is unsupported by "%s": %s', - get_class($provider), - $unsupported->getMessage() - )); - $messages[] = $unsupported->getMessage(); - } catch (CouldNotLocateDefinition $exception) { - $this->logger->info(sprintf('Could not locate definition ""%s"', $exception->getMessage())); - $messages[] = $exception->getMessage(); - } - } - - if ($messages) { - throw new CouldNotLocateDefinition(implode(', ', $messages)); - } - - throw new CouldNotLocateDefinition('No definition locators are registered'); - } - - private function add(DefinitionLocator $provider): void - { - $this->providers[] = $provider; - } -} diff --git a/lib/ReferenceFinder/ChainImplementationFinder.php b/lib/ReferenceFinder/ChainImplementationFinder.php deleted file mode 100644 index 85b8ab573f..0000000000 --- a/lib/ReferenceFinder/ChainImplementationFinder.php +++ /dev/null @@ -1,50 +0,0 @@ -add($finder); - } - } - - public function findImplementations(TextDocument $document, ByteOffset $byteOffset, bool $includeDefinition = false): Locations - { - $messages = []; - $locations = []; - foreach ($this->finders as $finder) { - $locations = array_merge( - $locations, - iterator_to_array( - $finder->findImplementations( - $document, - $byteOffset, - $includeDefinition - ) - ) - ); - } - - return new Locations($locations); - } - - private function add(ClassImplementationFinder $finder): void - { - $this->finders[] = $finder; - } -} diff --git a/lib/ReferenceFinder/ChainReferenceFinder.php b/lib/ReferenceFinder/ChainReferenceFinder.php deleted file mode 100644 index 63a50c1d35..0000000000 --- a/lib/ReferenceFinder/ChainReferenceFinder.php +++ /dev/null @@ -1,42 +0,0 @@ -add($finder); - } - } - - public function findReferences(TextDocument $document, ByteOffset $byteOffset): Generator - { - foreach ($this->finders as $finder) { - $generator = $finder->findReferences($document, $byteOffset); - yield from $generator; - - // stop no more generators should be executed - if ($generator->getReturn() === true) { - return true; - } - } - - return false; - } - - private function add(ReferenceFinder $finder): void - { - $this->finders[] = $finder; - } -} diff --git a/lib/ReferenceFinder/ChainTypeLocator.php b/lib/ReferenceFinder/ChainTypeLocator.php deleted file mode 100644 index ff010b8c61..0000000000 --- a/lib/ReferenceFinder/ChainTypeLocator.php +++ /dev/null @@ -1,65 +0,0 @@ -add($locator); - } - } - - public function locateTypes(TextDocument $document, ByteOffset $byteOffset): TypeLocations - { - $messages = []; - foreach ($this->locators as $locator) { - try { - $typeLocations = $locator->locateTypes($document, $byteOffset); - } catch (UnsupportedDocument $unsupported) { - $this->logger->debug(sprintf( - 'Document is unsupported by "%s": %s', - get_class($locator), - $unsupported->getMessage() - )); - $messages[] = $unsupported->getMessage(); - continue; - } - - if (!$typeLocations->count()) { - continue; - } - - return $typeLocations; - } - - if ($messages) { - throw new CouldNotLocateType(implode(', ', $messages)); - } - - throw new CouldNotLocateType('No type locators are registered'); - } - - private function add(TypeLocator $locator): void - { - $this->locators[] = $locator; - } -} diff --git a/lib/ReferenceFinder/ClassImplementationFinder.php b/lib/ReferenceFinder/ClassImplementationFinder.php deleted file mode 100644 index 78a0c39eaa..0000000000 --- a/lib/ReferenceFinder/ClassImplementationFinder.php +++ /dev/null @@ -1,21 +0,0 @@ -locator->locateDefinition($document, $byteOffset); - yield PotentialLocation::surely($location->first()->location()); - } catch (CouldNotLocateDefinition) { - } - - $generator = $this->referenceFinder->findReferences($document, $byteOffset); - foreach ($generator as $reference) { - yield $reference; - } - return $generator->getReturn(); - } -} diff --git a/lib/ReferenceFinder/DefinitionLocator.php b/lib/ReferenceFinder/DefinitionLocator.php deleted file mode 100644 index 63b29e9089..0000000000 --- a/lib/ReferenceFinder/DefinitionLocator.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @param NameSearcherType::* $type - */ - public function search(string $search, ?string $type = null): Generator; -} diff --git a/lib/ReferenceFinder/NameSearcherType.php b/lib/ReferenceFinder/NameSearcherType.php deleted file mode 100644 index 7437a362ad..0000000000 --- a/lib/ReferenceFinder/NameSearcherType.php +++ /dev/null @@ -1,21 +0,0 @@ -confidence === self::CONFIDENCE_SURELY; - } - - public function isMaybe(): bool - { - return $this->confidence === self::CONFIDENCE_MAYBE; - } - - public function isNot(): bool - { - return $this->confidence === self::CONFIDENCE_NOT; - } - - public function location(): Location - { - return $this->location; - } -} diff --git a/lib/ReferenceFinder/ReferenceFinder.php b/lib/ReferenceFinder/ReferenceFinder.php deleted file mode 100644 index 37d694f436..0000000000 --- a/lib/ReferenceFinder/ReferenceFinder.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ - public function findReferences(TextDocument $document, ByteOffset $byteOffset): Generator; -} diff --git a/lib/ReferenceFinder/Search/NameSearchResult.php b/lib/ReferenceFinder/Search/NameSearchResult.php deleted file mode 100644 index 9fabd28530..0000000000 --- a/lib/ReferenceFinder/Search/NameSearchResult.php +++ /dev/null @@ -1,44 +0,0 @@ -name; - } - - public function type(): NameSearchResultType - { - return $this->type; - } - - public function uri(): ?TextDocumentUri - { - return $this->uri; - } -} diff --git a/lib/ReferenceFinder/Search/NameSearchResultType.php b/lib/ReferenceFinder/Search/NameSearchResultType.php deleted file mode 100644 index 4983859522..0000000000 --- a/lib/ReferenceFinder/Search/NameSearchResultType.php +++ /dev/null @@ -1,47 +0,0 @@ -type = $type; - } - - public function __toString(): string - { - return $this->type; - } - - public function isClass(): bool - { - return $this->type === self::TYPE_CLASS; - } - - public function isFunction(): bool - { - return $this->type === self::TYPE_FUNCTION; - } - - public function isConstant(): bool - { - return $this->type === self::TYPE_CONSTANT; - } -} diff --git a/lib/ReferenceFinder/Search/NullNameSearcher.php b/lib/ReferenceFinder/Search/NullNameSearcher.php deleted file mode 100644 index 261a2c8fca..0000000000 --- a/lib/ReferenceFinder/Search/NullNameSearcher.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ - public function search(string $search, ?string $type = null): Generator - { - $fullyQualified = str_starts_with($search, '\\'); - foreach ($this->results as $result) { - - if ($fullyQualified && str_starts_with('\\'. $result->name()->__toString(), $search)) { - yield $result; - continue; - } - if (str_starts_with($result->name()->head()->__toString(), $search)) { - yield $result; - continue; - } - } - } -} diff --git a/lib/ReferenceFinder/TestDefinitionLocator.php b/lib/ReferenceFinder/TestDefinitionLocator.php deleted file mode 100644 index f60614ed5e..0000000000 --- a/lib/ReferenceFinder/TestDefinitionLocator.php +++ /dev/null @@ -1,35 +0,0 @@ -location) { - throw new CouldNotLocateDefinition( - 'Definition not found' - ); - } - - return $this->location; - } -} diff --git a/lib/ReferenceFinder/TestReferenceFinder.php b/lib/ReferenceFinder/TestReferenceFinder.php deleted file mode 100644 index 8dd77904f1..0000000000 --- a/lib/ReferenceFinder/TestReferenceFinder.php +++ /dev/null @@ -1,29 +0,0 @@ -locations = $locations; - } - - - public function findReferences(TextDocument $document, ByteOffset $byteOffset): Generator - { - foreach ($this->locations as $location) { - yield $location; - } - return true; - } -} diff --git a/lib/ReferenceFinder/TestTypeLocator.php b/lib/ReferenceFinder/TestTypeLocator.php deleted file mode 100644 index 63fa41c8c9..0000000000 --- a/lib/ReferenceFinder/TestTypeLocator.php +++ /dev/null @@ -1,18 +0,0 @@ -locations; - } -} diff --git a/lib/ReferenceFinder/Tests/Unit/ChainDefinitionLocationProviderTest.php b/lib/ReferenceFinder/Tests/Unit/ChainDefinitionLocationProviderTest.php deleted file mode 100644 index d736202246..0000000000 --- a/lib/ReferenceFinder/Tests/Unit/ChainDefinitionLocationProviderTest.php +++ /dev/null @@ -1,96 +0,0 @@ - - */ - private ObjectProphecy $locator1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $locator2; - - private TextDocument $document; - - private ByteOffset $offset; - - public function setUp(): void - { - $this->locator1 = $this->prophesize(DefinitionLocator::class); - $this->locator2 = $this->prophesize(DefinitionLocator::class); - - $this->document = TextDocumentBuilder::create('build(); - $this->offset = ByteOffset::fromInt(1234); - } - - public function testProvidesAggregatedLocations(): void - { - $locator = new ChainDefinitionLocationProvider([ - $this->locator1->reveal(), - $this->locator2->reveal() - ]); - - $location1 = $this->createLocation(); - $this->locator1->locateDefinition($this->document, $this->offset)->willReturn($location1); - - $location2 = $this->createLocation(); - $this->locator2->locateDefinition($this->document, $this->offset)->willReturn($location2); - - $location = $locator->locateDefinition($this->document, $this->offset); - $this->assertSame($location, $location1); - } - - public function testExceptionWhenDefinitionNotFound(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $this->expectExceptionMessage('No'); - - $locator = new ChainDefinitionLocationProvider([ - $this->locator1->reveal() - ]); - - $this->locator1->locateDefinition($this->document, $this->offset)->willThrow(new CouldNotLocateDefinition('No')); - $locator->locateDefinition($this->document, $this->offset); - } - - public function testExceptionWhenDefinitionNotSupported(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $this->expectExceptionMessage('Not supported'); - - $locator = new ChainDefinitionLocationProvider([ - $this->locator1->reveal() - ]); - - $this->locator1->locateDefinition($this->document, $this->offset)->willThrow(new UnsupportedDocument('Not supported')); - $locator->locateDefinition($this->document, $this->offset); - } - - private function createLocation(): TypeLocations - { - return new TypeLocations([ - new TypeLocation(TypeFactory::unknown(), Location::fromPathAndOffsets('/path/to.php', 1234, 1234)) - ]); - } -} diff --git a/lib/ReferenceFinder/Tests/Unit/ChainImplementationFinderTest.php b/lib/ReferenceFinder/Tests/Unit/ChainImplementationFinderTest.php deleted file mode 100644 index db77341440..0000000000 --- a/lib/ReferenceFinder/Tests/Unit/ChainImplementationFinderTest.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ - private ObjectProphecy $locator1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $locator2; - - private TextDocument $document; - - private ByteOffset $offset; - - public function setUp(): void - { - $this->locator1 = $this->prophesize(ClassImplementationFinder::class); - $this->locator2 = $this->prophesize(ClassImplementationFinder::class); - - $this->document = TextDocumentBuilder::create('build(); - $this->offset = ByteOffset::fromInt(1234); - } - - public function testProvidesAggregateLocations(): void - { - $locator = new ChainImplementationFinder([ - $this->locator1->reveal(), - $this->locator2->reveal() - ]); - - $location1 = $this->createLocation(); - $this->locator1->findImplementations($this->document, $this->offset, false)->willReturn(new Locations([$location1])); - - $location2 = $this->createLocation(); - $this->locator2->findImplementations($this->document, $this->offset, false)->willReturn(new Locations([$location2])); - - $locationRanges = $locator->findImplementations($this->document, $this->offset); - $this->assertEquals(new Locations([$location1, $location2]), $locationRanges); - } - - private function createLocation(): Location - { - return Location::fromPathAndOffsets('/path/to.php', 1234, 5234); - } -} diff --git a/lib/ReferenceFinder/Tests/Unit/ChainReferenceFinderTest.php b/lib/ReferenceFinder/Tests/Unit/ChainReferenceFinderTest.php deleted file mode 100644 index bbc09caf14..0000000000 --- a/lib/ReferenceFinder/Tests/Unit/ChainReferenceFinderTest.php +++ /dev/null @@ -1,69 +0,0 @@ - - */ - private ObjectProphecy $locator1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $locator2; - - private TextDocument $document; - - private ByteOffset $offset; - - public function setUp(): void - { - $this->locator1 = $this->prophesize(ReferenceFinder::class); - $this->locator2 = $this->prophesize(ReferenceFinder::class); - - $this->document = TextDocumentBuilder::create('build(); - $this->offset = ByteOffset::fromInt(1234); - } - - public function testProvidesAggregateLocations(): void - { - $locator = new ChainReferenceFinder([ - $this->locator1->reveal(), - $this->locator2->reveal() - ]); - - $location1 = $this->createLocation(); - $this->locator1->findReferences($this->document, $this->offset)->willYield([$location1]); - - $location2 = $this->createLocation(); - $this->locator2->findReferences($this->document, $this->offset)->willYield([$location2]); - - $locations = []; - foreach ($locator->findReferences($this->document, $this->offset) as $location) { - $locations[] = $location; - } - - $this->assertEquals([$location1, $location2], $locations); - } - - private function createLocation(): Location - { - return Location::fromPathAndOffsets('/path/to.php', 1234, 4578); - } -} diff --git a/lib/ReferenceFinder/Tests/Unit/ChainTypeLocatorTest.php b/lib/ReferenceFinder/Tests/Unit/ChainTypeLocatorTest.php deleted file mode 100644 index b66d393b12..0000000000 --- a/lib/ReferenceFinder/Tests/Unit/ChainTypeLocatorTest.php +++ /dev/null @@ -1,106 +0,0 @@ - - */ - private ObjectProphecy $locator1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $locator2; - - private TextDocument $document; - - private ByteOffset $offset; - - public function setUp(): void - { - $this->locator1 = $this->prophesize(TypeLocator::class); - $this->locator2 = $this->prophesize(TypeLocator::class); - - $this->document = TextDocumentBuilder::create('build(); - $this->offset = ByteOffset::fromInt(1234); - } - - public function testProvidesAggregatedLocations(): void - { - $locator = new ChainTypeLocator([ - $this->locator1->reveal(), - $this->locator2->reveal() - ]); - - $location1 = $this->createLocation(); - $this->locator1->locateTypes($this->document, $this->offset)->willReturn($this->createLocations($location1)); - - $location2 = $this->createLocation(); - $this->locator2->locateTypes($this->document, $this->offset)->willReturn($this->createLocations($location2)); - - $location = $locator->locateTypes($this->document, $this->offset); - $this->assertSame($location->first()->location(), $location1); - } - - public function testExceptionWhenTypeNotFound(): void - { - $this->expectException(CouldNotLocateType::class); - $this->expectExceptionMessage('No'); - - $locator = new ChainTypeLocator([ - $this->locator1->reveal() - ]); - - $this->locator1->locateTypes($this->document, $this->offset)->willThrow(new CouldNotLocateType('No')); - $locator->locateTypes($this->document, $this->offset); - } - - public function testExceptionWhenTypeNotSupported(): void - { - $this->expectException(CouldNotLocateType::class); - $this->expectExceptionMessage('Not supported'); - - $locator = new ChainTypeLocator([ - $this->locator1->reveal() - ]); - - $this->locator1->locateTypes($this->document, $this->offset)->willThrow(new UnsupportedDocument('Not supported')); - $locator->locateTypes($this->document, $this->offset); - } - - private function createLocation(): Location - { - return new Location( - TextDocumentUri::fromString('/path/to.php'), - ByteOffsetRange::fromByteOffsets(ByteOffset::fromInt(1234), ByteOffset::fromInt(1534)) - ); - } - - private function createLocations(Location $location1): TypeLocations - { - return new TypeLocations([ - new TypeLocation(new MixedType(), $location1) - ]); - } -} diff --git a/lib/ReferenceFinder/Tests/Unit/DefinitionAndReferenceFinderTest.php b/lib/ReferenceFinder/Tests/Unit/DefinitionAndReferenceFinderTest.php deleted file mode 100644 index ba805571fe..0000000000 --- a/lib/ReferenceFinder/Tests/Unit/DefinitionAndReferenceFinderTest.php +++ /dev/null @@ -1,40 +0,0 @@ -build(); - self::assertCount(2, iterator_to_array($finder->findReferences($document, ByteOffset::fromInt(1)))); - } - - public function testReturnsReferenceIfDefinitionNotFound(): void - { - $finder = new DefinitionAndReferenceFinder( - new TestDefinitionLocator(null), - new TestReferenceFinder(PotentialLocation::surely(Location::fromPathAndOffsets('/path', 2, 4))) - ); - $document = TextDocumentBuilder::create('asd')->build(); - self::assertCount(1, iterator_to_array($finder->findReferences($document, ByteOffset::fromInt(1)))); - } -} diff --git a/lib/ReferenceFinder/Tests/Unit/Search/NameSearchResultTest.php b/lib/ReferenceFinder/Tests/Unit/Search/NameSearchResultTest.php deleted file mode 100644 index a75e7a048f..0000000000 --- a/lib/ReferenceFinder/Tests/Unit/Search/NameSearchResultTest.php +++ /dev/null @@ -1,32 +0,0 @@ -expectException(RuntimeException::class); - $this->expectExceptionMessage('is invalid'); - NameSearchResult::create('foobar', 'Foobar'); - } - - public function testCreateClassResult(): void - { - $result = NameSearchResult::create(NameSearchResultType::TYPE_CLASS, 'Foobar'); - self::assertInstanceOf(NameSearchResult::class, $result); - self::assertTrue($result->type()->isClass()); - } - - public function testCreateFunctionResult(): void - { - $result = NameSearchResult::create(NameSearchResultType::TYPE_FUNCTION, 'Foobar'); - self::assertInstanceOf(NameSearchResult::class, $result); - self::assertTrue($result->type()->isFunction()); - } -} diff --git a/lib/ReferenceFinder/TypeLocation.php b/lib/ReferenceFinder/TypeLocation.php deleted file mode 100644 index 76c9942106..0000000000 --- a/lib/ReferenceFinder/TypeLocation.php +++ /dev/null @@ -1,25 +0,0 @@ -type; - } - - public function location(): Location - { - return $this->location; - } -} diff --git a/lib/ReferenceFinder/TypeLocations.php b/lib/ReferenceFinder/TypeLocations.php deleted file mode 100644 index fbfe3b62e6..0000000000 --- a/lib/ReferenceFinder/TypeLocations.php +++ /dev/null @@ -1,72 +0,0 @@ - - */ -class TypeLocations implements IteratorAggregate -{ - /** - * @param TypeLocation[] $typeLocations - */ - public function __construct(private array $typeLocations) - { - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->typeLocations); - } - - public function first(): TypeLocation - { - if (!$this->typeLocations) { - throw new CouldNotLocateType( - 'There are no type locations, cannot get the first' - ); - } - - return reset($this->typeLocations); - } - - public function atIndex(int $index): TypeLocation - { - if (!isset($this->typeLocations[$index])) { - throw new CouldNotLocateType(sprintf( - 'There are no type locations at index "%s"', - $index - )); - } - - return $this->typeLocations[$index]; - } - - public function count(): int - { - return count($this->typeLocations); - } - - public function byTypeName(string $typeName): TypeLocation - { - foreach ($this->typeLocations as $typeLocation) { - if ($typeLocation->type()->__toString() === $typeName) { - return $typeLocation; - } - } - throw new CouldNotLocateType(sprintf( - 'Unknown type name "%s"', - $typeName - )); - } - - public static function forLocation(TypeLocation $location): self - { - return new self([$location]); - } -} diff --git a/lib/ReferenceFinder/TypeLocator.php b/lib/ReferenceFinder/TypeLocator.php deleted file mode 100644 index e5ee4f911b..0000000000 --- a/lib/ReferenceFinder/TypeLocator.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function renameFile(TextDocumentUri $from, TextDocumentUri $to): Promise - { - return call(function () use ($from, $to) { - try { - $fromClass = $this->converter->convert($from); - $toClass = $this->converter->convert($to); - } catch (CouldNotConvertUriToClass $error) { - throw new CouldNotRename($error->getMessage(), 0, $error); - } - - $references = $this->client->class()->referencesTo($fromClass); - - // rename class definition - $locatedEdits = $this->replaceDefinition($to, $fromClass, $toClass); - - $edits = TextEdits::none(); - $seen = []; - foreach ($references as $reference) { - if (isset($seen[$reference->location()->uri()->__toString()])) { - continue; - } - - $seen[$reference->location()->uri()->__toString()] = true; - - try { - $document = $this->locator->get($reference->location()->uri()); - } catch (TextDocumentNotFound) { - continue; - } - - foreach ($this->mover->replaceReferences( - $this->mover->findReferences($document->__toString(), $fromClass), - $toClass - ) as $edit) { - $locatedEdits[] = new LocatedTextEdit($reference->location()->uri(), $edit); - } - } - - return LocatedTextEditsMap::fromLocatedEdits($locatedEdits); - }); - } - - /** - * @return LocatedTextEdit[] - */ - private function replaceDefinition(TextDocumentUri $file, string $fromClass, string $toClass): array - { - $document = $this->locator->get($file); - $locatedEdits = []; - foreach ($this->mover->replaceReferences( - $this->mover->findReferences($document, $fromClass), - $toClass - ) as $edit) { - $locatedEdits[] = new LocatedTextEdit($file, $edit); - } - - return $locatedEdits; - } -} diff --git a/lib/Rename/Adapter/ClassToFile/ClassToFileNameToUriConverter.php b/lib/Rename/Adapter/ClassToFile/ClassToFileNameToUriConverter.php deleted file mode 100644 index a8be256f4f..0000000000 --- a/lib/Rename/Adapter/ClassToFile/ClassToFileNameToUriConverter.php +++ /dev/null @@ -1,27 +0,0 @@ -classToFile->classToFileCandidates(ClassName::fromString($className))->best()); - } catch (RuntimeException $error) { - throw new CouldNotConvertClassToUri($error->getMessage(), 0, $error); - } - } -} diff --git a/lib/Rename/Adapter/ClassToFile/ClassToFileUriToNameConverter.php b/lib/Rename/Adapter/ClassToFile/ClassToFileUriToNameConverter.php deleted file mode 100644 index fe07f11d47..0000000000 --- a/lib/Rename/Adapter/ClassToFile/ClassToFileUriToNameConverter.php +++ /dev/null @@ -1,26 +0,0 @@ -fileToClass->fileToClassCandidates(FilePath::fromString($uri->path()))->best()->__toString(); - } catch (RuntimeException $error) { - throw new CouldNotConvertUriToClass($error->getMessage(), 0, $error); - } - } -} diff --git a/lib/Rename/Adapter/ReferenceFinder/AbstractReferenceRenamer.php b/lib/Rename/Adapter/ReferenceFinder/AbstractReferenceRenamer.php deleted file mode 100644 index ef4ef6bce0..0000000000 --- a/lib/Rename/Adapter/ReferenceFinder/AbstractReferenceRenamer.php +++ /dev/null @@ -1,125 +0,0 @@ -parser->get($textDocument)->getDescendantNodeAtPosition($offset->toInt()); - return $this->getRenameRangeForNode($node); - } - - public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator - { - $range = $this->getRenameRange($textDocument, $offset); - if (null === $range) { - return; - } - $originalName = $this->rangeText($textDocument, $range); - yield from $this->doRename($textDocument, $offset, $range, $originalName, $newName); - } - - /** - * @return Generator - */ - protected function doRename(TextDocument $textDocument, ByteOffset $offset, ByteOffsetRange $range, string $originalName, string $newName): Generator - { - foreach ($this->referenceFinder->findReferences($textDocument, $offset) as $reference) { - if (!$reference->isSurely()) { - continue; - } - - try { - yield $this->renameEdit($reference->location(), $range, $originalName, $newName); - } catch (TextDocumentNotFound) { - continue; - } - } - } - - abstract protected function getRenameRangeForNode(Node $node): ?ByteOffsetRange; - - /** - * @param Token|Node $tokenOrNode - */ - protected function offsetRangeFromToken($tokenOrNode, bool $hasDollar): ?ByteOffsetRange - { - if (!$tokenOrNode instanceof Token) { - return null; - } - - if ($hasDollar) { - return ByteOffsetRange::fromInts($tokenOrNode->start + 1, $tokenOrNode->getEndPosition()); - } - - return ByteOffsetRange::fromInts($tokenOrNode->start, $tokenOrNode->getEndPosition()); - } - - protected function renameEdit(Location $location, ?ByteOffsetRange $range, string $originalName, string $newName): LocatedTextEdit - { - $referenceDocument = $this->locator->get($location->uri()); - - $range = $this->getRenameRange($referenceDocument, $location->range()->start()); - - if (null === $range) { - throw new CouldNotRename(sprintf( - 'Could not find corresponding reference to member name "%s" in document "%s" at offset %s', - $originalName, - $referenceDocument->uri()->__toString(), - $location->range()->start()->toInt() - )); - } - - $foundName = $this->rangeText($referenceDocument, $range); - if ($foundName !== $originalName) { - throw new CouldNotRename(sprintf( - 'Found referenced name "%s" in "%s" does not match original name "%s", perhaps the text document is out of sync?', - $foundName, - $referenceDocument->uri()->__toString(), - $originalName - )); - } - - return new LocatedTextEdit( - $location->uri(), - PhpactorTextEdit::create( - $range->start(), - $range->end()->toInt() - $range->start()->toInt(), - $newName - ) - ); - } - - private function rangeText(TextDocument $textDocument, ByteOffsetRange $range): string - { - return substr( - $textDocument->__toString(), - $range->start()->toInt(), - $range->end()->toInt() - $range->start()->toInt() - ); - } -} diff --git a/lib/Rename/Adapter/ReferenceFinder/ClassMover/ClassRenamer.php b/lib/Rename/Adapter/ReferenceFinder/ClassMover/ClassRenamer.php deleted file mode 100644 index 547975df6c..0000000000 --- a/lib/Rename/Adapter/ReferenceFinder/ClassMover/ClassRenamer.php +++ /dev/null @@ -1,158 +0,0 @@ -parser->get($textDocument)->getDescendantNodeAtPosition($offset->toInt()); - - if ($node instanceof ClassDeclaration) { - return TokenUtil::offsetRangeFromToken($node->name, false); - } - - if ($node instanceof EnumDeclaration) { - return TokenUtil::offsetRangeFromToken($node->name, false); - } - - if ($node instanceof InterfaceDeclaration) { - return TokenUtil::offsetRangeFromToken($node->name, false); - } - - if ($node instanceof TraitDeclaration) { - return TokenUtil::offsetRangeFromToken($node->name, false); - } - - if ($node instanceof MicrosoftQualifiedName) { - return TokenUtil::offsetRangeFromToken($node, false); - } - - return null; - } - - public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator - { - $node = $this->parser->get($textDocument)->getDescendantNodeAtPosition($offset->toInt()); - - $originalName = $this->getFullName($node); - $newName = $this->createNewName($originalName, $newName); - - try { - $oldUri = $this->oldNameToUriConverter->convert($originalName->getFullyQualifiedNameText()); - $newUri = $this->newNameToUriConverter->convert($newName); - } catch (CouldNotConvertClassToUri $error) { - throw new CouldNotRename($error->getMessage(), 0, $error); - } - - if ($newName === $originalName->getFullyQualifiedNameText()) { - return; - } - - $seen = []; - foreach ($this->referenceFinder->findReferences($textDocument, $offset) as $reference) { - if (isset($seen[$reference->location()->uri()->__toString()])) { - continue; - } - $seen[$reference->location()->uri()->__toString()] = true; - - if (!$reference->isSurely()) { - continue; - } - - try { - $referenceDocument = $this->locator->get($reference->location()->uri()); - } catch (TextDocumentNotFound) { - continue; - } - - $edits = $this->classMover->replaceReferences( - $this->classMover->findReferences($referenceDocument->__toString(), $originalName->__toString()), - QualifiedName::fromString($newName) - ); - - foreach ($edits as $edit) { - yield new LocatedTextEdit( - $reference->location()->uri(), - $edit, - ); - } - } - - return new RenameResult($oldUri, $newUri); - } - - private function getFullName(Node $node): ResolvedName - { - if ($node instanceof MicrosoftQualifiedName) { - $name = $node->getResolvedName(); - if (!$name instanceof ResolvedName) { - throw new RuntimeException(sprintf( - 'Could not get resolved name for node "%s"', - get_class($node) - )); - } - - return $name; - } - - if ($node instanceof NamespacedNameInterface) { - return $node->getNamespacedName(); - } - - throw new RuntimeException(sprintf( - 'Could not resolve full name for node "%s"', - get_class($node) - )); - } - - private function createNewName(ResolvedName $originalName, string $newName): string - { - $parts = $originalName->getNameParts(); - - if (count($parts) === 1) { - return $newName; - } - - array_pop($parts); - $newName = implode('\\', $parts) . '\\' . $newName; - return $newName; - } -} diff --git a/lib/Rename/Adapter/ReferenceFinder/MemberRenamer.php b/lib/Rename/Adapter/ReferenceFinder/MemberRenamer.php deleted file mode 100644 index aadc6d4fa0..0000000000 --- a/lib/Rename/Adapter/ReferenceFinder/MemberRenamer.php +++ /dev/null @@ -1,109 +0,0 @@ -name->start, $node->name->getEndPosition()); - } - - // hack because the WR property deefinition locator returns the - // property declaration and not the variable - if ($node instanceof PropertyDeclaration) { - $variable = $node->getFirstDescendantNode(Variable::class); - if (!$variable instanceof Variable) { - return null; - } - return $this->offsetRangeFromToken($variable->name, true); - } - - if ($node instanceof Parameter) { - if ($node->visibilityToken === null) { - return null; - } - - return $this->offsetRangeFromToken($node->variableName, true); - } - - // hack because the WR property deefinition locator returns the - // property declaration and not the variable - if ($node instanceof ClassConstDeclaration) { - $constElement = $node->getFirstDescendantNode(ConstElement::class); - if (!$constElement instanceof ConstElement) { - return null; - } - return $this->offsetRangeFromToken($constElement->name, false); - } - if ($node instanceof EnumCaseDeclaration) { - return $this->offsetRangeFromToken($node->name, false); - } - - if ($node instanceof Variable && $node->getFirstAncestor(PropertyDeclaration::class)) { - return $this->offsetRangeFromToken($node->name, true); - } - - if ( - $node instanceof Variable && - ( - $node->getFirstAncestor(ScopedPropertyAccessExpression::class) || - $node->getFirstAncestor(MemberAccessExpression::class) - ) - ) { - return $this->offsetRangeFromToken($node->name, true); - } - - if ($node instanceof MemberAccessExpression || $node instanceof ScopedPropertyAccessExpression) { - return $this->offsetRangeFromToken($node->memberName, false); - } - - if ($node instanceof ConstElement) { - return ByteOffsetRange::fromInts($node->name->start, $node->name->getEndPosition()); - } - - return null; - } - - /** - * @return Generator - */ - protected function doRename(TextDocument $textDocument, ByteOffset $offset, ByteOffsetRange $range, string $originalName, string $newName): Generator - { - foreach ($this->implementationFinder->findImplementations($textDocument, $offset, true) as $location) { - yield $this->renameEdit($location, $range, $originalName, $newName); - } - - yield from parent::doRename($textDocument, $offset, $range, $originalName, $newName); - } -} diff --git a/lib/Rename/Adapter/ReferenceFinder/VariableRenamer.php b/lib/Rename/Adapter/ReferenceFinder/VariableRenamer.php deleted file mode 100644 index 0b92361f11..0000000000 --- a/lib/Rename/Adapter/ReferenceFinder/VariableRenamer.php +++ /dev/null @@ -1,51 +0,0 @@ -parent instanceof ScopedPropertyAccessExpression - && $node->parent->scopeResolutionQualifier !== $node - ) { - return null; - } - - if ( - $node instanceof Variable && - !$node->getFirstAncestor(PropertyDeclaration::class) - ) { - return $this->offsetRangeFromToken($node->name, true); - } - - - if ($node instanceof Parameter && $node->visibilityToken) { - return null; - } - - if ( - ( - $node instanceof Parameter || - $node instanceof UseVariableName || - $node instanceof CatchClause - ) && - $node->variableName !== null - ) { - return $this->offsetRangeFromToken($node->variableName, true); - } - - return null; - } -} diff --git a/lib/Rename/Adapter/Test/TestNameToUriConverter.php b/lib/Rename/Adapter/Test/TestNameToUriConverter.php deleted file mode 100644 index 34cf4bac0d..0000000000 --- a/lib/Rename/Adapter/Test/TestNameToUriConverter.php +++ /dev/null @@ -1,30 +0,0 @@ - $map - */ - public function __construct(private array $map) - { - } - - public function convert(string $className): TextDocumentUri - { - if (!isset($this->map[$className])) { - throw new RuntimeException(sprintf( - 'Test class name "%s" not mapped to file', - $className - )); - } - - return $this->map[$className]; - } - -} diff --git a/lib/Rename/Adapter/Tolerant/TokenUtil.php b/lib/Rename/Adapter/Tolerant/TokenUtil.php deleted file mode 100644 index f6969c9fa7..0000000000 --- a/lib/Rename/Adapter/Tolerant/TokenUtil.php +++ /dev/null @@ -1,30 +0,0 @@ -getStartPosition(), $tokenOrNode->getEndPosition()); - } - - if (!$tokenOrNode instanceof Token) { - return null; - } - - if ($hasDollar) { - return ByteOffsetRange::fromInts($tokenOrNode->start + 1, $tokenOrNode->getEndPosition()); - } - - return ByteOffsetRange::fromInts($tokenOrNode->start, $tokenOrNode->getEndPosition()); - } -} diff --git a/lib/Rename/Adapter/WorseReflection/WorseNameToUriConverter.php b/lib/Rename/Adapter/WorseReflection/WorseNameToUriConverter.php deleted file mode 100644 index edd1031fc6..0000000000 --- a/lib/Rename/Adapter/WorseReflection/WorseNameToUriConverter.php +++ /dev/null @@ -1,32 +0,0 @@ -reflector->reflectClassLike($className)->sourceCode()->uri(); - } catch (NotFound $notFound) { - throw new CouldNotConvertClassToUri($notFound->getMessage(), 0, $notFound); - } - - if (null === $uri) { - throw new CouldNotConvertClassToUri(sprintf('Reflected source for "%s" did not have a URI associated with it', $className)); - } - - return $uri; - } -} diff --git a/lib/Rename/Adapter/WorseReflection/WorseReflectionMemberRenamer.php b/lib/Rename/Adapter/WorseReflection/WorseReflectionMemberRenamer.php deleted file mode 100644 index e474007dac..0000000000 --- a/lib/Rename/Adapter/WorseReflection/WorseReflectionMemberRenamer.php +++ /dev/null @@ -1,109 +0,0 @@ -resolveMember($textDocument, $offset); - - if (null === $member) { - return null; - } - - return $member->nameRange(); - } - - public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator - { - $member = $this->resolveMember($textDocument, $offset); - - if (null === $member) { - return; - } - - $uri = $member->class()->sourceCode()->uri(); - - if (null === $uri) { - return; - } - - $rangeStart = $member->nameRange()->start(); - yield new LocatedTextEdit( - $uri, - PhpactorTextEdit::create( - $rangeStart, - $member->nameRange()->length(), - $newName, - ) - ); - - $accesses = match ($member->memberType()) { - ReflectionMember::TYPE_METHOD => $this->reflector->navigate($textDocument)->methodCalls(), - ReflectionMember::TYPE_PROPERTY => $this->reflector->navigate($textDocument)->propertyAccesses(), - default => [], - }; - - foreach ($accesses as $access) { - if ($access->name() !== $member->name()) { - continue; - } - yield new LocatedTextEdit( - $uri, - PhpactorTextEdit::create($access->nameRange()->start(), $access->nameRange()->length(), $newName) - ); - } - } - - private function resolveMember(TextDocument $textDocument, ByteOffset $offset): ?ReflectionMember - { - $context = $this->reflector->reflectOffset($textDocument, $offset)->nodeContext(); - - $symbolType = $context->symbol()->symbolType(); - if (!in_array($symbolType, [ - Symbol::METHOD, - Symbol::PROPERTY, - Symbol::VARIABLE, // promoted properties 🙃 - ])) { - return null; - } - - $containerType = $context->containerType(); - - if (!$containerType instanceof ClassLikeType) { - return null; - } - - $class = $this->reflector->reflectClassLike($containerType->name()); - - $memberType = $symbolType === Symbol::VARIABLE ? 'property' : $symbolType; - $members = $class->members()->byMemberType($memberType)->byName($context->symbol()->name()); - - foreach ($members as $member) { - if (!$member->visibility()->isPrivate()) { - return null; - } - return $member; - } - - return null; - } -} diff --git a/lib/Rename/Model/Exception/CouldNotConvertClassToUri.php b/lib/Rename/Model/Exception/CouldNotConvertClassToUri.php deleted file mode 100644 index 48279befb6..0000000000 --- a/lib/Rename/Model/Exception/CouldNotConvertClassToUri.php +++ /dev/null @@ -1,9 +0,0 @@ - - */ - public function renameFile(TextDocumentUri $from, TextDocumentUri $to): Promise; -} diff --git a/lib/Rename/Model/FileRenamer/LoggingFileRenamer.php b/lib/Rename/Model/FileRenamer/LoggingFileRenamer.php deleted file mode 100644 index de5993fd57..0000000000 --- a/lib/Rename/Model/FileRenamer/LoggingFileRenamer.php +++ /dev/null @@ -1,32 +0,0 @@ -innerRenamer->renameFile($from, $to); - $this->logger->debug(sprintf( - 'Moved file "%s" to "%s"', - $from->__toString(), - $to->__toString() - )); - return $result; - }); - } -} diff --git a/lib/Rename/Model/FileRenamer/TestFileRenamer.php b/lib/Rename/Model/FileRenamer/TestFileRenamer.php deleted file mode 100644 index 3d947ffb16..0000000000 --- a/lib/Rename/Model/FileRenamer/TestFileRenamer.php +++ /dev/null @@ -1,31 +0,0 @@ -workspaceEdits = $workspaceEdits ?: LocatedTextEditsMap::create(); - } - - public function renameFile(TextDocumentUri $from, TextDocumentUri $to): Promise - { - if ($this->throw) { - return new Failure(new CouldNotRename('There was a problem')); - } - return new Success($this->workspaceEdits); - } -} diff --git a/lib/Rename/Model/LocatedTextEdit.php b/lib/Rename/Model/LocatedTextEdit.php deleted file mode 100644 index 020574e341..0000000000 --- a/lib/Rename/Model/LocatedTextEdit.php +++ /dev/null @@ -1,25 +0,0 @@ -textEdit; - } - - public function documentUri(): TextDocumentUri - { - return $this->documentUri; - } -} diff --git a/lib/Rename/Model/LocatedTextEdits.php b/lib/Rename/Model/LocatedTextEdits.php deleted file mode 100644 index 3bbc7e7308..0000000000 --- a/lib/Rename/Model/LocatedTextEdits.php +++ /dev/null @@ -1,46 +0,0 @@ -textEdits; - } - - public function documentUri(): TextDocumentUri - { - return $this->documentUri; - } - - /** - * @return array - * @param LocatedTextEdit[] $edits - */ - public static function fromLocatedEditsToCollection(array $edits): array - { - $byPath = []; - $locatedEdits = []; - foreach ($edits as $edit) { - if (!isset($byPath[$edit->documentUri()->__toString()])) { - $byPath[$edit->documentUri()->__toString()] = []; - } - $byPath[$edit->documentUri()->__toString()][] = $edit->textEdit(); - } - foreach ($byPath as $path => $edits) { - $locatedEdits[] = new self(TextEdits::fromTextEdits($edits), TextDocumentUri::fromString($path)); - } - - return $locatedEdits; - } -} diff --git a/lib/Rename/Model/LocatedTextEditsMap.php b/lib/Rename/Model/LocatedTextEditsMap.php deleted file mode 100644 index 4c09332793..0000000000 --- a/lib/Rename/Model/LocatedTextEditsMap.php +++ /dev/null @@ -1,75 +0,0 @@ - $map - */ - public function __construct(private array $map) - { - } - - public static function create(): self - { - return new self([]); - } - - /** - * @param LocatedTextEdit[] $locatedEdits - */ - public static function fromLocatedEdits(array $locatedEdits): self - { - $map = new self([]); - foreach ($locatedEdits as $locationEdit) { - $map = $map->withTextEdit($locationEdit); - } - - return $map; - } - - public function withTextEdit(LocatedTextEdit $edit): self - { - $map = $this->map; - $uri = $edit->documentUri(); - $edit = $edit->textEdit(); - - if (!isset($map[$uri->__toString()])) { - $map[$uri->__toString()] = new TextEdits(); - } - - $map[$uri->__toString()] = $map[$uri->__toString()]->add($edit); - - return new self($map); - } - - public function merge(self $map): self - { - $me = $this; - - foreach ($map->toLocatedTextEdits() as $textEdit) { - foreach ($textEdit->textEdits() as $edit) { - $me = $me->withTextEdit(new LocatedTextEdit($textEdit->documentUri(), $edit)); - } - } - - return $me; - } - - /** - * @return LocatedTextEdits[] - */ - public function toLocatedTextEdits(): array - { - $locatedTextEdits = []; - foreach ($this->map as $uri => $edits) { - $locatedTextEdits[] = new LocatedTextEdits($edits, TextDocumentUri::fromString($uri)); - } - - return $locatedTextEdits; - } -} diff --git a/lib/Rename/Model/NameToUriConverter.php b/lib/Rename/Model/NameToUriConverter.php deleted file mode 100644 index efb9467763..0000000000 --- a/lib/Rename/Model/NameToUriConverter.php +++ /dev/null @@ -1,14 +0,0 @@ -locations = $locations; - } - - public function findReferences(TextDocument $document, ByteOffset $byteOffset): Generator - { - foreach ($this->locations as $location) { - yield $location; - } - return true; - } -} diff --git a/lib/Rename/Model/ReferenceFinder/PredefinedReferenceFinderFoo.php b/lib/Rename/Model/ReferenceFinder/PredefinedReferenceFinderFoo.php deleted file mode 100644 index 62ee9614a2..0000000000 --- a/lib/Rename/Model/ReferenceFinder/PredefinedReferenceFinderFoo.php +++ /dev/null @@ -1,31 +0,0 @@ -locations = $locations; - } - - public function findReferences(TextDocument $document, ByteOffset $byteOffset): Generator - { - foreach ($this->locations as $location) { - yield $location; - } - - return true; - } -} diff --git a/lib/Rename/Model/RenameResult.php b/lib/Rename/Model/RenameResult.php deleted file mode 100644 index af88dc75c1..0000000000 --- a/lib/Rename/Model/RenameResult.php +++ /dev/null @@ -1,24 +0,0 @@ -oldUri; - } - - public function newUri(): TextDocumentUri - { - return $this->newUri; - } -} diff --git a/lib/Rename/Model/Renamer.php b/lib/Rename/Model/Renamer.php deleted file mode 100644 index c7d982504b..0000000000 --- a/lib/Rename/Model/Renamer.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ - public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator; -} diff --git a/lib/Rename/Model/Renamer/ChainRenamer.php b/lib/Rename/Model/Renamer/ChainRenamer.php deleted file mode 100644 index 73f5894026..0000000000 --- a/lib/Rename/Model/Renamer/ChainRenamer.php +++ /dev/null @@ -1,39 +0,0 @@ -renamers as $renamer) { - if (null !== ($range = $renamer->getRenameRange($textDocument, $offset))) { - return $range; - } - } - return null; - } - - - public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator - { - foreach ($this->renamers as $renamer) { - if (null !== ($range = $renamer->getRenameRange($textDocument, $offset))) { - $rename = $renamer->rename($textDocument, $offset, $newName); - yield from $rename; - return $rename->getReturn(); - } - } - } -} diff --git a/lib/Rename/Model/Renamer/InMemoryRenamer.php b/lib/Rename/Model/Renamer/InMemoryRenamer.php deleted file mode 100644 index 7bd8f3d35c..0000000000 --- a/lib/Rename/Model/Renamer/InMemoryRenamer.php +++ /dev/null @@ -1,32 +0,0 @@ -range; - } - - public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator - { - yield from $this->results; - } -} diff --git a/lib/Rename/Model/UriToNameConverter.php b/lib/Rename/Model/UriToNameConverter.php deleted file mode 100644 index 8794971976..0000000000 --- a/lib/Rename/Model/UriToNameConverter.php +++ /dev/null @@ -1,14 +0,0 @@ -createDocument('1.php', 'createDocument('2.php', 'createDocument('3.php', 'createDocument('4.php', 'createRenamer( - [$document1, $document2, $document3, $document4], - [ - (new ClassRecord('One')) - ->setType('class') - ->addReference((string)TextDocumentUri::fromString($this->path('3.php'))) - ->addReference((string)TextDocumentUri::fromString($this->path('4.php'))), - FileRecord::fromPath((string)TextDocumentUri::fromString($this->path('3.php'))) - ->addReference(new RecordReference(ClassRecord::RECORD_TYPE, 'One', 10, end: 20)), - FileRecord::fromPath((string)TextDocumentUri::fromString($this->path('4.php'))) - ->addReference(new RecordReference(ClassRecord::RECORD_TYPE, 'One', 10, end: 20)), - ] - ); - - $edits = wait($renamer->renameFile($document1->uriOrThrow(), $document2->uriOrThrow())); - - self::assertInstanceOf(LocatedTextEditsMap::class, $edits); - assert($edits instanceof LocatedTextEditsMap); - self::assertCount(3, $edits->toLocatedTextEdits(), 'Locates two references'); - } - - /** - * @param TextDocument[] $textDocuments - * @param Record[] $records - */ - private function createRenamer(array $textDocuments, array $records): FileRenamer - { - foreach ($textDocuments as $textDocument) { - assert($textDocument instanceof TextDocument); - file_put_contents($textDocument->uri()->path(), $textDocument->__toString()); - } - - return new FileRenamer( - new ClassToFileUriToNameConverter(new SimpleFileToClass()), - InMemoryDocumentLocator::fromTextDocuments($textDocuments), - new QueryClient(new InMemoryIndex($records)), - new ClassMover(), - ); - } - - private function path(string $path): string - { - return $this->workspace()->path($path); - } - - private function createDocument(string $path, string $content): TextDocument - { - return TextDocumentBuilder::create($content)->uri($this->path($path))->build(); - } -} diff --git a/lib/Rename/Tests/Adapter/ClassToFile/ClassToFileNameToUriConverterTest.php b/lib/Rename/Tests/Adapter/ClassToFile/ClassToFileNameToUriConverterTest.php deleted file mode 100644 index 3732fb5966..0000000000 --- a/lib/Rename/Tests/Adapter/ClassToFile/ClassToFileNameToUriConverterTest.php +++ /dev/null @@ -1,23 +0,0 @@ -workspace()->put('Foo.php', 'workspace()->path())); - - $uri = $converter->convert('Foo'); - - self::assertInstanceOf(TextDocumentUri::class, $uri); - self::assertEquals($this->workspace()->path('Foo.php'), $uri->path()); - } -} diff --git a/lib/Rename/Tests/Adapter/ClassToFile/ClassToFileUriToNameConverterTest.php b/lib/Rename/Tests/Adapter/ClassToFile/ClassToFileUriToNameConverterTest.php deleted file mode 100644 index 714a4db864..0000000000 --- a/lib/Rename/Tests/Adapter/ClassToFile/ClassToFileUriToNameConverterTest.php +++ /dev/null @@ -1,30 +0,0 @@ -workspace()->put('1.php', 'convert(TextDocumentUri::fromString($this->workspace()->path('1.php'))); - self::assertEquals('Foo', $class); - } - - public function testErrorWhenCannotConvert(): void - { - $this->expectException(CouldNotConvertUriToClass::class); - $this->workspace()->put('1.php', 'convert(TextDocumentUri::fromString($this->workspace()->path('1.php'))); - } -} diff --git a/lib/Rename/Tests/Adapter/ReferenceFinder/ClassMover/ClassRenamerTest.php b/lib/Rename/Tests/Adapter/ReferenceFinder/ClassMover/ClassRenamerTest.php deleted file mode 100644 index 3b0116e9f5..0000000000 --- a/lib/Rename/Tests/Adapter/ReferenceFinder/ClassMover/ClassRenamerTest.php +++ /dev/null @@ -1,236 +0,0 @@ - TextDocumentUri::fromString('file:///foobar'), - 'FoobarBaz' => TextDocumentUri::fromString('file:///foobar-new'), - ]); - $renamer = new ClassRenamer( - $converter, - $converter, - $finder, - InMemoryDocumentLocator::fromTextDocuments([]), - new TolerantAstProvider(), - new ClassMover() - ); - - $textDocument = TextDocumentBuilder::fromPathAndString('/path', 'rename($textDocument, ByteOffset::fromInt(12), 'FoobarBaz'), false); - $this->addToAssertionCount(1); - } - - #[DataProvider('provideRename')] - public function testRename( - string $oldPath, - string $source, - string $newName, - ?string $newUri, - int $expectedEditsCount, - ?string $expected - ): void { - $extractor = OffsetExtractor::create() - ->registerOffset('offset', '<>') - ->registerOffset('r', '') - ->parse($source); - - $offset = $extractor->offset('offset'); - $references = $extractor->offsets('r'); - - $source = $extractor->source(); - - $textDocument = TextDocumentBuilder::create($source)->uri($oldPath)->build(); - - $renamer = $this->createRenamer('/foo/', $references, $textDocument); - self::assertNotNull($renamer->getRenameRange($textDocument, $offset)); - $rename = $renamer->rename($textDocument, $offset, $newName); - - $actualResults = iterator_to_array($rename, false); - - $renameResult = $rename->getReturn(); - self::assertSame($newUri, $renameResult ? $renameResult->newUri()->__toString() : null); - - $edits = LocatedTextEditsMap::fromLocatedEdits($actualResults); - $locateds = $edits->toLocatedTextEdits(); - self::assertCount($expectedEditsCount, $locateds); - - if (0 === $expectedEditsCount) { - return; - } - $located = reset($locateds); - assert($located instanceof LocatedTextEdits); - self::assertEquals($expected, $located->textEdits()->apply($source)); - } - - /** - * @return Generator - */ - public static function provideRename(): Generator - { - yield 'class' => [ - '/foo/Class1.php', - 'class Cl<>ass1 { }', - 'Class2', - 'file:///foo/Class2.php', - 1, - ' [ - '/foo/Class1.php', - 'class Cl<>ass1 { }', - 'Class1', - null, - 0, - null, - ]; - - yield 'interface' => [ - '/foo/Interface1.php', - 'interface Inter<>face1 { }', - 'Interface2', - 'file:///foo/Interface2.php', - 1, - ' [ - '/foo/Enum1.php', - 'enum Mar<>ker { }', - 'Pony', - 'file:///foo/Pony.php', - 1, - ' [ - '/foo/Interface1.php', - 'interface Inter<>face1 { } class Class1 implements Interface1 { }', - 'Interface2', - 'file:///foo/Interface2.php', - 1, - ' [ - '/foo/Trait1.php', - 'trait Tra<>it1 { }', - 'Trait2', - 'file:///foo/Trait2.php', - 1, - ' [ - '/foo/Trait1.php', - 'trait Tra<>it1 { } class Class1 { use Trait1; }', - 'Trait2', - 'file:///foo/Trait2.php', - 1, - ' [ - '/foo/Foo/Class1.php', - 'class Cl<>ass1 { }', - 'Class2', - 'file:///foo/Foo/Class2.php', - 1, - ' [ - '/foo/Foo/Class1.php', - 'class Class1 { } Cla<>ss1::foo();', - 'Class2', - 'file:///foo/Foo/Class2.php', - 1, - ' [ - '/foo/Foo/Class1.php', - 'Cla<>ss1::foo();', - 'Class2', - 'file:///foo/Foo/Class2.php', - 1, - ' [ - '/foo/Class1.php', - 'Cla<>ss1::foo();', - 'Class2', - 'file:///foo/Class2.php', - 1, - 'namespaceRootDir, - str_replace('\\', '/', $className), - )); - } - }; - - return new ClassRenamer( - $nameToUriConverter, - $nameToUriConverter, - $this->offsetsToReferenceFinder($textDocument, $references), - InMemoryDocumentLocator::fromTextDocuments([$textDocument]), - new TolerantAstProvider(), - new ClassMover() - ); - } -} diff --git a/lib/Rename/Tests/Adapter/ReferenceFinder/MemberRenamerTest.php b/lib/Rename/Tests/Adapter/ReferenceFinder/MemberRenamerTest.php deleted file mode 100644 index 61ee5df9af..0000000000 --- a/lib/Rename/Tests/Adapter/ReferenceFinder/MemberRenamerTest.php +++ /dev/null @@ -1,175 +0,0 @@ -registerOffset('selection', '<>') - ->registerRange('expectedRange', '{{', '}}') - ->parse($source); - - $selection = $extractor->offset('selection'); - $expectedRanges = $extractor->ranges('expectedRange'); - $newSource = $extractor->source(); - - $expectedRange = count($expectedRanges) > 0 ? $expectedRanges[0] : null; - - $document = TextDocumentBuilder::create($newSource) - ->uri('file:///test/testDoc') - ->build(); - - $variableRenamer = new MemberRenamer( - new PredefinedReferenceFinder(...[]), - InMemoryDocumentLocator::fromTextDocuments([]), - new TolerantAstProvider(), - new PredefiniedImplementationFinder(new Locations([])), - ); - - $actualRange = $variableRenamer->getRenameRange($document, $selection); - - $this->assertEquals($expectedRange, $actualRange); - } - - /** - * @return Generator - */ - public static function provideGetRenameRange(): Generator - { - yield 'method declaration' => [ - 'thod1}}(){ } }' - ]; - yield 'method call' => [ - '{{me<>thod1}}(); }' - ]; - yield 'static method call' => [ - 'thod1}}(); }' - ]; - yield 'property declaration' => [ - 'erty}}; }' - ]; - yield 'property access' => [ - '${{me<>thod1}};' - ]; - yield 'static property access' => [ - 'thod1}}; }' - ]; - yield 'constant declaration' => [ - 'OO}}="bar"; }' - ]; - yield 'constant access' => [ - 'OO}};' - ]; - } - - #[DataProvider('provideRename')] - public function testRename(string $source): void - { - $extractor = OffsetExtractor::create() - ->registerOffset('selection', '<>') - ->registerOffset('references', '') - ->registerOffset('implementations', '') - ->registerRange('resultEditRanges', '{{', '}}') - ->parse($source); - - $selection = $extractor->offset('selection'); - $references = $extractor->offsets('references'); - $implementations = $extractor->offsets('implementations'); - $resultEditRanges = $extractor->ranges('resultEditRanges'); - $newSource = $extractor->source(); - - $newName = 'newName'; - - $textDocument = TextDocumentBuilder::create($newSource) - ->uri(self::EXAMPLE_DOCUMENT_URI) - ->build(); - - $renamer = new MemberRenamer( - new PredefinedReferenceFinder(...array_map(function (ByteOffset $reference) use ($textDocument) { - return PotentialLocation::surely( - new Location($textDocument->uri(), ByteOffsetRange::fromByteOffset($reference)) - ); - }, $references)), - InMemoryDocumentLocator::fromTextDocuments([$textDocument]), - new TolerantAstProvider(), - new PredefiniedImplementationFinder(new Locations(array_map(function (ByteOffset $reference) use ($textDocument) { - return new Location($textDocument->uri(), ByteOffsetRange::fromByteOffset($reference)); - }, $implementations))), - ); - - $resultEdits = []; - foreach ($resultEditRanges as $range) { - assert($range instanceof ByteOffsetRange); - $resultEdits[] = TextEdit::create( - $range->start(), - $range->end()->toInt() - $range->start()->toInt(), - $newName - ); - } - - $renamer->getRenameRange($textDocument, $selection); - $actualResults = iterator_to_array($renamer->rename($textDocument, $selection, $newName), false); - $this->assertEquals( - [ - new LocatedTextEdits( - TextEdits::fromTextEdits($resultEdits), - $textDocument->uri() - ) - ], - LocatedTextEditsMap::fromLocatedEdits($actualResults)->toLocatedTextEdits() - ); - } - - /** - * @return Generator - */ - public static function provideRename(): Generator - { - yield 'method declaration' => [ - 'meth<>od1}}() { } }' - ]; - yield 'method call' => [ - '{{me<>thod1}}(); }' - ]; - yield 'method calls' => [ - '{{me<>thod1}}(); $foo->{{me<>thod1}}();}' - ]; - yield 'static method call' => [ - 'me<>thod1}}(); }' - ]; - yield 'property and definition' => [ - 'private ${{foobar}}; function bar() { return $this->{{fo<>obar}}; } }' - ]; - yield 'constant and definition' => [ - 'const {{FOO}}="bar"; function bar() { return self::{{F<>OO}}; } }' - ]; - yield 'definition and constant' => [ - 'const {{F<>OO}}="bar"; function bar() { return self::{{FOO}}; } }' - ]; - } -} diff --git a/lib/Rename/Tests/Adapter/ReferenceFinder/ReferenceRenamerIntegrationTestCase.php b/lib/Rename/Tests/Adapter/ReferenceFinder/ReferenceRenamerIntegrationTestCase.php deleted file mode 100644 index 22dd1778c9..0000000000 --- a/lib/Rename/Tests/Adapter/ReferenceFinder/ReferenceRenamerIntegrationTestCase.php +++ /dev/null @@ -1,27 +0,0 @@ -uriOrThrow(), ByteOffsetRange::fromByteOffset($reference)) - ); - }, $references)); - } -} diff --git a/lib/Rename/Tests/Adapter/ReferenceFinder/VariableRenamerTest.php b/lib/Rename/Tests/Adapter/ReferenceFinder/VariableRenamerTest.php deleted file mode 100644 index 739fb956eb..0000000000 --- a/lib/Rename/Tests/Adapter/ReferenceFinder/VariableRenamerTest.php +++ /dev/null @@ -1,229 +0,0 @@ -registerOffset('selection', '<>') - ->registerRange('expectedRange', '{{', '}}') - ->parse($source); - - [ $selection ] = $extractor->offsets('selection'); - $expectedRanges = $extractor->ranges('expectedRange'); - $newSource = $extractor->source(); - - $expectedRange = count($expectedRanges) > 0 ? $expectedRanges[0] : null; - - $document = TextDocumentBuilder::create($newSource) - ->uri('file:///test/testDoc') - ->build(); - - $variableRenamer = $this->createRenamer([], null, []); - $actualRange = $variableRenamer->getRenameRange($document, $selection); - $this->assertEquals($expectedRange, $actualRange); - } - - /** - * @return Generator - */ - public static function provideGetRenameRange(): Generator - { - yield 'Rename argument' => [ - 'rg1}}){ } }' - ]; - - yield 'Rename variable' => [ - 'r1}} = 5;' - ]; - - yield 'Rename dynamic variable' => [ - 'r1}} = 5; } }' - ]; - - yield 'Rename variable in list deconstruction' => [ - 'r1}} ] = someFunc(); } }' - ]; - - yield 'Rename variable in anonymous function use statement' => [ - 'ar1}}) {} } }' - ]; - - yield 'Rename variable in catch statatement' => [ - 'xcp}}) {} } }' - ]; - - yield 'NULL: Rename static property (definition)' => [ - 'aticProp; } }' - ]; - - yield 'NULL: Rename property (definition)' => [ - 'p; } }' - ]; - - yield 'NULL: Rename property (multiple definition)' => [ - 'op2; } }' - ]; - - } - - #[DataProvider('provideRename')] - public function testRename(string $source): void - { - $extractor = OffsetExtractor::create() - ->registerOffset('selection', '<>') - ->registerOffset('definition', '') - ->registerOffset('references', '') - ->registerRange('expectedRanges', '{{', '}}') - ->parse($source); - - $selection = $extractor->offset('selection'); - $definition = $extractor->offset('definition'); - $references = $extractor->offsets('references'); - $expectedRanges = $extractor->ranges('expectedRanges'); - $newName = 'newName'; - - $textDocument = TextDocumentBuilder::create($extractor->source()) - ->uri(self::URI) - ->build(); - - $expectedEdits = []; - foreach ($expectedRanges as $range) { - assert($range instanceof ByteOffsetRange); - $expectedEdits[] = TextEdit::create( - $range->start(), - $range->end()->toInt() - $range->start()->toInt(), - $newName - ); - } - - $renamer = $this->createRenamer( - array_map( - function (ByteOffset $reference) use ($textDocument) { - return PotentialLocation::surely( - new Location($textDocument->uriOrThrow(), ByteOffsetRange::fromByteOffset($reference)) - ); - }, - $references - ), - new Location($textDocument->uriOrThrow(), ByteOffsetRange::fromByteOffsets($definition, $definition)), - [ $textDocument ] - ); - - $renamer->getRenameRange($textDocument, $selection); - - $actualResults = iterator_to_array( - $renamer->rename($textDocument, $selection, $newName), - false - ); - - $this->assertEquals( - [ - new LocatedTextEdits(TextEdits::fromTextEdits($expectedEdits), $textDocument->uri()) - ], - LocatedTextEditsMap::fromLocatedEdits($actualResults)->toLocatedTextEdits() - ); - } - - /** - * @return Generator - */ - public static function provideRename(): Generator - { - yield 'Rename variable' => [ - '${{va<>r1}} = 5; $var2 = ${{var1}} + 5; } }' - ]; - - yield 'Rename parameter' => [ - '${{ar<>g1}}){ $var5 = ${{arg1}}; } }' - ]; - - yield 'Rename variable (in list deconstructor)' => [ - '${{va<>r1}} ] = 5; $var2 = ${{var1}} + 5; } }' - ]; - - yield 'Rename variable (in list deconstructor with key)' => [ - '${{va<>r1}} ] = 5; $var2 = ${{var1}} + 5; } }' - ]; - - yield 'Rename variable (in list function no key)' => [ - '${{va<>r1}}) = 5; $var2 = ${{var1}} + 5; } }' - ]; - - yield 'Rename variable (in list function with key)' => [ - '${{va<>r1}}) = 5; $var2 = ${{var1}} + 5; } }' - ]; - - yield 'Rename variable (as foreach array)' => [ - '${{var}} = []; foreach(${{v<>ar}} as $val) { } } }' - ]; - - yield 'Rename variable (as foreach value)' => [ - '${{val}}) { ${{v<>al}} += 5; } } }' - ]; - - yield 'Rename variable (as foreach key)' => [ - '${{key}}=>$val) { ${{k<>ey}} += 5; } } }' - ]; - - yield 'Rename argument' => [ - '${{ar<>g1}}){ ${{arg1}} = 5; $var2 = ${{arg1}} + 5; } }' - ]; - - yield 'Rename argument (no hint)' => [ - '${{ar<>g1}}){ ${{arg1}} = 5; $var2 = ${{arg1}} + 5; } }' - ]; - - yield 'Rename foreach variable' => [ - '${{value}}) { echo ${{val<>ue}}; }' - ]; - - yield 'Rename variable on ::class' => [ - '${{va<>r1}}::class; $var2 = ${{var1}}; } }' - ]; - } - - /** - * @param PotentialLocation[] $references - * @param TextDocument[] $textDocuments - */ - private function createRenamer(array $references, ?Location $defintionLocation, array $textDocuments): VariableRenamer - { - $variableRenamer = new VariableRenamer( - new DefinitionAndReferenceFinder( - TestDefinitionLocator::fromSingleLocation(TypeFactory::unknown(), $defintionLocation), - new PredefinedReferenceFinder(...$references), - ), - InMemoryDocumentLocator::fromTextDocuments($textDocuments), - new TolerantAstProvider() - ); - return $variableRenamer; - } -} diff --git a/lib/Rename/Tests/Adapter/WorseReflection/WorseReflectionMemberRenamerTest.php b/lib/Rename/Tests/Adapter/WorseReflection/WorseReflectionMemberRenamerTest.php deleted file mode 100644 index f3e31d6676..0000000000 --- a/lib/Rename/Tests/Adapter/WorseReflection/WorseReflectionMemberRenamerTest.php +++ /dev/null @@ -1,119 +0,0 @@ -> - */ - public function provideRename(): Generator - { - yield from $this->renames(); - } - - protected function createRenamer(): Renamer - { - return new WorseReflectionMemberRenamer( - $this->reflector, - ); - } - - /** - * @return Generator> - */ - private function renames(): Generator - { - yield 'private method declaration' => [ - 'member_renamer/method_declaration_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('Foo\ClassOne'); - $method = $reflection->methods()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $method->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('Foo\ClassOne'); - self::assertTrue($reflection->methods()->has('newName')); - } - ]; - yield 'private static method declaration' => [ - 'member_renamer/method_declaration_static_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $method = $reflection->methods()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $method->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->methods()->has('newName')); - } - ]; - yield 'private property declaration' => [ - 'member_renamer/property_declaration_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $method = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $method->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->properties()->has('newName')); - } - ]; - yield 'private static property declaration' => [ - 'member_renamer/property_declaration_static_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $method = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $method->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->properties()->has('newName')); - } - ]; - yield 'private promoted property declaration' => [ - 'member_renamer/property_promoted_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $method = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $method->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->properties()->has('newName')); - } - ]; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/constant_declaration_private/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/constant_declaration_private/ClassOne.ph deleted file mode 100644 index 254f0a80ed..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/constant_declaration_private/ClassOne.ph +++ /dev/null @@ -1,11 +0,0 @@ -foobar(); - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/constant_declaration_private/test.ph b/lib/Rename/Tests/Cases/member_renamer/constant_declaration_private/test.ph deleted file mode 100644 index 4f176136c5..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/constant_declaration_private/test.ph +++ /dev/null @@ -1,9 +0,0 @@ -foobar() !== 'bar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/constant_declaration_protected/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/constant_declaration_protected/ClassOne.ph deleted file mode 100644 index 00bb5b0175..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/constant_declaration_protected/ClassOne.ph +++ /dev/null @@ -1,6 +0,0 @@ -barfoo() !== 'bar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/constant_declaration_public/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/constant_declaration_public/ClassOne.ph deleted file mode 100644 index a92eb33663..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/constant_declaration_public/ClassOne.ph +++ /dev/null @@ -1,11 +0,0 @@ -barfoo() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} -if (ClassOne::FOO !== 'bar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} -if (ClassOne::ZOO !== 'bar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/enum_case_declaration_private/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/enum_case_declaration_private/ClassOne.ph deleted file mode 100644 index 6b7061036b..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/enum_case_declaration_private/ClassOne.ph +++ /dev/null @@ -1,10 +0,0 @@ -foobar(); - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/method_declaration/test.ph b/lib/Rename/Tests/Cases/member_renamer/method_declaration/test.ph deleted file mode 100644 index afb41b7c4d..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/method_declaration/test.ph +++ /dev/null @@ -1,10 +0,0 @@ -hello() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/method_declaration_private/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/method_declaration_private/ClassOne.ph deleted file mode 100644 index 698fa960b0..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/method_declaration_private/ClassOne.ph +++ /dev/null @@ -1,16 +0,0 @@ -foobar(); - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/method_declaration_private/test.ph b/lib/Rename/Tests/Cases/member_renamer/method_declaration_private/test.ph deleted file mode 100644 index 54afc923b8..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/method_declaration_private/test.ph +++ /dev/null @@ -1,10 +0,0 @@ -hello() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} - diff --git a/lib/Rename/Tests/Cases/member_renamer/method_declaration_static_private/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/method_declaration_static_private/ClassOne.ph deleted file mode 100644 index f4d151c40f..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/method_declaration_static_private/ClassOne.ph +++ /dev/null @@ -1,14 +0,0 @@ -hello() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} - diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_private/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_private/ClassOne.ph deleted file mode 100644 index 723410c3f8..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_private/ClassOne.ph +++ /dev/null @@ -1,16 +0,0 @@ -foobar = $foobar; - } - - public function foobar(): string - { - return $this->foobar; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_private/test.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_private/test.ph deleted file mode 100644 index f092642707..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_private/test.ph +++ /dev/null @@ -1,9 +0,0 @@ -foobar() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/ClassOne.ph deleted file mode 100644 index ec3c4dbdd7..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/ClassOne.ph +++ /dev/null @@ -1,11 +0,0 @@ -foobar = $foobar; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/ClassTwo.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/ClassTwo.ph deleted file mode 100644 index 3e368fc5f6..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/ClassTwo.ph +++ /dev/null @@ -1,9 +0,0 @@ -foobar; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/test.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/test.ph deleted file mode 100644 index 84aea07f0e..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_protected/test.ph +++ /dev/null @@ -1,10 +0,0 @@ -barfoo() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/ClassOne.ph deleted file mode 100644 index fa60833b52..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/ClassOne.ph +++ /dev/null @@ -1,16 +0,0 @@ -foobar = $foobar; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/ClassTwo.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/ClassTwo.ph deleted file mode 100644 index 9e8bc3fbe7..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/ClassTwo.ph +++ /dev/null @@ -1,11 +0,0 @@ -foobar; - - return $this->found; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/test.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/test.ph deleted file mode 100644 index eb6fe813a6..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public/test.ph +++ /dev/null @@ -1,19 +0,0 @@ -barfoo() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} -if (!$two->foobar === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} -if (!$two->found === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_does_not_rename_others/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_does_not_rename_others/ClassOne.ph deleted file mode 100644 index 16aab83a43..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_does_not_rename_others/ClassOne.ph +++ /dev/null @@ -1,13 +0,0 @@ -foobar = $foobar; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_does_not_rename_others/test.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_does_not_rename_others/test.ph deleted file mode 100644 index 78683e1d0a..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_does_not_rename_others/test.ph +++ /dev/null @@ -1,9 +0,0 @@ -foobar === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/ClassOne.ph deleted file mode 100644 index 4fc83772f5..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/ClassOne.ph +++ /dev/null @@ -1,20 +0,0 @@ -foobar = $foobar; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/ClassTwo.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/ClassTwo.ph deleted file mode 100644 index 1135ad3e41..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/ClassTwo.ph +++ /dev/null @@ -1,12 +0,0 @@ -foobar; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/test.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/test.ph deleted file mode 100644 index f274fd4aa0..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_public_generic/test.ph +++ /dev/null @@ -1,15 +0,0 @@ -barfoo() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} -if (!$two->foobar === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_declaration_static_private/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/property_declaration_static_private/ClassOne.ph deleted file mode 100644 index c0f0a3d97d..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_declaration_static_private/ClassOne.ph +++ /dev/null @@ -1,16 +0,0 @@ -foobar() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/ClassOne.ph deleted file mode 100644 index a23c6d954b..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/ClassOne.ph +++ /dev/null @@ -1,20 +0,0 @@ -foobar; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/ClassTwo.ph b/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/ClassTwo.ph deleted file mode 100644 index 8ff6f1954e..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/ClassTwo.ph +++ /dev/null @@ -1,14 +0,0 @@ -foobar; - } - - public function dep(): string - { - return $this->depOld; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/test.ph b/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/test.ph deleted file mode 100644 index f68e365586..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_promoted_declaration_public/test.ph +++ /dev/null @@ -1,23 +0,0 @@ -barfoo() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} -if (!$two->foobar === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} -if (!$two->depOld === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} -if (!$two->barfoo() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_promoted_private/ClassOne.ph b/lib/Rename/Tests/Cases/member_renamer/property_promoted_private/ClassOne.ph deleted file mode 100644 index 7f2afed3d8..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_promoted_private/ClassOne.ph +++ /dev/null @@ -1,22 +0,0 @@ -barfoo = 'barfoo'; - } - - public function bar(): string - { - return $this->foobar; - } - - public function foo(): string - { - return $this->barfoo; - } -} diff --git a/lib/Rename/Tests/Cases/member_renamer/property_promoted_private/test.ph b/lib/Rename/Tests/Cases/member_renamer/property_promoted_private/test.ph deleted file mode 100644 index fa8c027817..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/property_promoted_private/test.ph +++ /dev/null @@ -1,19 +0,0 @@ -bar() === 'foobar') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} - -// this method call ensure that we did not replace -// all property names -if (!$two->foo() === 'barfoo') { - echo 'expected "foobar" but didn\'t get it'; - exit(127); -} diff --git a/lib/Rename/Tests/Cases/member_renamer/trait_insteadof/One.ph b/lib/Rename/Tests/Cases/member_renamer/trait_insteadof/One.ph deleted file mode 100644 index a51448ab55..0000000000 --- a/lib/Rename/Tests/Cases/member_renamer/trait_insteadof/One.ph +++ /dev/null @@ -1,26 +0,0 @@ -foobar() !== 'a') { - echo sprintf('expected "a" but didn\'t but got "%s"', $talker->foobar()); - exit(127); -} diff --git a/lib/Rename/Tests/Integration/Adapter/ReferenceFinder/MemberRenamerTest.php b/lib/Rename/Tests/Integration/Adapter/ReferenceFinder/MemberRenamerTest.php deleted file mode 100644 index de6ad9aabf..0000000000 --- a/lib/Rename/Tests/Integration/Adapter/ReferenceFinder/MemberRenamerTest.php +++ /dev/null @@ -1,472 +0,0 @@ - - */ - public function provideRename(): Generator - { - yield from $this->methodRenames(); - yield from $this->propertyRenames(); - yield from $this->constantRenames(); - yield from $this->traitRenames(); - yield from $this->enumRenames(); - } - - protected function createRenamer(): Renamer - { - $finder = new IndexedReferenceFinder( - $this->indexAgent->query(), - $this->reflector - ); - return new MemberRenamer( - $finder, - new FilesystemTextDocumentLocator(), - new TolerantAstProvider(), - new IndexedImplementationFinder($this->indexAgent->query(), $this->reflector) - ); - } - - /** - * @return Generator - */ - private function methodRenames(): Generator - { - yield 'method declaration' => [ - 'member_renamer/method_declaration', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $method = $reflection->methods()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $method->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->methods()->has('newName')); - } - ]; - - yield 'attributed method declaration' => [ - 'member_renamer/method_declaration', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $method = $reflection->methods()->get('złom'); - - return $renamer->rename( - $reflection->sourceCode(), - $method->nameRange()->start(), - 'scrap' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->methods()->has('scrap')); - } - ]; - - yield 'method reference' => [ - 'member_renamer/method_declaration', - function (Reflector $reflector, Renamer $renamer): Generator { - $methodCalls = $reflector->navigate(TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build())->methodCalls(); - $first = $methodCalls->first(); - assert($first instanceof ReflectionMethodCall); - - return $renamer->rename( - TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build(), - $first->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $methodCalls = $reflector->navigate(TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build())->methodCalls(); - $first = $methodCalls->first(); - self::assertEquals('newName', $first->name()); - } - ]; - } - - /** - * @return Generator - */ - private function propertyRenames(): Generator - { - yield 'property declaration private' => [ - 'member_renamer/property_declaration_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $property = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $property->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->properties()->has('newName')); - } - ]; - - yield 'property declaration protected' => [ - 'member_renamer/property_declaration_protected', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $property = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $property->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $propertyAccesses = $reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build() - )->propertyAccesses(); - $first = $propertyAccesses->first(); - self::assertEquals('newName', $first->name()); - } - ]; - - yield 'property declaration public' => [ - 'member_renamer/property_declaration_public', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $property = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $property->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $propertyAccesses = $reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build() - )->propertyAccesses(); - $first = $propertyAccesses->first(); - self::assertEquals('newName', $first->name()); - $propertyAccesses = $reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/test.php'))->build() - )->propertyAccesses(); - $first = $propertyAccesses->first(); - self::assertEquals('newName', $first->name()); - } - ]; - - yield 'attributed property declaration public' => [ - 'member_renamer/property_declaration_public', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $property = $reflection->properties()->get('found'); - - return $renamer->rename( - $reflection->sourceCode(), - $property->nameRange()->start(), - 'results' - ); - }, - function (Reflector $reflector): void { - $propertyAccesses = [...$reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build() - )->propertyAccesses()]; - $property = end($propertyAccesses); - self::assertInstanceOf(ReflectionPropertyAccess::class, $property); - self::assertEquals('results', $property->name()); - - $propertyAccesses = [...$reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/test.php'))->build() - )->propertyAccesses()->getIterator()]; - $property = end($propertyAccesses); - self::assertInstanceOf(ReflectionPropertyAccess::class, $property); - self::assertEquals('results', $property->name()); - } - ]; - - yield 'property declaration generic' => [ - 'member_renamer/property_declaration_public_generic', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $property = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $property->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $propertyAccesses = $reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build() - )->propertyAccesses(); - $first = $propertyAccesses->first(); - self::assertEquals('newName', $first->name()); - $propertyAccesses = $reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/test.php'))->build() - )->propertyAccesses(); - $first = $propertyAccesses->first(); - self::assertEquals('newName', $first->name()); - } - ]; - - yield 'property promoted declaration public' => [ - 'member_renamer/property_promoted_declaration_public', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('Test\ClassOne'); - $property = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $property->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $propertyAccesses = $reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build() - )->propertyAccesses(); - $first = $propertyAccesses->first(); - self::assertEquals('newName', $first->name()); - $propertyAccesses = $reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/test.php'))->build() - )->propertyAccesses(); - $first = $propertyAccesses->first(); - self::assertEquals('newName', $first->name()); - } - ]; - - yield 'attributed promoted property declaration public' => [ - 'member_renamer/property_promoted_declaration_public', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('Test\ClassOne'); - $property = $reflection->properties()->get('depOld'); - - return $renamer->rename( - $reflection->sourceCode(), - $property->nameRange()->start(), - 'depNew' - ); - }, - function (Reflector $reflector): void { - $propertyAccesses = [...$reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build() - )->propertyAccesses()]; - $property = end($propertyAccesses); - self::assertInstanceOf(ReflectionPropertyAccess::class, $property); - self::assertEquals('depNew', $property->name()); - $propertyAccesses = [...$reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/test.php'))->build() - )->propertyAccesses()]; - $property = end($propertyAccesses); - self::assertInstanceOf(ReflectionPropertyAccess::class, $property); - self::assertEquals('depNew', $property->name()); - } - ]; - - yield 'property declaration public does not rename other members' => [ - 'member_renamer/property_declaration_public_does_not_rename_others', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $property = $reflection->properties()->get('foobar'); - - return $renamer->rename( - $reflection->sourceCode(), - $property->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->properties()->has('barfoo')); - self::assertTrue($reflection->properties()->has('bazbar')); - self::assertTrue($reflection->properties()->has('newName')); - } - ]; - } - - /** - * @return Generator - */ - private function constantRenames(): Generator - { - yield 'constant declaration private' => [ - 'member_renamer/constant_declaration_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $constant = $reflection->constants()->get('BAR'); - - return $renamer->rename( - $reflection->sourceCode(), - $constant->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->constants()->has('newName')); - } - ]; - yield 'constant declaration protected' => [ - 'member_renamer/constant_declaration_protected', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $constant = $reflection->constants()->get('BAR'); - - return $renamer->rename( - $reflection->sourceCode(), - $constant->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->constants()->has('newName')); - - $propertyAccesses = $reflector->navigate( - TextDocumentBuilder::fromUri($this->workspace()->path('project/ClassTwo.php'))->build() - )->constantAccesses(); - $first = $propertyAccesses->first(); - self::assertEquals('newName', $first->name()); - } - ]; - yield 'constant declaration public' => [ - 'member_renamer/constant_declaration_public', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $constant = $reflection->constants()->get('FOO'); - - return $renamer->rename( - $reflection->sourceCode(), - $constant->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->constants()->has('newName')); - } - ]; - yield 'attributed constant declaration public' => [ - 'member_renamer/constant_declaration_public', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectClass('ClassOne'); - $constant = $reflection->constants()->get('ZOO'); - - return $renamer->rename( - $reflection->sourceCode(), - $constant->nameRange()->start(), - 'ZŁOM' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectClass('ClassOne'); - self::assertTrue($reflection->constants()->has('ZŁOM')); - } - ]; - } - - /** - * @return Generator - */ - private function enumRenames(): Generator - { - yield 'enum case declaration private' => [ - 'member_renamer/enum_case_declaration_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectEnum('ClassOne'); - $enum = $reflection->cases()->get('BAR'); - - return $renamer->rename( - $reflection->sourceCode(), - $enum->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectEnum('ClassOne'); - self::assertTrue($reflection->cases()->has('newName')); - } - ]; - - yield 'enum attributed case declaration' => [ - 'member_renamer/enum_case_declaration_private', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectEnum('ClassOne'); - $enum = $reflection->cases()->get('BAZ'); - - return $renamer->rename( - $reflection->sourceCode(), - $enum->nameRange()->start(), - 'newName' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectEnum('ClassOne'); - self::assertTrue($reflection->cases()->has('newName')); - } - ]; - } - - /** - * @return Generator - */ - private function traitRenames(): Generator - { - yield 'trait use with insteadof' => [ - 'member_renamer/trait_insteadof', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectTrait('A'); - $method = $reflection->methods()->get('smallTalk'); - - return $renamer->rename( - $reflection->sourceCode(), - $method->nameRange()->start(), - 'foobar' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectTrait('A'); - self::assertTrue($reflection->methods()->has('foobar')); - } - ]; - yield 'trait use with insteadof rename alias' => [ - 'member_renamer/trait_insteadof', - function (Reflector $reflector, Renamer $renamer): Generator { - $reflection = $reflector->reflectTrait('A'); - - return $renamer->rename( - $reflection->sourceCode(), - ByteOffset::fromInt(311), - 'foobar' - ); - }, - function (Reflector $reflector): void { - $reflection = $reflector->reflectTrait('A'); - self::assertTrue($reflection->methods()->has('foobar')); - } - ]; - } -} diff --git a/lib/Rename/Tests/Model/Renamer/ChainRenamerTest.php b/lib/Rename/Tests/Model/Renamer/ChainRenamerTest.php deleted file mode 100644 index 310d376500..0000000000 --- a/lib/Rename/Tests/Model/Renamer/ChainRenamerTest.php +++ /dev/null @@ -1,81 +0,0 @@ -assertResolvesRangeAndResults([], null, []); - } - - public function testGetFirstNonNullRename(): void - { - $range1 = ByteOffsetRange::fromInts(0, 1); - $results1 = [ - new LocatedTextEdit(TextDocumentUri::fromString('/foo/bar'), TextEdit::create(1, 1, 'foo')) - ]; - $renamer1 = new InMemoryRenamer($range1, $results1); - $renamer2 = new InMemoryRenamer(null, []); - - $this->assertResolvesRangeAndResults([$renamer2, $renamer1], $range1, $results1); - $this->assertResolvesRangeAndResults([$renamer1, $renamer2], $range1, $results1); - } - - public function testFirstRenameForTwoCapableRenamers(): void - { - $range1 = ByteOffsetRange::fromInts(0, 1); - $range2 = ByteOffsetRange::fromInts(0, 1); - $results2 = [ - new LocatedTextEdit(TextDocumentUri::fromString('/foo/bar'), TextEdit::create(1, 1, 'foo')) - ]; - $renamer1 = new InMemoryRenamer($range1, []); - $renamer2 = new InMemoryRenamer($range2, $results2); - - $this->assertResolvesRangeAndResults([$renamer2, $renamer1], $range2, $results2); - } - - /** - * @param Renamer[] $renamers - * @param array $expectedResults - */ - private function assertResolvesRangeAndResults( - array $renamers, - ?ByteOffsetRange $expectedRange, - array $expectedResults - ): void { - $textDocument = TextDocumentBuilder::create('text')->uri('file:///test1')->build(); - $byteOffset = ByteOffset::fromInt(0); - - $this->assertSame( - $expectedRange, - $this->createRenamer($renamers)->getRenameRange($textDocument, $byteOffset), - 'Returns expected range', - ); - $this->assertSame( - $expectedResults, - iterator_to_array($this->createRenamer($renamers)->rename($textDocument, $byteOffset, 'foobar')), - 'Returns expected results', - ); - } - - /** - * @param Renamer[] $renamers - */ - private function createRenamer(array $renamers): ChainRenamer - { - return new ChainRenamer($renamers); - } -} diff --git a/lib/Rename/Tests/RenamerTestCase.php b/lib/Rename/Tests/RenamerTestCase.php deleted file mode 100644 index 2951d2de2b..0000000000 --- a/lib/Rename/Tests/RenamerTestCase.php +++ /dev/null @@ -1,95 +0,0 @@ -workspace()->reset(); - $this->workspace()->mkdir('project'); - $this->reflector = ReflectorBuilder::create() - ->addLocator(new BruteForceSourceLocator(ReflectorBuilder::create()->build(), $this->workspace()->path('project'))) - ->build(); - $this->indexAgent = IndexAgentBuilder::create( - $this->workspace()->path('index'), - $this->workspace()->path('project') - )->setReferenceEnhancer(new WorseRecordReferenceEnhancer( - $this->reflector, - new NullLogger(), - new FilesystemTextDocumentLocator(), - ))->buildAgent(); - } - - /** - * @param Closure(Reflector,Renamer):Generator $operation - */ - #[DataProvider('provideRename')] - public function testRename(string $path, Closure $operation, Closure $assertion): void - { - $basePath = __DIR__ . '/Cases/' . $path; - - - if (!file_exists($basePath)) { - throw new RuntimeException(sprintf( - 'Case path "%s" does not yet exist', - $basePath - )); - } - - - foreach ((array)glob($basePath . '/**.ph') as $path) { - $this->workspace()->put( - 'project/' . ((string)substr((string)$path, strlen($basePath))) . 'p', - (string)file_get_contents((string)$path) - ); - } - $this->indexAgent->indexer()->getJob()->run(); - - $generator = $operation->bindTo($this)->__invoke($this->reflector, $this->createRenamer()); - assert(is_iterable($generator)); - /** @phpstan-ignore argument.type */ - $edits = LocatedTextEdits::fromLocatedEditsToCollection(iterator_to_array($generator, false)); - foreach ($edits as $documentEdits) { - file_put_contents( - $documentEdits->documentUri()->path(), - $documentEdits->textEdits()->apply((string)file_get_contents($documentEdits->documentUri()->path())) - ); - } - - $process = Process::fromShellCommandline(PHP_BINARY . ' ' . $this->workspace()->path('project/test.php')); - $process->mustRun(); - $assertion($this->reflector); - } - - protected function workspace(): Workspace - { - return new Workspace(__DIR__ . '/Workspace'); - } - - abstract protected function createRenamer(): Renamer; -} diff --git a/lib/TextDocument/ByteOffset.php b/lib/TextDocument/ByteOffset.php deleted file mode 100644 index 9403ef5501..0000000000 --- a/lib/TextDocument/ByteOffset.php +++ /dev/null @@ -1,57 +0,0 @@ -offset = $offset; - } - - public static function fromInt(int $offset): self - { - return new self($offset); - } - - public static function fromUnknown(ByteOffset|int $value): self - { - if ($value instanceof ByteOffset) { - return $value; - } - - return self::fromInt($value); - } - - /** - * @param int|ByteOffset $offset - */ - public static function fromIntOrByteOffset($offset): self - { - if ($offset instanceof ByteOffset) { - return $offset; - } - - return self::fromInt($offset); - } - - public function toInt(): int - { - return $this->offset; - } - - public function add(int $amount): self - { - return new self($this->offset + $amount); - } -} diff --git a/lib/TextDocument/ByteOffsetRange.php b/lib/TextDocument/ByteOffsetRange.php deleted file mode 100644 index 1f73945159..0000000000 --- a/lib/TextDocument/ByteOffsetRange.php +++ /dev/null @@ -1,45 +0,0 @@ -start; - } - - public function length(): int - { - return $this->end->toInt() - $this->start->toInt(); - } - - public function end(): ByteOffset - { - return $this->end; - } -} diff --git a/lib/TextDocument/EfficientLineCols.php b/lib/TextDocument/EfficientLineCols.php deleted file mode 100644 index ebdc6f57ee..0000000000 --- a/lib/TextDocument/EfficientLineCols.php +++ /dev/null @@ -1,111 +0,0 @@ - $positions - */ - private function __construct(private array $positions) - { - } - - /** - * Initialize the converter with the list of byte offsets to be converted. - * - * For converting to LSP positions there is a flag to return the character - * offset. Note that, unlike the line/col number, this is 1 based and is based - * on UTF-16 code units. - * - * @param list $byteOffsetInts - */ - public static function fromByteOffsetInts( - string $text, - array $byteOffsetInts, - bool $lspPosition = false - ): self { - sort($byteOffsetInts); - - $lines = preg_split('{(' . LineCol::NEWLINE_PATTERN . ')}', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - - if (false === $lines) { - throw new RuntimeException( - 'Failed to preg-split text into lines' - ); - } - - $offset = 0; - $lineNb = 0; - $byteOffset = array_shift($byteOffsetInts); - if (null === $byteOffset) { - return new EfficientLineCols([]); - } - $positions = []; - - foreach ($lines as $lineOrDelim) { - $lineOrDelim = (string)$lineOrDelim; - - if ((bool)preg_match('{(' . LineCol::NEWLINE_PATTERN . ')}', (string)$lineOrDelim)) { - $offset += strlen($lineOrDelim); - continue; - } - $lineNb++; - - $start = $offset; - $end = $offset + strlen($lineOrDelim); - - while ($byteOffset >= $start && $byteOffset <= $end) { - $section = substr( - $lineOrDelim, - 0, - $byteOffset - $start - ); - - if ($lspPosition) { - $utf16 = \mb_convert_encoding($section, 'UTF-16', 'UTF-8'); - $positions[$byteOffset] = new LineCol( - $lineNb, - intval(strlen($utf16) / 2) + 1, - ); - } else { - $positions[$byteOffset] = new LineCol( - $lineNb, - strlen($section) + 1, - ); - } - $byteOffset = array_shift($byteOffsetInts); - if (null === $byteOffset) { - break; - } - } - - $offset = $end; - } - - /** @var array $positions */ - return new EfficientLineCols($positions); - } - - public function get(int $offset): LineCol - { - if (!isset($this->positions[$offset])) { - throw new OutOfBoundsException(sprintf( - 'Pre-computed position not known for offset: %s', - $offset - )); - } - - return $this->positions[$offset]; - } -} diff --git a/lib/TextDocument/Exception/InvalidByteOffset.php b/lib/TextDocument/Exception/InvalidByteOffset.php deleted file mode 100644 index 250492b944..0000000000 --- a/lib/TextDocument/Exception/InvalidByteOffset.php +++ /dev/null @@ -1,9 +0,0 @@ -__toString())) { - throw TextDocumentNotFound::fromUri($uri); - } - - return TextDocumentBuilder::fromUri($uri->__toString())->build(); - } -} diff --git a/lib/TextDocument/LineCol.php b/lib/TextDocument/LineCol.php deleted file mode 100644 index e10924b71b..0000000000 --- a/lib/TextDocument/LineCol.php +++ /dev/null @@ -1,144 +0,0 @@ -line = $line; - $this->col = $col; - } - - public function toByteOffset(string $text): ByteOffset - { - $linesAndDelims = (array)preg_split('{(' . self::NEWLINE_PATTERN . ')}', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - - if (count($linesAndDelims) === 0) { - return ByteOffset::fromInt( - strlen((string)reset($linesAndDelims)) - ); - } - - $lineNb = 1; - $offset = 0; - foreach ($linesAndDelims as $lineOrDelim) { - $lineOrDelim = (string)$lineOrDelim; - - if ((bool)preg_match('{(' . self::NEWLINE_PATTERN . ')}', (string)$lineOrDelim)) { - $lineNb++; - $offset += strlen($lineOrDelim); - continue; - } - - if ($lineNb === $this->line()) { - $lineSection = mb_substr( - $lineOrDelim, - 0, - $this->col() - 1 - ); - return ByteOffset::fromInt( - $offset + (int)strlen($lineSection) - ); - } - - $offset += strlen((string)$lineOrDelim); - } - - return ByteOffset::fromInt(strlen($text)); - } - - public static function fromByteOffset(string $text, ByteOffset $byteOffset, bool $lspPosition = false): self - { - if ($byteOffset->toInt() > strlen($text)) { - $byteOffset = ByteOffset::fromInt(strlen($text)); - } - - $lines = preg_split('{(' . self::NEWLINE_PATTERN . ')}', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - - if (false === $lines) { - throw new RuntimeException( - 'Failed to preg-split text into lines' - ); - } - - $offset = 0; - $lineNb = 0; - foreach ($lines as $lineOrDelim) { - $lineOrDelim = (string)$lineOrDelim; - - if ((bool)preg_match('{(' . self::NEWLINE_PATTERN . ')}', (string)$lineOrDelim)) { - $offset += strlen($lineOrDelim); - continue; - } - $lineNb++; - - $start = $offset; - $end = $offset + strlen($lineOrDelim); - - // if the offset is in line... - if ($byteOffset->toInt() >= $start && $byteOffset->toInt() <= $end) { - $section = substr( - $lineOrDelim, - 0, - $byteOffset->toInt() - $start - ); - if ($lspPosition) { - $utf16 = \mb_convert_encoding($section, 'UTF-16', 'UTF-8'); - return new self( - $lineNb, - intval(strlen($utf16) / 2) + 1, - ); - } - - return new self($lineNb, mb_strlen($section) + 1); - } - - $offset = $end; - } - - throw new OutOfBoundsException(sprintf( - 'Byte offset %s is larger than text length %s', - $byteOffset->toInt(), - strlen($text) - )); - } - - public function col(): int - { - return $this->col; - } - - public function line(): int - { - return $this->line; - } -} diff --git a/lib/TextDocument/LineColRange.php b/lib/TextDocument/LineColRange.php deleted file mode 100644 index 739a9a27c4..0000000000 --- a/lib/TextDocument/LineColRange.php +++ /dev/null @@ -1,22 +0,0 @@ -start; - } - - public function end(): LineCol - { - return $this->end; - } -} diff --git a/lib/TextDocument/Location.php b/lib/TextDocument/Location.php deleted file mode 100644 index 25abd71869..0000000000 --- a/lib/TextDocument/Location.php +++ /dev/null @@ -1,30 +0,0 @@ -uri; - } - - public function range(): ByteOffsetRange - { - return $this->range; - } -} diff --git a/lib/TextDocument/Locations.php b/lib/TextDocument/Locations.php deleted file mode 100644 index 573a320492..0000000000 --- a/lib/TextDocument/Locations.php +++ /dev/null @@ -1,86 +0,0 @@ - - */ -final class Locations implements IteratorAggregate, Countable -{ - /** - * @var Location[] - */ - private array $locations = []; - - /** - * @param iterable $locations - */ - public function __construct(iterable $locations) - { - foreach ($locations as $location) { - $this->add($location); - } - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->locations); - } - - public function append(Locations $locations): self - { - $newLocations = $this->locations; - foreach ($locations as $location) { - $newLocations[] = $location; - } - - return new self($newLocations); - } - - public function count(): int - { - return count($this->locations); - } - - public function first(): Location - { - if (count($this->locations) === 0) { - throw new RuntimeException( - 'There are no locations in this collection' - ); - } - - return reset($this->locations); - } - - public function sorted(): self - { - $sortedLocations = $this->locations; - - usort($sortedLocations, function (Location $first, Location $second) { - $order = strcmp((string) $first->uri(), (string) $second->uri()); - if (0 !== $order) { - return $order; - } - - return $first->range()->start()->toInt() - $second->range()->start()->toInt(); - }); - - return new self($sortedLocations); - } - - private function add(Location $location): self - { - $this->locations[] = $location; - - return $this; - } -} diff --git a/lib/TextDocument/StandardTextDocument.php b/lib/TextDocument/StandardTextDocument.php deleted file mode 100644 index 5ef37a7bbc..0000000000 --- a/lib/TextDocument/StandardTextDocument.php +++ /dev/null @@ -1,42 +0,0 @@ -text; - } - - - public function uri(): ?TextDocumentUri - { - return $this->uri; - } - - - public function language(): TextDocumentLanguage - { - return $this->language; - } - - public function uriOrThrow(): TextDocumentUri - { - if (null === $this->uri) { - throw new RuntimeException( - 'Document has no URI' - ); - } - return $this->uri; - } -} diff --git a/lib/TextDocument/Tests/Benchmark/EfficientLineColsBench.php b/lib/TextDocument/Tests/Benchmark/EfficientLineColsBench.php deleted file mode 100644 index e33274f74a..0000000000 --- a/lib/TextDocument/Tests/Benchmark/EfficientLineColsBench.php +++ /dev/null @@ -1,44 +0,0 @@ -contents = (string)file_get_contents(__DIR__ . '/example/code.test'); - } - public function benchLineCols(): void - { - EfficientLineCols::fromByteOffsetInts( - $this->contents, - [89,191,274,326,373,480,552,2000], - ); - } - - public function benchLineColsUtf16Positions(): void - { - EfficientLineCols::fromByteOffsetInts( - $this->contents, - [89,191,274,326,373,480,552,2000], - true - ); - } - - public function benchIneffificentLineCols(): void - { - foreach ( - [89,191,274,326,373,480,552,2000] as $byteOffset - ) { - LineCol::fromByteOffset($this->contents, ByteOffset::fromInt($byteOffset)); - } - } -} diff --git a/lib/TextDocument/Tests/Benchmark/example/code.test b/lib/TextDocument/Tests/Benchmark/example/code.test deleted file mode 100644 index f321ce8059..0000000000 --- a/lib/TextDocument/Tests/Benchmark/example/code.test +++ /dev/null @@ -1,108 +0,0 @@ -toInt() > strlen($text)) { - $offset = ByteOffset::fromInt(strlen($text)); - } - - $lineCol = LineCol::fromByteOffset($text, $offset); - $lineAtOffset = LineAtOffset::lineAtByteOffset($text, $offset); - - $lineAtOffset = mb_substr( - $lineAtOffset, - 0, - $lineCol->col() - 1 - ); - - return new Position($lineCol->line() - 1, self::countUtf16CodeUnits($lineAtOffset)); - } -} -toInt() > strlen($text)) { - $offset = ByteOffset::fromInt(strlen($text)); - } - - $lineCol = LineCol::fromByteOffset($text, $offset); - $lineAtOffset = LineAtOffset::lineAtByteOffset($text, $offset); - - $lineAtOffset = mb_substr( - $lineAtOffset, - 0, - $lineCol->col() - 1 - ); - - return new Position($lineCol->line() - 1, self::countUtf16CodeUnits($lineAtOffset)); - } -} -toInt() > strlen($text)) { - $offset = ByteOffset::fromInt(strlen($text)); - } - - $lineCol = LineCol::fromByteOffset($text, $offset); - $lineAtOffset = LineAtOffset::lineAtByteOffset($text, $offset); - - $lineAtOffset = mb_substr( - $lineAtOffset, - 0, - $lineCol->col() - 1 - ); - - return new Position($lineCol->line() - 1, self::countUtf16CodeUnits($lineAtOffset)); - } -} -toInt() > strlen($text)) { - $offset = ByteOffset::fromInt(strlen($text)); - } - - $lineCol = LineCol::fromByteOffset($text, $offset); - $lineAtOffset = LineAtOffset::lineAtByteOffset($text, $offset); - - $lineAtOffset = mb_substr( - $lineAtOffset, - 0, - $lineCol->col() - 1 - ); - - return new Position($lineCol->line() - 1, self::countUtf16CodeUnits($lineAtOffset)); - } -} diff --git a/lib/TextDocument/Tests/Unit/ByteOffsetTest.php b/lib/TextDocument/Tests/Unit/ByteOffsetTest.php deleted file mode 100644 index 2f45c0d9be..0000000000 --- a/lib/TextDocument/Tests/Unit/ByteOffsetTest.php +++ /dev/null @@ -1,39 +0,0 @@ -assertEquals(10, $offset->toInt()); - } - - public function testAdd(): void - { - $offset = ByteOffset::fromInt(10)->add(10); - $this->assertEquals(20, $offset->toInt()); - } - - public function testExceptionOnLessThanZero1(): void - { - $this->expectException(InvalidByteOffset::class); - ByteOffset::fromInt(-1); - } - - public function testExceptionOnLessThanZero2(): void - { - $this->expectException(InvalidByteOffset::class); - ByteOffset::fromInt(-10); - } - - public function testByteOffsetIsZero(): void - { - self::assertEquals(0, ByteOffset::fromInt(0)->toInt()); - } -} diff --git a/lib/TextDocument/Tests/Unit/EfficientLineColsTest.php b/lib/TextDocument/Tests/Unit/EfficientLineColsTest.php deleted file mode 100644 index 99a035350d..0000000000 --- a/lib/TextDocument/Tests/Unit/EfficientLineColsTest.php +++ /dev/null @@ -1,90 +0,0 @@ - $offsets - */ - #[DataProvider('provideConvertOffsetsToLineCol')] - public function testFromByteOffsets(array $offsets, string $text, Closure $assertion): void - { - $converter = EfficientLineCols::fromByteOffsetInts($text, $offsets); - $assertion($converter); - } - - /** - * @return Generator - */ - public static function provideConvertOffsetsToLineCol(): Generator - { - yield [ - [], - '', - function (EfficientLineCols $lineCols): void { - self::assertInstanceOf(EfficientLineCols::class, $lineCols); - } - ]; - yield [ - [2], - '01234', - function (EfficientLineCols $lineCols): void { - self::assertEquals(3, $lineCols->get(2)->col()); - self::assertEquals(1, $lineCols->get(2)->line()); - } - ]; - yield [ - [2, 3, 0, 10], - "01234\n5678", - function (EfficientLineCols $lineCols): void { - self::assertEquals(3, $lineCols->get(2)->col()); - self::assertEquals(1, $lineCols->get(2)->line()); - self::assertEquals(2, $lineCols->get(10)->line()); - self::assertEquals(5, $lineCols->get(10)->col()); - } - ]; - } - - /** - * @param list $offsets - */ - #[DataProvider('provideConvertOffsetsToLineColAsOffset')] - public function testFromByteOffsetsAsOffset(array $offsets, string $text, Closure $assertion): void - { - $converter = EfficientLineCols::fromByteOffsetInts($text, $offsets, true); - $assertion($converter); - } - - /** - * @return Generator - */ - public static function provideConvertOffsetsToLineColAsOffset(): Generator - { - yield 'cat' => [ - [5], - 'a😸bc', - function (EfficientLineCols $lineCols): void { - self::assertEquals(4, $lineCols->get(5)->col()); - } - ]; - yield 'utf16' => [ - [46], - <<<'PHP' - get(46)->line()); - self::assertEquals(32, $lineCols->get(46)->col()); - } - ]; - } -} diff --git a/lib/TextDocument/Tests/Unit/LineColTest.php b/lib/TextDocument/Tests/Unit/LineColTest.php deleted file mode 100644 index d0298fce8d..0000000000 --- a/lib/TextDocument/Tests/Unit/LineColTest.php +++ /dev/null @@ -1,72 +0,0 @@ -assertEquals($expectedOffset, $lineCol->toByteOffset($text)->toInt()); - if ($sanityCheck) { - self::assertEquals($sanityCheck, substr($text, 0, $lineCol->toByteOffset($text)->toInt())); - } - } - - /** - * @return Generator - */ - public static function provideConvertLineColToOffset(): Generator - { - yield [ - '', - new LineCol(1, 1), - 0, - ]; - - yield [ - 'a', - new LineCol(1, 1), - 0, - ]; - - yield 'new line' => [ - "\na", - new LineCol(2, 1), - 1, - ]; - - yield 'multi-byte 1' => [ - 'ᅑa', - new LineCol(1, 2), - 3, - 'ᅑ', - ]; - - yield 'multi-byte 2' => [ - 'ᅑacd', - new LineCol(1, 3), - 4, - 'ᅑa' - ]; - - yield 'multi-byte 3' => [ - "ᅑ\nacd", - new LineCol(2, 2), - 5, - "ᅑ\na" - ]; - } - - public function testOutOfBoundsToByteOffset(): void - { - $lineCol = new LineCol(10, 20); - assert($lineCol instanceof LineCol); - self::assertEquals(13, $lineCol->toByteOffset("foobar\nbarfoo")->toInt()); - } -} diff --git a/lib/TextDocument/Tests/Unit/LocationTest.php b/lib/TextDocument/Tests/Unit/LocationTest.php deleted file mode 100644 index 82d81117cd..0000000000 --- a/lib/TextDocument/Tests/Unit/LocationTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertEquals('file:///path/to.php', $location->uri()->__toString()); - } - - public function testProvidesAccessToByteOffset(): void - { - $location = Location::fromPathAndOffsets('/path/to.php', 123, 455); - $this->assertEquals(123, $location->range()->start()->toInt()); - $this->assertEquals(455, $location->range()->end()->toInt()); - } -} diff --git a/lib/TextDocument/Tests/Unit/LocationsTest.php b/lib/TextDocument/Tests/Unit/LocationsTest.php deleted file mode 100644 index 5b9b576be4..0000000000 --- a/lib/TextDocument/Tests/Unit/LocationsTest.php +++ /dev/null @@ -1,99 +0,0 @@ -assertCount(2, $locations); - } - - public function testIsCountable(): void - { - $locations = new Locations([ - Location::fromPathAndOffsets('/path/to.php', 12, 12), - Location::fromPathAndOffsets('/path/to.php', 13, 13) - ]); - - $this->assertEquals(2, $locations->count()); - } - - public function testExceptionIfFirstNotAvailable(): void - { - $this->expectException(RuntimeException::class); - - $locations = new Locations([]); - $locations->first(); - } - - public function testAppendLocations(): void - { - $locations = new Locations([ - Location::fromPathAndOffsets('/path/to.php', 12, 19), - ]); - $locations = $locations->append(new Locations([ - Location::fromPathAndOffsets('/path/to.php', 13, 40), - ])); - - self::assertEquals(new Locations([ - Location::fromPathAndOffsets('/path/to.php', 12, 19), - Location::fromPathAndOffsets('/path/to.php', 13, 40) - ]), $locations); - } - - /** - * @param Location[] $unsortedLocationsArray - * @param Location[] $sortedLocationsArray - */ - #[DataProvider('provideUnsortedLocations')] - public function testSortLocations( - array $unsortedLocationsArray, - array $sortedLocationsArray - ): void { - $locations = new Locations($unsortedLocationsArray); - $sortedLocations = $locations->sorted(); - - $this->assertNotSame($locations, $sortedLocations); - $this->assertCount(count($unsortedLocationsArray), $sortedLocations); - - foreach (iterator_to_array($sortedLocations) as $index => $sortedLocation) { - $expectedLocation = $sortedLocationsArray[$index]; - - self::assertEquals($sortedLocation, new Location($expectedLocation->uri(), $expectedLocation->range())); - } - } - - /** - * @return Generator - */ - public static function provideUnsortedLocations(): Generator - { - yield 'Same file is sorted by start position' => [[ - Location::fromPathAndOffsets('/path/to.php', 30, 50), - Location::fromPathAndOffsets('/path/to.php', 12, 24), - ], [ - Location::fromPathAndOffsets('/path/to.php', 12, 24), - Location::fromPathAndOffsets('/path/to.php', 30, 50), - ]]; - - yield 'Sort by file name first' => [[ - Location::fromPathAndOffsets('/path/to.php', 12, 42), - Location::fromPathAndOffsets('/path/from.php', 15, 43), - ], [ - Location::fromPathAndOffsets('/path/from.php', 15, 43), - Location::fromPathAndOffsets('/path/to.php', 12, 42), - ]]; - } -} diff --git a/lib/TextDocument/Tests/Unit/TextDocumentBuilderTest.php b/lib/TextDocument/Tests/Unit/TextDocumentBuilderTest.php deleted file mode 100644 index d7fd6f8f73..0000000000 --- a/lib/TextDocument/Tests/Unit/TextDocumentBuilderTest.php +++ /dev/null @@ -1,50 +0,0 @@ -language('php')->uri(self::EXAMPLE_URI)->build(); - $this->assertEquals(self::EXAMPLE_URI, $doc->uri()->__toString()); - $this->assertEquals(self::EXAMPLE_TEXT, $doc->__toString()); - $this->assertEquals('php', $doc->language()); - } - - public function testFromUri(): void - { - $uri = (string)TextDocumentUri::fromString(__FILE__); - $doc = TextDocumentBuilder::fromUri($uri)->build(); - $this->assertEquals($uri, $doc->uri()); - $this->assertEquals(file_get_contents(__FILE__), $doc->__toString()); - } - - public function testFromTextDocument(): void - { - $doc = TextDocumentBuilder::fromTextDocument( - TextDocumentBuilder::create('foobar') - ->uri('file:///foobar/asd') - ->language('foo')->build() - )->build(); - - $this->assertEquals('foobar', $doc->__toString()); - $this->assertEquals('file:///foobar/asd', $doc->uri()->__toString()); - $this->assertEquals('/foobar/asd', $doc->uri()?->path()); - $this->assertEquals('foo', $doc->language()->__toString()); - } - - public function testExceptionOnNotExists(): void - { - $this->expectException(TextDocumentNotFound::class); - TextDocumentBuilder::fromUri('file:///no-existy'); - } -} diff --git a/lib/TextDocument/Tests/Unit/TextDocumentLanguageTest.php b/lib/TextDocument/Tests/Unit/TextDocumentLanguageTest.php deleted file mode 100644 index 67c9c90e01..0000000000 --- a/lib/TextDocument/Tests/Unit/TextDocumentLanguageTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertEquals('php', (string) $language); - $this->assertTrue($language->isDefined()); - $this->assertTrue($language->isPhp()); - $this->assertTrue($language->is('php')); - $this->assertTrue($language->is('PHP')); - $this->assertFalse($language->is('french')); - $this->assertTrue($language->in(['php', 'cobol'])); - $this->assertFalse($language->in(['c', 'cobol'])); - } - - public function testCreateUndefined(): void - { - $language = TextDocumentLanguage::undefined(); - $this->assertFalse($language->isDefined()); - } -} diff --git a/lib/TextDocument/Tests/Unit/TextDocumentLocator/ChainDocumentLocatorTest.php b/lib/TextDocument/Tests/Unit/TextDocumentLocator/ChainDocumentLocatorTest.php deleted file mode 100644 index a4126bf4e9..0000000000 --- a/lib/TextDocument/Tests/Unit/TextDocumentLocator/ChainDocumentLocatorTest.php +++ /dev/null @@ -1,60 +0,0 @@ -expectException(TextDocumentNotFound::class); - $this->createWorkspace()->get(TextDocumentUri::fromString('file:///foobar')); - } - - public function testReturnsTextDocument(): void - { - $document = TextDocumentBuilder::create('foobar')->uri('/path/to/foo')->build(); - - self::assertSame( - $document, - $this->createWorkspace([ - InMemoryDocumentLocator::fromTextDocuments([ - $document - ]) - ])->get(TextDocumentUri::fromString('file:///path/to/foo')) - ); - } - - public function testReturnsTextDocumentFromFirstWorkspace(): void - { - $document1 = TextDocumentBuilder::create('one')->uri('/path/to/foo')->build(); - $document2 = TextDocumentBuilder::create('two')->uri('/path/to/foo')->build(); - - self::assertSame( - $document1, - $this->createWorkspace([ - InMemoryDocumentLocator::fromTextDocuments([ - $document1 - ]), - InMemoryDocumentLocator::fromTextDocuments([ - $document2 - ]) - ])->get(TextDocumentUri::fromString('file:///path/to/foo')) - ); - } - - /** - * @param TextDocumentLocator[] $workspaces - */ - private function createWorkspace(array $workspaces = []): ChainDocumentLocator - { - return new ChainDocumentLocator($workspaces); - } -} diff --git a/lib/TextDocument/Tests/Unit/TextDocumentUriTest.php b/lib/TextDocument/Tests/Unit/TextDocumentUriTest.php deleted file mode 100644 index 2986431af7..0000000000 --- a/lib/TextDocument/Tests/Unit/TextDocumentUriTest.php +++ /dev/null @@ -1,108 +0,0 @@ -assertEquals('file:///foo/bar.php', (string) $uri); - - $uri = TextDocumentUri::fromString('file:///C:/foo/bar.php'); - $this->assertEquals('file:///C:/foo/bar.php', (string) $uri); - } - - public function testFromPhar(): void - { - $uri = TextDocumentUri::fromString('phar:///home/daniel/www/phpactor/phpactor/vendor/phpstan/phpstan/phpstan.phar/resources/functionMap.php'); - $this->assertEquals('phar:///home/daniel/www/phpactor/phpactor/vendor/phpstan/phpstan/phpstan.phar/resources/functionMap.php', (string) $uri); - } - - public function testFromPharWindows(): void - { - $uri = TextDocumentUri::fromString('phar://C:/zobo/vscode-phpactor/phpactor.phar/vendor/jetbrains/phpstorm-stubs\Core\Core.php'); - $this->assertEquals('phar://C:/zobo/vscode-phpactor/phpactor.phar/vendor/jetbrains/phpstorm-stubs/Core/Core.php', (string) $uri); - $uri = TextDocumentUri::fromString('phar:///C:/zobo/vscode-phpactor/phpactor.phar/vendor/jetbrains/phpstorm-stubs\Core\Core.php'); - $this->assertEquals('phar://C:/zobo/vscode-phpactor/phpactor.phar/vendor/jetbrains/phpstorm-stubs/Core/Core.php', (string) $uri); - } - - public function testExceptionOnInvalidFormatUnix(): void - { - $this->expectException(InvalidUriException::class); - TextDocumentUri::fromString('file://foo/bar.php'); - } - - public function testExceptionOnInvalidFormatWindows(): void - { - $this->expectException(InvalidUriException::class); - TextDocumentUri::fromString('file://C:/foo/bar.php'); - } - - public function testCreateUntitled(): void - { - $uri = TextDocumentUri::fromString('untitled:Untitled-1'); - $this->assertEquals('untitled:Untitled-1', (string) $uri); - } - - public function testCreatePhar(): void - { - $uri = TextDocumentUri::fromString('phar:///foo/bar.php'); - $this->assertEquals('phar:///foo/bar.php', (string) $uri); - } - - public function testNormalizesToFileScheme(): void - { - $uri = TextDocumentUri::fromString('/foo/bar.php'); - $this->assertEquals('file:///foo/bar.php', (string) $uri); - $uri = TextDocumentUri::fromString('C:/foo/bar.php'); - $this->assertEquals('file:///C:/foo/bar.php', (string) $uri); - } - - public function testExceptionOnNonAbsolutePath(): void - { - $this->expectException(InvalidUriException::class); - TextDocumentUri::fromString('i is relative'); - } - - public function testExceptionOnInvalidUri(): void - { - $this->expectException(InvalidUriException::class); - $this->expectExceptionMessage('not parse'); - TextDocumentUri::fromString(''); - } - - public function testExceptionOnNoPath(): void - { - $this->expectException(InvalidUriException::class); - $this->expectExceptionMessage('has no path'); - TextDocumentUri::fromString('file://'); - } - - public function testFromHttpUri(): void - { - $this->expectException(InvalidUriException::class); - $this->expectExceptionMessage('Only "file", "untitled", "phar" schemes are supported, got "http"'); - $uri = TextDocumentUri::fromString('http://foobar/foobar'); - } - - public function testReturnsPath(): void - { - $uri = TextDocumentUri::fromString('file:///foo/bar.php'); - $this->assertEquals('/foo/bar.php', $uri->path()); - $uri = TextDocumentUri::fromString('file:///C:/foo/bar.php'); - $this->assertEquals('C:/foo/bar.php', $uri->path()); - } - - public function testScheme(): void - { - $uri = TextDocumentUri::fromString('file:///foo/bar.php'); - $this->assertEquals('file', $uri->scheme()); - $uri = TextDocumentUri::fromString('file:///C:/foo/bar.php'); - $this->assertEquals('file', $uri->scheme()); - } -} diff --git a/lib/TextDocument/Tests/Unit/TextEditDiffTest.php b/lib/TextDocument/Tests/Unit/TextEditDiffTest.php deleted file mode 100644 index bb8179bc5e..0000000000 --- a/lib/TextDocument/Tests/Unit/TextEditDiffTest.php +++ /dev/null @@ -1,92 +0,0 @@ -diff($one, $two); - self::assertEquals( - $two, - $edits->apply($one) - ); - } - - /** - * @return Generator - */ - public static function provideDiff(): Generator - { - yield 'add string' => [ - 'foo', - 'foo bar', - ]; - yield 'remove string' => [ - 'foo bar', - 'foo', - ]; - yield 'insert string' => [ - 'foo bar', - 'foo baz boo bar bag', - ]; - - yield 'first char' => [ - 'i', - 'b', - ]; - - yield 'differnet' => [ - 'it little profits', - 'that an idle king', - ]; - - yield 'poem' => [ - implode("\n", [ - 'it little profits that an idle king', - 'matched with an aged wife', - ]), - implode("\n", [ - 'by this still hearth', - 'it little profits that an idle king', - ]) - ]; - - yield 'code' => [ - implode("\n", [ - '', - ' */', - ' public function bar() {', - ' }', - '}' - ]), - implode("\n", [ - ' $bar', - ' * @return array', - ' */', - ' public function bar(array $bar) {', - ' }', - '}' - ]), - ]; - } -} diff --git a/lib/TextDocument/Tests/Unit/TextEditTest.php b/lib/TextDocument/Tests/Unit/TextEditTest.php deleted file mode 100644 index b486f56ce0..0000000000 --- a/lib/TextDocument/Tests/Unit/TextEditTest.php +++ /dev/null @@ -1,38 +0,0 @@ -expectException(OutOfRangeException::class); - TextEdit::create(10, -10, 'asd'); - } - - public function testExceptionIfLengthIsNegative2(): void - { - $this->expectException(OutOfRangeException::class); - TextEdit::create(10, -1, 'asd'); - } - - public function testLengthIsZero(): void - { - self::assertEquals(0, TextEdit::create(10, 0, 'asd')->length()); - } - - public function testReturnLength(): void - { - self::assertEquals(1, TextEdit::create(10, 1, 'asd')->length()); - } - - public function testCreateWithByteOffset(): void - { - self::assertEquals(1, TextEdit::create(ByteOffset::fromInt(10), 1, 'asd')->length()); - } -} diff --git a/lib/TextDocument/Tests/Unit/TextEditsTest.php b/lib/TextDocument/Tests/Unit/TextEditsTest.php deleted file mode 100644 index 4509cfbd4a..0000000000 --- a/lib/TextDocument/Tests/Unit/TextEditsTest.php +++ /dev/null @@ -1,197 +0,0 @@ -merge( - TextEdits::fromTextEdits($edits2) - ) - ); - } - - /** - * @return Generator, array, array}> - */ - public static function provideMerge(): Generator - { - yield 'empty' => [ - [ - ], - [ - ], - [ - ], - ]; - - yield 'empty merge does not affect existing data' => [ - [ - TextEdit::create(1, 5, 'foobar'), - TextEdit::create(2, 5, 'foobar'), - ], - [ - ], - [ - TextEdit::create(1, 5, 'foobar'), - TextEdit::create(2, 5, 'foobar'), - ], - ]; - - yield 'original edits are ordered before subsequent edits with same offset' => [ - [ - TextEdit::create(1, 5, 'foobar'), - TextEdit::create(2, 5, 'foobar'), - ], - [ - TextEdit::create(1, 5, 'barfoo'), - TextEdit::create(2, 5, 'barfoo'), - ], - [ - TextEdit::create(1, 5, 'foobar'), - TextEdit::create(1, 5, 'barfoo'), - TextEdit::create(2, 5, 'foobar'), - TextEdit::create(2, 5, 'barfoo'), - ], - ]; - - yield 'text edits are sorted' => [ - [ - TextEdit::create(2, 5, 'foobar'), - TextEdit::create(3, 5, 'foobar'), - ], - [ - TextEdit::create(1, 5, 'barfoo'), - TextEdit::create(2, 5, 'barfoo'), - ], - [ - TextEdit::create(1, 5, 'barfoo'), - TextEdit::create(2, 5, 'foobar'), - TextEdit::create(2, 5, 'barfoo'), - TextEdit::create(3, 5, 'foobar'), - ], - ]; - } - - #[DataProvider('provideApplyTextEdits')] - public function testApplyTextEdits(string $source, TextEdits $textEdits, string $expected): void - { - self::assertEquals( - $expected, - $textEdits->apply($source) - ); - } - - /** - * @return Generator - */ - public static function provideApplyTextEdits(): Generator - { - yield 'nothing' => [ - '', - TextEdits::none(), - '' - ]; - - yield 'insert' => [ - '', - TextEdits::one(TextEdit::create(0, 0, 'hello')), - 'hello' - ]; - - yield 'delete' => [ - 'delete', - TextEdits::one(TextEdit::create(0, 6, '')), - '' - ]; - - yield 'replace' => [ - 'delete', - TextEdits::one(TextEdit::create(0, 6, 'foobar')), - 'foobar' - ]; - - yield 'multiple edits at same offset' => [ - 'hello ', - TextEdits::fromTextEdits([ - TextEdit::create(6, 0, 'world'), - TextEdit::create(6, 0, ' how'), - TextEdit::create(6, 0, ' you'), - TextEdit::create(6, 0, ' do'), - ]), - 'hello world how you do' - ]; - } - - #[DataProvider('provideApplyTextEditsErrors')] - public function testApplyTextEditsErrors(string $source, TextEdits $textEdits, string $expectedMessage): void - { - $this->expectExceptionMessage($expectedMessage); - $textEdits->apply($source); - } - - /** - * @return Generator - */ - public static function provideApplyTextEditsErrors(): Generator - { - yield 'shows debug information' => [ - 'hello ', - TextEdits::fromTextEdits([ - TextEdit::create(1, 4, 'world'), - TextEdit::create(2, 8, ' how'), - ]), - '> 1 5 "world"', - ]; - - yield 'overlapping text edits disallowed' => [ - 'hello ', - TextEdits::fromTextEdits([ - TextEdit::create(1, 4, 'world'), - TextEdit::create(2, 8, ' how'), - ]), - 'Overlapping', - ]; - - yield 'out of bounds text edit' => [ - 'hello', - TextEdits::fromTextEdits([ - TextEdit::create(10, 4, 'world'), - ]), - 'Text edit end', - ]; - } - - public function testCreateOneConstructor(): void - { - self::assertInstanceOf(TextEdits::class, TextEdits::one(TextEdit::create(10, 10, 'f'))); - } - - public function testCreateNoneConstructor(): void - { - self::assertInstanceOf(TextEdits::class, TextEdits::none()); - } - - public function testAddTextEdit(): void - { - self::assertEquals( - TextEdits::none()->add(TextEdit::create(10, 10, 'asd')), - TextEdits::one(TextEdit::create(10, 10, 'asd')) - ); - } -} diff --git a/lib/TextDocument/Tests/Unit/Util/LineAtOffsetTest.php b/lib/TextDocument/Tests/Unit/Util/LineAtOffsetTest.php deleted file mode 100644 index a9765662f9..0000000000 --- a/lib/TextDocument/Tests/Unit/Util/LineAtOffsetTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - */ - public static function provideLineAtOffset(): Generator - { - yield [ - 'hello thi<>s is', - 'hello this is', - ]; - yield 'first char' => [ - 'h<>ello this is', - 'hello this is', - ]; - yield 'last char' => [ - 'hello this is<>', - 'hello this is', - ]; - yield 'offset is newline' => [ - "hello this is\n<>", - 'hello this is', - ]; - yield [ - <<<'EOT' - s is my line - - Thanks - EOT - , 'This is my line', - ]; - yield 'multibyte 1' => [ - <<<'EOT' - 注字 / 轉注字 - - Thanks - EOT - , '转注字 / 轉注字', - ]; - yield 'multibyte 2' => [ - <<<'EOT' - - - Thanks - EOT - , '转注字 / 轉注字', - ]; - } - - public function testOutOfRange(): void - { - $this->expectException(OutOfBoundsException::class); - (new LineAtOffset())('a', 2); - } -} diff --git a/lib/TextDocument/Tests/Unit/Util/LineColRangeForLineTest.php b/lib/TextDocument/Tests/Unit/Util/LineColRangeForLineTest.php deleted file mode 100644 index daebb55245..0000000000 --- a/lib/TextDocument/Tests/Unit/Util/LineColRangeForLineTest.php +++ /dev/null @@ -1,76 +0,0 @@ -rangeFromLine($text, $lineNo)); - } - - /** - * @return Generator - */ - public static function provideRangeForLine(): Generator - { - yield [ - 'one', - 1, - new LineColRange( - new LineCol(1, 1), - new LineCol(1, 3), - ) - ]; - yield [ - ' one', - 1, - new LineColRange( - new LineCol(1, 3), - new LineCol(1, 5), - ) - ]; - yield [ - ' one ', - 1, - new LineColRange( - new LineCol(1, 3), - new LineCol(1, 5), - ) - ]; - yield [ - " one \n two \n", - 2, - new LineColRange( - new LineCol(2, 3), - new LineCol(2, 5), - ) - ]; - - yield 'empty line' => [ - " one \n two \n", - 3, - new LineColRange( - new LineCol(3, 1), - new LineCol(3, 1), - ) - ]; - - yield 'out of range' => [ - " one \n two \n", - 4, - new LineColRange( - new LineCol(4, 1), - new LineCol(4, 1), - ) - ]; - } -} diff --git a/lib/TextDocument/Tests/Unit/Util/WordAtOffsetTest.php b/lib/TextDocument/Tests/Unit/Util/WordAtOffsetTest.php deleted file mode 100644 index 08762999b7..0000000000 --- a/lib/TextDocument/Tests/Unit/Util/WordAtOffsetTest.php +++ /dev/null @@ -1,117 +0,0 @@ -assertEquals($expectedWord, (new WordAtOffset($split))($text, $offset)); - } - - /** - * @return Generator - */ - public static function provideWordAtOffset(): Generator - { - yield 'middle word' => [ - 'hello thi<>s is', - 'this', - ]; - - yield 'first word' => [ - 'h<>ello this is', - 'hello', - ]; - yield 'last word' => [ - 'hello this i<>s', - 'is', - ]; - yield 'last position' => [ - 'hello this is<>', - 'is', - ]; - yield 'after last word' => [ - 'hello this is <>', - ' ', - ]; - yield 'before word' => [ - 'hello this <>is', - ' ', - ]; - yield 'with newline' => [ - "hello this is\nsom<>ething", - 'something', - ]; - yield 'first offset only' => [ - " <> hello this is\nsom<>ething", - ' ', - ]; - yield 'trailing semicolons' => [ - 'Reque<>st;', - 'Request', - ]; - yield 'namespaced' => [ - "Foobar\Reque<>st;", - 'Request', - ]; - yield 'qualified name' => [ - "Foobar\Reque<>st;", - 'Foobar\Request', - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ]; - yield 'nullable type' => [ - '?Reque<>st;', - 'Request', - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ]; - yield 'trailing comma' => [ - 'Reque<>st,', - 'Request', - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ]; - yield 'pipe type separator' => [ - 'Reque<>st|null,', - 'Request', - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ]; - yield 'annotations' => [ - '@Reque<>st', - 'Request', - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ]; - yield 'subannotations (removing equal)' => [ - '* input=Re<>quest::class', - 'Request', - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ]; - yield 'templated type' => [ - 'arrayquest>', - 'Request', - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ]; - yield 'constant' => [ - <<<'EOT' - /** - * @SWG\Post( - * @SWG\Response( - * response=Resp<>onse::HTTP_OK, - * description="Reset password sent successfully" - * ) - */ - EOT - , 'Response', - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ]; - } -} diff --git a/lib/TextDocument/TextDocument.php b/lib/TextDocument/TextDocument.php deleted file mode 100644 index dc893a36d8..0000000000 --- a/lib/TextDocument/TextDocument.php +++ /dev/null @@ -1,27 +0,0 @@ -uri = $uri; - $new->language = TextDocumentLanguage::fromString($language); - - return $new; - } - - public static function fromTextDocument(TextDocument $document): self - { - $new = new self($document->__toString()); - $new->uri = $document->uri(); - $new->language = $document->language(); - - return $new; - } - - public function uri(string $uri): self - { - $this->uri = TextDocumentUri::fromString($uri); - - return $this; - } - - public function language(string $language): self - { - $this->language = TextDocumentLanguage::fromString($language); - - return $this; - } - - public function text(string $text): self - { - $this->text = $text; - - return $this; - } - - public function build(): TextDocument - { - return new StandardTextDocument( - $this->language ?? TextDocumentLanguage::undefined(), - $this->text, - $this->uri - ); - } - - /** - * @deprecated this method encourages the creation of documents without the URI. - */ - public static function fromUnknown(TextDocument|string $sourceCode): TextDocument - { - if ($sourceCode instanceof TextDocument) { - return $sourceCode; - } - - return self::create($sourceCode)->build(); - } - - public static function empty(): TextDocument - { - return self::create('')->build(); - } - - public static function fromPathAndString(string $path, string $string): TextDocument - { - return self::create($string)->uri($path)->build(); - } - - public static function fromString(string $string): TextDocument - { - return self::create($string)->build(); - } -} diff --git a/lib/TextDocument/TextDocumentEdits.php b/lib/TextDocument/TextDocumentEdits.php deleted file mode 100644 index 1651d2e609..0000000000 --- a/lib/TextDocument/TextDocumentEdits.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ -class TextDocumentEdits implements IteratorAggregate -{ - public function __construct( - private TextDocumentUri $uri, - private TextEdits $textEdits - ) { - } - - public static function fromTextDocument(TextDocument $textDocument, TextEdits $edits): self - { - return new self( - $textDocument->uriOrThrow(), - $edits - ); - } - - public function uri(): TextDocumentUri - { - return $this->uri; - } - - public function textEdits(): TextEdits - { - return $this->textEdits; - } - /** - * @return Iterator - */ - public function getIterator(): Iterator - { - return $this->textEdits->getIterator(); - } -} diff --git a/lib/TextDocument/TextDocumentLanguage.php b/lib/TextDocument/TextDocumentLanguage.php deleted file mode 100644 index 4c4c8bea98..0000000000 --- a/lib/TextDocument/TextDocumentLanguage.php +++ /dev/null @@ -1,51 +0,0 @@ -language; - } - - public static function fromString(string $language): self - { - return new self($language); - } - - public static function undefined(): self - { - return new self(self::LANGUAGE_UNDEFINED); - } - - public function is(string $language): bool - { - return $this->language === strtolower($language); - } - - /** - * @param array $languages - */ - public function in(array $languages): bool - { - return in_array($this->language, $languages); - } - - public function isDefined(): bool - { - return !$this->is(self::LANGUAGE_UNDEFINED); - } - - public function isPhp(): bool - { - return $this->is(self::LANGUAGE_PHP); - } -} diff --git a/lib/TextDocument/TextDocumentLocator.php b/lib/TextDocument/TextDocumentLocator.php deleted file mode 100644 index 972925aee6..0000000000 --- a/lib/TextDocument/TextDocumentLocator.php +++ /dev/null @@ -1,15 +0,0 @@ -locators as $workspace) { - try { - return $workspace->get($uri); - } catch (TextDocumentNotFound) { - } - } - - throw TextDocumentNotFound::fromUri($uri); - } -} diff --git a/lib/TextDocument/TextDocumentLocator/InMemoryDocumentLocator.php b/lib/TextDocument/TextDocumentLocator/InMemoryDocumentLocator.php deleted file mode 100644 index 72f02b810a..0000000000 --- a/lib/TextDocument/TextDocumentLocator/InMemoryDocumentLocator.php +++ /dev/null @@ -1,42 +0,0 @@ - $documents - */ - private function __construct(private array $documents) - { - } - - public function get(TextDocumentUri $uri): TextDocument - { - if (isset($this->documents[$uri->__toString()])) { - return $this->documents[$uri->__toString()]; - } - - throw TextDocumentNotFound::fromUri($uri); - } - - /** - * @param TextDocument[] $textDocuments - */ - public static function fromTextDocuments(array $textDocuments): self - { - return new self((array)array_combine(array_map(function (TextDocument $document): string { - return $document->uri()->__toString(); - }, $textDocuments), array_values($textDocuments))); - } - - public static function new(): self - { - return new self([]); - } -} diff --git a/lib/TextDocument/TextDocumentUri.php b/lib/TextDocument/TextDocumentUri.php deleted file mode 100644 index e94902e46f..0000000000 --- a/lib/TextDocument/TextDocumentUri.php +++ /dev/null @@ -1,106 +0,0 @@ -scheme === self::SCHEME_UNTITLED) { - return sprintf('%s:%s', $this->scheme, $this->path); - } - if ($this->scheme === self::SCHEME_PHAR) { - return sprintf('%s://%s', $this->scheme, $this->path); - } - return sprintf('%s:///%s', $this->scheme, ltrim($this->path, '/')); - } - - /** - * Construct a TextDocumentUri from a URI string or a filesystem path. - */ - public static function fromString(?string $uri): self - { - if ($uri === null || $uri === '') { - throw new InvalidUriException(sprintf( - 'Could not parse_url "%s"', - $uri - )); - } - - if (str_starts_with($uri, 'untitled:')) { - return new self(self::SCHEME_UNTITLED, substr($uri, 9)); - } - - $match = preg_match('{^(?[a-z]+://)?(?.+)?}', $uri, $components, PREG_UNMATCHED_AS_NULL); - ['scheme' => $scheme, 'path' => $path] = $components; - - if ($path === null) { - throw new InvalidUriException(sprintf( - 'URI "%s" has no path component', - $uri - )); - } - - if ($scheme === null) { - // Allow this function to accept filesystem paths too (not URIs), convert to file: URIs - - if (!Path::isAbsolute($path)) { - throw new InvalidUriException(sprintf( - 'Filesystem path must be absolute, got "%s"', - $path - )); - } - - $path = Path::canonicalize($path); - return new self(self::SCHEME_FILE, $path); - } - - - $scheme = substr($scheme, 0, -3); - - if (!in_array($scheme, self::SCHEMES)) { - throw new InvalidUriException(sprintf( - 'Only "%s" schemes are supported, got "%s"', - implode('", "', self::SCHEMES), - $scheme - )); - } - - if ($scheme === self::SCHEME_FILE && !str_starts_with($path, '/')) { - throw new InvalidUriException(sprintf( - 'URI for file:// must be absolute, got "%s"', - $uri - )); - } - - if (str_starts_with($path, '/')) { - $path = substr($path, 1); - } - $path = Path::makeAbsolute($path, '/'); - return new self($scheme, $path); - } - - public function path(): string - { - return $this->path; - } - - public function scheme(): string - { - return $this->scheme; - } -} diff --git a/lib/TextDocument/TextEdit.php b/lib/TextDocument/TextEdit.php deleted file mode 100644 index 667d12f49a..0000000000 --- a/lib/TextDocument/TextEdit.php +++ /dev/null @@ -1,59 +0,0 @@ -toInt(), - $content - )); - } - - $this->start = $start; - $this->length = $length; - $this->replacement = $content; - } - - /** - * @param int|ByteOffset $start - */ - public static function create($start, int $length, string $replacement): self - { - return new self(ByteOffset::fromIntOrByteOffset($start), $length, $replacement); - } - - public function end(): ByteOffset - { - return $this->start->add($this->length); - } - - public function start(): ByteOffset - { - return $this->start; - } - - public function length(): int - { - return $this->length; - } - - public function replacement(): string - { - return $this->replacement; - } -} diff --git a/lib/TextDocument/TextEditDiff.php b/lib/TextDocument/TextEditDiff.php deleted file mode 100644 index 3cdf6f12c7..0000000000 --- a/lib/TextDocument/TextEditDiff.php +++ /dev/null @@ -1,149 +0,0 @@ -lcsTable($one, $two); - $ops = $this->resolveOps( - $table, - mb_str_split($one), - mb_str_split($two), - mb_strlen($one) - 1, - mb_strlen($two) - 1 - ); - $edits = $this->textEdits($ops); - - return $edits; - } - - /** - * @param array> $table - * @param list $x - * @param list $y - * @param list $ops - * @return list - */ - public function resolveOps(array $table, array $x, array $y, int $i, int $j, array $ops = []): array - { - if ($i > 0 && $j > 0 && $x[$i] === $y[$j]) { - $ops = $this->resolveOps($table, $x, $y, $i-1, $j-1); - $ops[] = [self::NOOP, $x[$i], $i]; - return $ops; - } - - if ($j > 0 && ($i === 0 || $table[$i][$j-1] >= $table[$i-1][$j])) { - $ops = $this->resolveOps($table, $x, $y, $i, $j-1); - $ops[] = [self::ADD, $y[$j], $i + 1]; - return $ops; - } - - if ($i > 0 && ($j === 0 || $table[$i][$j-1] < $table[$i-1][$j])) { - $ops = $this->resolveOps($table, $x, $y, $i - 1, $j); - $ops[] = [self::DEL, $x[$i], $i]; - return $ops; - } - - if ($j === 0 && $i === 0 && $x[$i] !== $y[$j]) { - $ops[] = [self::REPLACE, $y[$i], $i]; - return $ops; - } - - return $ops; - } - - /** - * @return array> - */ - private function lcsTable(string $one, string $two): array - { - $m = mb_strlen($one); - $n = mb_strlen($two); - $table = []; - - for ($i = 0; $i <= $m; $i++) { - $table[$i][0] = 0; - } - for ($j = 0; $j <= $n; $j++) { - $table[0][$j] = 0; - } - - for ($i = 1; $i <= $m; $i++) { - for ($j = 1; $j <= $n; $j++) { - if (substr($one, $i - 1, 1) === substr($two, $j - 1, 1)) { - $table[$i][$j] = $table[$i - 1][$j - 1] + 1; - } else { - $table[$i][$j] = max($table[$i][$j - 1], $table[$i - 1][$j]); - } - } - } - - return $table; - } - - /** - * @param list $ops - */ - private function textEdits(array $ops): TextEdits - { - $chunks = []; - $currentOps = []; - $currentOpName = null; - $lastOp = null; - - // chunk by operation - foreach ($ops as $op) { - $opName = $op[0]; - - if ($lastOp === null) { - $currentOps[] = $op; - } elseif ($opName != $lastOp) { - $chunks[] = $currentOps; - $currentOps = [$op]; - } else { - $currentOps[] = $op; - } - - $lastOp = $opName; - } - - if ($currentOps) { - $chunks[] = $currentOps; - } - - // covert to text edits - $edits = []; - foreach ($chunks as $chunk) { - if ($chunk[0][0] === self::ADD) { - $edits[] = TextEdit::create( - $chunk[0][2], - 0, - implode('', array_map(fn (array $op) => $op[1], $chunk)) - ); - } - if ($chunk[0][0] === self::DEL) { - $edits[] = TextEdit::create( - $chunk[0][2], - count($chunk), - '', - ); - } - if ($chunk[0][0] === self::REPLACE) { - $edits[] = TextEdit::create( - $chunk[0][2], - count($chunk), - implode('', array_map(fn (array $ops) => $ops[1], $chunk)), - ); - } - } - - return TextEdits::fromTextEdits($edits); - } -} diff --git a/lib/TextDocument/TextEdits.php b/lib/TextDocument/TextEdits.php deleted file mode 100644 index c597a88feb..0000000000 --- a/lib/TextDocument/TextEdits.php +++ /dev/null @@ -1,126 +0,0 @@ - - */ -class TextEdits implements IteratorAggregate, Countable -{ - /** - * @var TextEdit[] - */ - private array $textEdits; - - public function __construct(TextEdit ...$textEdits) - { - usort($textEdits, function (TextEdit $a, TextEdit $b) { - return $a->start() <=> $b->start(); - }); - $this->textEdits = $textEdits; - } - - public static function one(TextEdit $textEdit): self - { - return new self($textEdit); - } - - /** - * @return Iterator - */ - public function getIterator(): Iterator - { - return new ArrayIterator($this->textEdits); - } - - public static function none(): self - { - return new self(); - } - - /** - * @param array $textEdits - */ - public static function fromTextEdits(array $textEdits): self - { - return new self(...$textEdits); - } - - /** - * Merge one set of edits into this set. - * - * Edits from this set are ordered before those of the merged edits. - */ - public function merge(TextEdits $edits): self - { - return new self(...array_merge($this->textEdits, $edits->textEdits)); - } - - /** - * Apply this set of edits to the given text - */ - public function apply(string $text): string - { - $prevEditStart = PHP_INT_MAX; - - for ($i = \count($this->textEdits) - 1; $i >= 0; $i--) { - $edit = $this->textEdits[$i]; - assert($edit instanceof TextEdit); - - if ($prevEditStart < $edit->start()->toInt() || $prevEditStart < $edit->end()->toInt()) { - throw new OutOfBoundsException(sprintf( - "Overlapping text edit:\n%s", - self::renderDebugTextEdits($edit, $this->textEdits) - )); - } - - if ($edit->end()->toInt() > \strlen($text)) { - throw new OutOfBoundsException(sprintf( - 'Text edit end (%s) exceeds length of text (%s): %s', - $edit->end()->toInt(), - $edit->replacement(), - self::renderDebugTextEdits($edit, $this->textEdits) - )); - } - - $prevEditStart = $edit->start()->toInt(); - $head = \substr($text, 0, $edit->start()->toInt()); - $tail = \substr($text, $edit->end()->toInt()); - $text = $head . $edit->replacement() . $tail; - } - - return $text; - } - - public function add(TextEdit $textEdit): self - { - return new self(...array_merge($this->textEdits, [$textEdit])); - } - - public function count(): int - { - return count($this->textEdits); - } - - /** - * @param array $edits - */ - private static function renderDebugTextEdits(TextEdit $edit, array $edits): string - { - return implode("\n", array_map(function (TextEdit $otherEdit) use ($edit) { - return sprintf( - '%s%s %s "%s"', - $edit === $otherEdit ? '> ' : ' ', - $otherEdit->start()->toInt(), - $otherEdit->end()->toInt(), - str_replace("\n", '\n', $otherEdit->replacement()) - ); - }, $edits)); - } -} diff --git a/lib/TextDocument/Util/LineAtOffset.php b/lib/TextDocument/Util/LineAtOffset.php deleted file mode 100644 index 05f7f75edf..0000000000 --- a/lib/TextDocument/Util/LineAtOffset.php +++ /dev/null @@ -1,44 +0,0 @@ -= $start && $byteOffset <= $end) { - if (preg_match('{^(\r\n|\n|\r)$}', $line)) { - return $lastLine; - } - return $line; - } - $lastLine = $line; - $start = $end; - } - - throw new OutOfBoundsException(sprintf( - 'Byte offset %s is larger than text length %s', - $byteOffset, - strlen($text) - )); - } - public static function lineAtByteOffset(string $text, ByteOffset $offset): string - { - return (new self())->__invoke($text, $offset->toInt()); - } -} diff --git a/lib/TextDocument/Util/LineColRangeForLine.php b/lib/TextDocument/Util/LineColRangeForLine.php deleted file mode 100644 index 986084ef99..0000000000 --- a/lib/TextDocument/Util/LineColRangeForLine.php +++ /dev/null @@ -1,47 +0,0 @@ -splitPattern . ')}', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - - if (false === $chunks) { - throw new RuntimeException( - 'Failed to preg-split text into chunks' - ); - } - - $start = 1; - foreach ($chunks as $chunk) { - $end = $start + strlen($chunk); - if ($byteOffset >= $start && $byteOffset < $end) { - return $chunk; - } - $start = $end; - } - - throw new OutOfBoundsException(sprintf( - 'Byte offset %s is larger than text length %s', - $byteOffset, - strlen($text) - )); - } -} diff --git a/lib/TextDocument/WorkspaceEdits.php b/lib/TextDocument/WorkspaceEdits.php deleted file mode 100644 index 09a52ab73a..0000000000 --- a/lib/TextDocument/WorkspaceEdits.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -final class WorkspaceEdits implements IteratorAggregate, Countable -{ - /** - * @var TextDocumentEdits[] - */ - private array $documentEdits; - - public function __construct(TextDocumentEdits ...$documentEdits) - { - $this->documentEdits = $documentEdits; - } - /** - * @return Iterator - */ - public function getIterator(): Iterator - { - return new ArrayIterator($this->documentEdits); - } - - public static function none(): self - { - return new self(); - } - - public function count(): int - { - return count($this->documentEdits); - } -} diff --git a/lib/VersionResolver/AggregateSemVerResolver.php b/lib/VersionResolver/AggregateSemVerResolver.php deleted file mode 100644 index 87090a6dff..0000000000 --- a/lib/VersionResolver/AggregateSemVerResolver.php +++ /dev/null @@ -1,35 +0,0 @@ -resolvers = $resolvers; - } - - /** - * @return Promise - */ - public function resolve(): Promise - { - return call(function () { - foreach ($this->resolvers as $resolver) { - $version = yield $resolver->resolve(); - if (null !== $version) { - return $version; - } - } - - return null; - }); - } -} diff --git a/lib/VersionResolver/ArbitrarySemVerResolver.php b/lib/VersionResolver/ArbitrarySemVerResolver.php deleted file mode 100644 index 69ca74a164..0000000000 --- a/lib/VersionResolver/ArbitrarySemVerResolver.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ - public function resolve(): Promise - { - return new Success((null === $this->version) ? null : SemVersion::fromString($this->version)); - } -} diff --git a/lib/VersionResolver/CachedSemVerResolver.php b/lib/VersionResolver/CachedSemVerResolver.php deleted file mode 100644 index f314be54ee..0000000000 --- a/lib/VersionResolver/CachedSemVerResolver.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ - public function resolve(): Promise - { - return call(function () { - if (isset($this->version)) { - return $this->version; - } - - $this->version = yield $this->resolver->resolve(); - - if (null !== $this->version) { - $this->logger->info(sprintf( - 'resolved version "%s"', - $this->version->__toString() - )); - } - - return $this->version; - }); - } -} diff --git a/lib/VersionResolver/SemVersion.php b/lib/VersionResolver/SemVersion.php deleted file mode 100644 index a8228df6e7..0000000000 --- a/lib/VersionResolver/SemVersion.php +++ /dev/null @@ -1,33 +0,0 @@ -version; - } - - public static function fromString(string $string): self - { - return new self($string); - } - - public function greaterThanOrEqualTo(SemVersion $version): bool - { - return Comparator::greaterThanOrEqualTo($this->version, $version->__toString()); - } - - public function lessThan(SemVersion $version): bool - { - return Comparator::lessThan($this->version, $version->__toString()); - } -} diff --git a/lib/VersionResolver/SemVersionResolver.php b/lib/VersionResolver/SemVersionResolver.php deleted file mode 100644 index ac716f942c..0000000000 --- a/lib/VersionResolver/SemVersionResolver.php +++ /dev/null @@ -1,13 +0,0 @@ - - */ - public function resolve(): Promise; -} diff --git a/lib/VersionResolver/Tests/AggregateSemVerResolverTest.php b/lib/VersionResolver/Tests/AggregateSemVerResolverTest.php deleted file mode 100644 index 6af00cb6b7..0000000000 --- a/lib/VersionResolver/Tests/AggregateSemVerResolverTest.php +++ /dev/null @@ -1,46 +0,0 @@ - new ArbitrarySemVerResolver($version), - $componentVersions, - )); - - $actual = wait($resolver->resolve()); - - if (null === $expected) { - self::assertNull($actual); - return; - } - - self::assertNotNull($actual); - - self::assertSame($expected, $actual->__toString()); - } - - /** - * @return iterable> - */ - public static function provideResolverData(): iterable - { - yield 'not null first' => ['1', '1', null]; - yield 'null first' => ['1', null, '1']; - yield 'null only' => [null, null, null]; - } -} diff --git a/lib/VersionResolver/Tests/ArbitrarySemVerResolverTest.php b/lib/VersionResolver/Tests/ArbitrarySemVerResolverTest.php deleted file mode 100644 index b54575a071..0000000000 --- a/lib/VersionResolver/Tests/ArbitrarySemVerResolverTest.php +++ /dev/null @@ -1,24 +0,0 @@ -resolve()); - - self::assertNotNull($version); - self::assertSame('1', $version->__toString()); - } -} diff --git a/lib/VersionResolver/Tests/CachedSemVerResolverTest.php b/lib/VersionResolver/Tests/CachedSemVerResolverTest.php deleted file mode 100644 index e35ee909a4..0000000000 --- a/lib/VersionResolver/Tests/CachedSemVerResolverTest.php +++ /dev/null @@ -1,43 +0,0 @@ -prophesize(SemVersionResolver::class); - $resolver - ->resolve() - ->willReturn(new Success(SemVersion::fromString($version))) - ->shouldBeCalledOnce() - ; - - $logger = $this->prophesize(LoggerInterface::class); - $logger->info('resolved version "1"')->shouldBeCalledOnce(); - - $cachedResolver = new CachedSemVerResolver( - $resolver->reveal(), - $logger->reveal(), - ); - - for ($i = 1; $i <= 2; $i++) { - $actual = wait($cachedResolver->resolve()); - self::assertNotNull($actual); - self::assertSame($version, $actual->__toString()); - } - } -} diff --git a/lib/WorseReferenceFinder/Tests/DefinitionLocatorTestCase.php b/lib/WorseReferenceFinder/Tests/DefinitionLocatorTestCase.php deleted file mode 100644 index 29a69fe8c8..0000000000 --- a/lib/WorseReferenceFinder/Tests/DefinitionLocatorTestCase.php +++ /dev/null @@ -1,37 +0,0 @@ -location(), - Location::fromPathAndOffsets($this->workspace->path($path), $start, $end) - ); - } - - - protected function locate(string $manifest, string $source): TypeLocations - { - [$source, $offset] = ExtractOffset::fromSource($source); - - $documentUri = $this->workspace->path('somefile.php'); - $this->workspace->loadManifest($manifest); - return $this->locator()->locateDefinition( - TextDocumentBuilder::create($source)->uri($documentUri)->language('php')->build(), - ByteOffset::fromInt((int)$offset) - ); - } - - abstract protected function locator(): DefinitionLocator; -} diff --git a/lib/WorseReferenceFinder/Tests/IntegrationTestCase.php b/lib/WorseReferenceFinder/Tests/IntegrationTestCase.php deleted file mode 100644 index c441a8b80e..0000000000 --- a/lib/WorseReferenceFinder/Tests/IntegrationTestCase.php +++ /dev/null @@ -1,32 +0,0 @@ -workspace = Workspace::create(__DIR__ . '/Workspace'); - $this->workspace->reset(); - } - - protected function reflector(): Reflector - { - return ReflectorBuilder::create() - ->enableContextualSourceLocation() - ->addLocator(new StubSourceLocator( - ReflectorBuilder::create()->build(), - $this->workspace->path(''), - $this->workspace->path('cache') - )) - ->build(); - } -} diff --git a/lib/WorseReferenceFinder/Tests/Unit/TolerantVariableDefintionLocatorTest.php b/lib/WorseReferenceFinder/Tests/Unit/TolerantVariableDefintionLocatorTest.php deleted file mode 100644 index 505d83c045..0000000000 --- a/lib/WorseReferenceFinder/Tests/Unit/TolerantVariableDefintionLocatorTest.php +++ /dev/null @@ -1,52 +0,0 @@ -locate(<<<'EOT' - // File: Foobar.php - oo->foobar;'); - - $this->assertTypeLocation($location->first(), 'somefile.php', 6, 10); - } - - public function testVariableIsMethodArgument(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - ar->baz(); } }'); - - $this->assertTypeLocation($location->first(), 'somefile.php', 45, 50); - } - - public function testGotoFirstIfVariableNotDefined(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - ar->foobar;'); - - $this->assertTypeLocation($location->first(), 'somefile.php', 27, 31); - } - - protected function locator(): DefinitionLocator - { - return new TolerantVariableDefintionLocator( - new TolerantVariableReferenceFinder(new TolerantAstProvider(), true) - ); - } -} diff --git a/lib/WorseReferenceFinder/Tests/Unit/TolerantVariableReferenceFinderTest.php b/lib/WorseReferenceFinder/Tests/Unit/TolerantVariableReferenceFinderTest.php deleted file mode 100644 index 1d3c0bc1f0..0000000000 --- a/lib/WorseReferenceFinder/Tests/Unit/TolerantVariableReferenceFinderTest.php +++ /dev/null @@ -1,246 +0,0 @@ -offsetsFromSource($source, $uri); - $document = TextDocumentBuilder::create($source) - ->uri($uri) - ->language('php') - ->build(); - - $finder = new TolerantVariableReferenceFinder(new TolerantAstProvider(), $includeDefinition); - $generator = $finder->findReferences($document, ByteOffset::fromInt($selectionOffset)); - $actualReferences = iterator_to_array($generator, false); - - - $this->assertEquals(count($expectedReferences), count($actualReferences)); - foreach ($expectedReferences as $index => $reference) { - $this->assertEquals($reference->location()->uri(), $actualReferences[$index]->location()->uri()); - $this->assertEquals($reference->isSurely(), $actualReferences[$index]->isSurely()); - $this->assertEquals($reference->isMaybe(), $actualReferences[$index]->isMaybe()); - $this->assertEquals($reference->isNot(), $actualReferences[$index]->isNot()); - $this->assertEquals($reference->location()->range()->start(), $actualReferences[$index]->location()->range()->start()); - } - self::assertEquals($isDone, $generator->getReturn()); - } - - /** - * @return Generator - */ - public static function provideReferences(): Generator - { - yield 'not on variable' => [ - '5;', - false, - false, - ]; - - yield 'basic' => [ - 'ar1 = 5; $var2 = $var1 + 10;' - ]; - - yield 'dynamic name' => [ - 'ar1 = 5; echo $$var1;', - ]; - - yield 'function argument' => [ - 'ar1 = 5; func($var1);', - ]; - - yield 'function argument with type' => [ - 'ar1 = 5; func(string $var1);', - ]; - - yield 'global statement' => [ - 'ar1 = 5; global $var1;', - ]; - - yield 'dynamic property name' => [ - 'ar1 = 5; $obj->$var1 = 5;', - ]; - - yield 'dynamic property name (braced)' => [ - 'ar1 = 5; $obj->{$var1} = 5;', - ]; - - yield 'dynamic method name' => [ - 'ar1 = 5; $obj->$var1(5);', - ]; - - yield 'dynamic method name (braced)' => [ - 'ar1 = 5; $obj->{$var1}(5);', - ]; - - yield 'dynamic class name' => [ - 'ar1 = 5; $obj = new $var1();', - ]; - - yield 'embedded string' => [ - 'ar1 = 5; $str = "Text {$var1} more text";', - ]; - - yield 'exception in a catch clause' => [ - '$<>e) { echo $e->getMessage(); }', - true - ]; - - yield 'scope: exception in a catch clause (skip other with same names)' => [ - 'getMessage(); } try { $a = 5; } catch (Exception $e) { echo $<>e->getMessage(); }', - true - ]; - - yield 'scope: anonymous function: argument' => [ - 'ar1 = 5; $func = function($var1) { };', - ]; - - yield 'scope: anonymous function: use statement' => [ - 'ar1 = 5; $func = function() use ($var1) { };', - ]; - - yield 'static var::' => [ - 'ar4::prop1; $var4 = 12; } }', - ]; - - yield 'scope: anonymous function: inside' => [ - 'ar1 = 5; $func = function() use ($var1) { $var2 = $var1; };', - ]; - - yield 'scope: anonymous function: inside selection' => [ - '$var1) { $var2 = $v<>ar1; };', - ]; - - yield 'scope: anonymous function: only inside' => [ - 'ar1 = 5; $var2 = $var1 + 10; };', - ]; - - yield 'scope: anonymous function: only outside' => [ - 'r1 = 2; $func = function() { $var1 = 5; $var2 = $var1 + 10; }; $var2 = $var1 / 4;', - ]; - - yield 'scope: inside class method' => [ - '$v<>ar1 = 5; $var2 = $var1 + 10; } }', - ]; - - yield 'scope: inside class method: select argument' => [ - 'r1) { $var1 = 5; $var2 = $var1 + 10; } }', - ]; - - yield 'scope: inside class method: select argument definition' => [ - '$va<>r1) { $var1 = 5; $var2 = $var1 + 10; } }', - true - ]; - - yield 'scope: inside class method: inside anonumous function + use, click inside' => [ - '$var1) { $v<>ar1 = 5; $var2 = $var1 + 10; } } }', - ]; - - yield 'scope: inside class method: inside anonumous function + use, click outside' => [ - 'ar1 = 10; $f = function() use ($var1) { $var1 = 5; $var2 = $var1 + 10; } } }', - ]; - - yield 'scope: inside class method: inside anonumous function + use, click in use' => [ - '$v<>ar1) { $var1 = 5; $var2 = $var1 + 10; } } }', - ]; - - yield 'scope: inside class method: inside anonumous function (no use), click inside' => [ - '$v<>ar1 = 5; $var2 = $var1 + 10; } } }', - ]; - - yield 'scope: inside class method: inside anonumous function (no use), click outside' => [ - 'ar1 = 10; $f = function($var1) { $var1 = 5; $var2 = $var1 + 10; } } }', - ]; - - yield 'scope: inside class method: inside anonumous class method' => [ - 'ar = 1; } } '. - ' } }', - ]; - - yield 'skip: static property access' => [ - 'p1 = 5; $var4 = self::$prop1; } }', - false, - false - ]; - - yield 'skip: static property declaration' => [ - 'op1; function M1() { self::$prop1 = 5; $var4 = self::$prop1; } }', - false, - false - ]; - - yield 'skip: instance property declaration' => [ - 'op1; function M1() { $this->prop1 = 5; $var4 = $this->prop1; } }', - false, - false - ]; - - yield 'skip: promoted property' => [ - 'op1){ $this->prop1; } }', - false, - false - ]; - } - - /** @return array{string, int, array} */ - private static function offsetsFromSource(string $source, string $uri): array - { - $textDocumentUri = TextDocumentUri::fromString($uri); - $results = preg_split('/(<>|)/u', $source, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - - $referenceLocations = []; - $selectionOffset = -1; - - if (!is_array($results)) { - throw new Exception('No selection.'); - } - - $newSource = ''; - $offset = 0; - foreach ($results as $result) { - if ($result == '<>') { - $selectionOffset = $offset; - continue; - } - - if ($result == '') { - $referenceLocations[] = PotentialLocation::surely( - new Location( - $textDocumentUri, - ByteOffsetRange::fromInts($offset, $offset + mb_strlen($result)) - ) - ); - continue; - } - - $newSource .= $result; - $offset += mb_strlen($result); - } - - return [$newSource, $selectionOffset, $referenceLocations]; - } -} diff --git a/lib/WorseReferenceFinder/Tests/Unit/WorsePlainTextDefinitionLocatorTest.php b/lib/WorseReferenceFinder/Tests/Unit/WorsePlainTextDefinitionLocatorTest.php deleted file mode 100644 index 60fff0b052..0000000000 --- a/lib/WorseReferenceFinder/Tests/Unit/WorsePlainTextDefinitionLocatorTest.php +++ /dev/null @@ -1,103 +0,0 @@ -locate(<<<'EOT' - // File: Foobar.php - assertEquals($this->workspace->path($expectedPath), $location->first()->location()->uri()->path()); - } - - public function testExceptionIfCannotFindClass(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $this->expectExceptionMessage('Word "is" could not be resolved to a class'); - $this->locate('', 'Hello this i<>s '); - } - - public function testLastOffset(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $this->locate('', 'Hello this is <>'); - } - - /** - * @return Generator - */ - public static function provideGotoWord(): Generator - { - yield 'property docblock' => [ '/** @var Foob<>ar */', 'Foobar.php' ]; - yield 'fully qualified' => [ '/** @var \Barfoo\Barf<>oo */', 'Barfoo.php' ]; - yield 'qualified' => [ '/** @var Barfoo\Barf<>oo */', 'Barfoo.php' ]; - yield 'xml attribute' => [ '', 'Foobar.php' ]; - yield 'array access' => [ '[Foob<>ar::class]', 'Foobar.php' ]; - yield 'list' => [ '/** @return <>Foobar[]', 'Foobar.php' ]; - yield 'solid block of text' => [ 'Foob<>ar', 'Foobar.php' ]; - yield 'imported class 1' => [ <<<'EOT' - rfoo */ - private $hello; - } - } - EOT - , 'Barfoo.php' ]; - yield 'imported class 2' => [ <<<'EOT' - rfoo */ - EOT - , 'Barfoo.php' ]; - yield 'relative class' => [ <<<'EOT' - rfoo */ - EOT - , 'Barfoo.php' ]; - yield 'imported class' => [ <<<'EOT' - oo - */ - class Baz {} - EOT - , 'Boo.php' ]; - } - - protected function locator(): DefinitionLocator - { - return new WorsePlainTextClassDefinitionLocator($this->reflector()); - } -} diff --git a/lib/WorseReferenceFinder/Tests/Unit/WorseReflectionDefinitionLocatorTest.php b/lib/WorseReferenceFinder/Tests/Unit/WorseReflectionDefinitionLocatorTest.php deleted file mode 100644 index 19cc14d5c8..0000000000 --- a/lib/WorseReferenceFinder/Tests/Unit/WorseReflectionDefinitionLocatorTest.php +++ /dev/null @@ -1,405 +0,0 @@ -expectException(CouldNotLocateDefinition::class); - $this->expectExceptionMessage('PHP'); - - $this->locator()->locateDefinition( - TextDocumentBuilder::create('asd')->language('asd')->build(), - ByteOffset::fromInt(1234) - ); - } - - public function testExceptionOnUnresolvableSymbol(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $this->expectExceptionMessage('Do not know how'); - - [$source, $offset] = ExtractOffset::fromSource(''); - - $this->locator()->locateDefinition( - TextDocumentBuilder::create($source)->language('php')->build(), - ByteOffset::fromInt($offset) - ); - } - - public function testExceptionWhenNoContainingClass(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $this->expectExceptionMessage('No definition(s) found'); - - [$source, $offset] = ExtractOffset::fromSource('fo<>'); - - $this->locator()->locateDefinition( - TextDocumentBuilder::create($source)->language('php')->build(), - ByteOffset::fromInt($offset) - ); - } - - public function testExceptionWhenContainingClassNotFound(): void - { - $this->markTestSkipped('Cannot reproduce'); - } - - public function testExceptionWhrenClassNoPath(): void - { - $this->markTestSkipped('Cannot reproduce'); - } - - public function testExceptionWhenFunctionHasNoSourceCode(): void - { - $this->markTestSkipped('Cannot reproduce'); - } - - public function testLocatesFunction(): void - { - $location = $this->locate(<<<'EOT' - // File: file1.php - ar();'); - - $this->assertTypeLocation($location->first(), 'file1.php', 7, 28); - } - - public function testLocatesFunctionFromFirstClassCallable(): void - { - $location = $this->locate(<<<'EOT' - // File: file1.php - ar(...);'); - - $this->assertTypeLocation($location->first(), 'file1.php', 7, 28); - } - - public function testExceptionForFunctionWithNoDefinition(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $location = $this->locate(<<<'EOT' - // File: file1.php - ar();'); - } - - public function testExceptionIfMethodNotFound(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $this->expectExceptionMessage('No definition(s) found'); - $location = $this->locate(<<<'EOT' - // File: Foobar.php - b<>ar;'); - } - - public function testLocatesToMethod(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - b<>ar();'); - - $locationRange = $location->first()->location(); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 45); - } - - public function testLocatesToMethodFromFirstClassCallable(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - b<>ar(...);'); - - $locationRange = $location->first()->location(); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 45); - } - - public function testLocatesToStaticMethodFromFirstClassCallable(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - ar(...)'); - - $locationRange = $location->first()->location(); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 52); - } - - public function testLocatesToConstant(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - BAR;'); - - $locationRange = $location->first()->location(); - $this->assertEquals($this->workspace->path('Foobar.php'), (string) $locationRange->uri()->path()); - } - - public function testLocatesMethodDeclaration(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - EOT - , 'ar() {} }'); - - $this->assertTypeLocation($location->first(), 'somefile.php', 21, 45); - } - - public function testLocatesMethodDeclarationInParentClass(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - ar() {} } - EOT - , 'ar() {} }'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 30, 63); - } - - public function testLocatesPropertyInParentClass(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - ar; }'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 33); - } - - public function testLocatesMethodInInterface(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - oo() }'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 25, 47); - } - - public function testLocatesToMethodOnUnionTypeWithOneTypeMissingTheMethod(): void - { - $location = $this->locate(<<<'EOT' - // File: Factory.php - b<>ar();'); - - self::assertCount(1, $location); - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 45); - } - - public function testLocatesToMethodOnUnionTypeFromParam(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - b<>ar(); }}'); - - self::assertCount(2, $location); - self::assertEquals('Foobar', $location->atIndex(0)->type()->__toString()); - self::assertEquals('Barfoo', $location->atIndex(1)->type()->__toString()); - } - - public function testLocatesToMethodOnUnionType(): void - { - $location = $this->locate(<<<'EOT' - // File: Factory.php - b<>ar();'); - - self::assertCount(2, $location); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 45); - } - - public function testLocatesConstant(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - BAR;'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 42); - } - - public function testLocatesProperty(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - foo<>bar;'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 36); - } - - public function testLocatesGeneric(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - */public static function barfoo() {} } - EOT - , 'g<>et();'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 39, 62); - } - - public function testLocatesDeclaringClass(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - bar<>foo();'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 48); - } - - public function testLocatesNullableMethod(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - foobar()->baz<>();'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 20, 43); - } - - public function testLocatesNullableProperty(): void - { - $location = $this->locate(<<<'EOT' - // File: Foobar.php - foobar->baz<>;'); - - $this->assertTypeLocation($location->first(), 'Foobar.php', 20, 32); - } - - public function testLocatesCase(): void - { - $location = $this->locate(<<<'EOT' - // File: FoobarEnum.php - AR;'); - - $this->assertTypeLocation($location->first(), 'FoobarEnum.php', 24, 33); - } - - public function testLocatesEnumConst(): void - { - $location = $this->locate(<<<'EOT' - // File: FoobarEnum.php - BAR;'); - - $this->assertTypeLocation($location->first(), 'FoobarEnum.php', 34, 58); - } - - public function testLocatesTraitConst(): void - { - // note this isn't actually valid PHP but I'm too lazy - // to test it proeprtly #2784 - $location = $this->locate(<<<'EOT' - // File: FoobarTrait.php - BAR;'); - - $this->assertTypeLocation($location->first(), 'FoobarTrait.php', 26, 50); - } - - public function testExceptionIfPropertyIsInterface(): void - { - $this->expectException(CouldNotLocateDefinition::class); - $this->expectExceptionMessage('is an interface'); - $location = $this->locate(<<<'EOT' - // File: Foobar.php - foo<>bar;'); - - $locationRange = $location->first()->location(); - $this->assertTypeLocation($location->first(), 'Foobar.php', 21, 24); - } - - protected function locator(): DefinitionLocator - { - return new WorseReflectionDefinitionLocator($this->reflector(), new NullCache()); - } -} diff --git a/lib/WorseReferenceFinder/Tests/Unit/WorseReflectionTypeLocatorTest.php b/lib/WorseReferenceFinder/Tests/Unit/WorseReflectionTypeLocatorTest.php deleted file mode 100644 index 8758a02fc3..0000000000 --- a/lib/WorseReferenceFinder/Tests/Unit/WorseReflectionTypeLocatorTest.php +++ /dev/null @@ -1,217 +0,0 @@ -locate( - <<<'EOT' - // File: One.php - // o<>ne; - } - } - EOT - ); - - self::assertEquals('One', $typeLocations->first()->type()->__toString()); - self::assertEquals( - $typeLocations->first()->location(), - Location::fromPathAndOffsets($this->workspace->path('One.php'), 9, 21) - ); - } - - public function testLocatesArrayType(): void - { - $typeLocations = $this->locate( - <<<'EOT' - // File: One.php - // o<>ne; - } - } - EOT - ); - - $location = $typeLocations->first()->location(); - self::assertEquals( - $location, - Location::fromPathAndOffsets($this->workspace->path('One.php'), 9, 21) - ); - } - - public function testLocatesFromArray(): void - { - $typeLocations = $this->locate( - <<<'EOT' - // File: One.php - // nes; - - EOT - ); - - $location = $typeLocations->first()->location(); - self::assertEquals( - $location, - Location::fromPathAndOffsets($this->workspace->path('One.php'), 9, 21) - ); - } - - public function testLocatesInterface(): void - { - $typeLocations = $this->locate( - <<<'EOT' - // File: One.php - // o<>ne; - } - } - EOT - ); - - self::assertEquals( - $typeLocations->first()->location(), - Location::fromPathAndOffsets($this->workspace->path('One.php'), 9, 25) - ); - } - - public function testLocatesUnion(): void - { - $typeLocations = $this->locate( - <<<'EOT' - // File: One.php - // o<>ne; - } - } - EOT - ); - self::assertEquals($this->workspace->path('Two.php'), $typeLocations->atIndex(0)->location()->uri()->path()); - self::assertEquals($this->workspace->path('One.php'), $typeLocations->atIndex(1)->location()->uri()->path()); - } - - public function testLocatesFirstUnionWithNullAndScalar(): void - { - $typeLocations = $this->locate( - <<<'EOT' - // File: One.php - // o<>ne; - } - } - EOT - ); - self::assertEquals($this->workspace->path('Two.php'), $typeLocations->first()->location()->uri()->path()); - } - - protected function locate(string $manifest, string $source): TypeLocations - { - [$source, $offset] = ExtractOffset::fromSource($source); - - $this->workspace->loadManifest($manifest); - - return (new WorseReflectionTypeLocator($this->reflector()))->locateTypes( - TextDocumentBuilder::create($source)->language('php')->build(), - ByteOffset::fromInt((int)$offset) - ); - } -} diff --git a/lib/WorseReferenceFinder/TolerantVariableDefintionLocator.php b/lib/WorseReferenceFinder/TolerantVariableDefintionLocator.php deleted file mode 100644 index 521331d3db..0000000000 --- a/lib/WorseReferenceFinder/TolerantVariableDefintionLocator.php +++ /dev/null @@ -1,36 +0,0 @@ -finder->findReferences($document, $byteOffset) as $reference) { - assert($reference instanceof PotentialLocation); - return TypeLocations::forLocation(new TypeLocation( - // we don't have the type info of the variable here, but - // there'll only be one so we don't need it. - TypeFactory::undefined(), - $reference->location() - )); - } - - throw new CouldNotLocateDefinition('Could not locate any references to variable'); - } -} diff --git a/lib/WorseReferenceFinder/TolerantVariableReferenceFinder.php b/lib/WorseReferenceFinder/TolerantVariableReferenceFinder.php deleted file mode 100644 index d9ee10dfbe..0000000000 --- a/lib/WorseReferenceFinder/TolerantVariableReferenceFinder.php +++ /dev/null @@ -1,213 +0,0 @@ - - */ - public function findReferences(TextDocument $document, ByteOffset $byteOffset): Generator - { - $sourceNode = $this->parser->get($document); - $variable = $this->variableNodeFromSource($sourceNode, $byteOffset->toInt()); - if ($variable === null) { - return false; - } - - $scopeNode = $this->scopeNode($variable); - $referencesGenerator = $this->find($scopeNode, $this->variableName($variable), $document->uri()); - - if (false === $this->includeDefinition) { - $referencesGenerator->next(); - } - - if ($referencesGenerator->valid()) { - yield from $referencesGenerator; - } - - return true; - } - - private function variableNodeFromSource(SourceFileNode $sourceNode, int $offset): ?Node - { - $node = $sourceNode->getDescendantNodeAtPosition($offset); - - if ( - false === $node instanceof Variable && - false === $node instanceof UseVariableName && - false === $node instanceof Parameter && - false === $node instanceof CatchClause - ) { - return null; - } - - if ($node instanceof Parameter) { - if ($node->visibilityToken) { - return null; - } - } - - if ( - $node instanceof Variable && $node->parent instanceof ScopedPropertyAccessExpression - && $node->parent->scopeResolutionQualifier !== $node - ) { - return null; - } - - if ($node instanceof Variable && $node->getFirstAncestor(PropertyDeclaration::class)) { - return null; - } - - return $node; - } - - private function scopeNode(Node $variable): Node - { - if ($variable instanceof CatchClause) { - return $variable; - } - - $name = $this->variableName($variable); - - if (null === $name) { - return $variable; - } - - if ($variable instanceof UseVariableName) { - $variable = $variable->getFirstAncestor(MethodDeclaration::class) ?: $variable; - } - - $scopeNode = $variable->getFirstAncestor(FunctionLike::class, ClassLike::class, SourceFileNode::class, CatchClause::class); - while ( - $scopeNode instanceof AnonymousFunctionCreationExpression && - $this->nameExistsInUseClause($name, $scopeNode) - ) { - $scopeNode = $scopeNode->getFirstAncestor(FunctionLike::class, ClassLike::class, SourceFileNode::class, CatchClause::class); - } - - if (null === $scopeNode) { - throw new Exception( - 'Could not determine scope node, this should not happen as ' . - 'there should always be a SourceFileNode.' - ); - } - - return $scopeNode; - } - - private function nameExistsInUseClause(string $variableName, AnonymousFunctionCreationExpression $function): bool - { - if ( - $function->anonymousFunctionUseClause === null - || $function->anonymousFunctionUseClause->useVariableNameList === null - || $function->anonymousFunctionUseClause->useVariableNameList instanceof MissingToken - ) { - return false; - } - - foreach ($function->anonymousFunctionUseClause->useVariableNameList->getElements() as $useVariableName) { - assert($useVariableName instanceof UseVariableName); - if ($this->variableName($useVariableName) == $variableName) { - return true; - } - } - return false; - } - - /** - * @return Generator - */ - private function find(Node $scopeNode, ?string $name, ?string $uri): Generator - { - if (null === $uri || null === $name) { - return; - } - if ($scopeNode instanceof CatchClause && $scopeNode->variableName instanceof Token && $name == substr((string)$scopeNode->variableName->getText($scopeNode->getFileContents()), 1)) { - yield PotentialLocation::surely( - Location::fromPathAndOffsets($uri, $scopeNode->variableName->start, $scopeNode->variableName->getEndPosition()) - ); - } - - /** @var Node $node */ - foreach ($scopeNode->getChildNodes() as $node) { - if ($node instanceof AnonymousFunctionCreationExpression && !$this->nameExistsInUseClause($name, $node)) { - continue; - } - - if ($node instanceof Variable && $name == (string)$node->getName()) { - yield PotentialLocation::surely( - Location::fromPathAndOffsets($uri, $node->getStartPosition(), $node->getEndPosition()) - ); - continue; - } - - if ($node instanceof Parameter && $name == $node->getName()) { - $variableName = $node->variableName; - if (!$variableName instanceof Token) { - continue; - } - yield PotentialLocation::surely( - Location::fromPathAndOffsets($uri, $variableName->start, $variableName->start + $variableName->length) - ); - continue; - } - - if ($node instanceof UseVariableName && $name == $node->getName()) { - yield PotentialLocation::surely( - Location::fromPathAndOffsets($uri, $node->getStartPosition(), $node->getEndPosition()) - ); - continue; - } - - yield from $this->find($node, $name, $uri); - } - } - - private function variableName(Node $variable): ?string - { - if ( - $variable instanceof Variable || - $variable instanceof UseVariableName || - $variable instanceof Parameter - ) { - return $variable->getName(); - } - - if ($variable instanceof CatchClause && $variable->variableName) { - return substr((string)$variable->variableName->getText($variable->getFileContents()), 1); - } - - return null; - } -} diff --git a/lib/WorseReferenceFinder/WorsePlainTextClassDefinitionLocator.php b/lib/WorseReferenceFinder/WorsePlainTextClassDefinitionLocator.php deleted file mode 100644 index a578bd1f1c..0000000000 --- a/lib/WorseReferenceFinder/WorsePlainTextClassDefinitionLocator.php +++ /dev/null @@ -1,171 +0,0 @@ -parser = new TolerantAstProvider(); - } - - - public function locateDefinition(TextDocument $document, ByteOffset $byteOffset): TypeLocations - { - $word = $this->extractWord($document, $byteOffset); - $word = $this->resolveClassName($document, $byteOffset, $word); - - try { - $reflectionClass = $this->reflector->reflectClassLike($word); - } catch (NotFound $notFound) { - throw new CouldNotLocateDefinition(sprintf( - 'Word "%s" could not be resolved to a class', - $word - ), 0, $notFound); - } - - $uri = $reflectionClass->sourceCode()->uri(); - - if (null === $uri) { - throw new CouldNotLocateDefinition(sprintf( - 'The source code for class "%s" has no path associated with it.', - (string) $reflectionClass->type() - )); - } - - return new TypeLocations([ - new TypeLocation( - $reflectionClass->type(), - new Location($uri, $reflectionClass->position()) - ) - ]); - } - - private function extractWord(TextDocument $document, ByteOffset $byteOffset): string - { - $offset = $byteOffset->toInt() + 1; - $docLength = strlen($document->__toString()); - if ($offset > $docLength) { - $offset = $docLength; - } - return (new WordAtOffset( - WordAtOffset::SPLIT_QUALIFIED_PHP_NAME - ))->__invoke($document->__toString(), $offset); - } - - private function resolveClassName(TextDocument $document, ByteOffset $byteOffset, string $word): string - { - if (!$document->language()->isPhp()) { - return $word; - } - - $node = $this->parser->get($document); - $node = NodeUtil::firstDescendantNodeAfterOffset($node, $byteOffset->toInt()); - - if ($node instanceof SourceFileNode) { - $node = $node->getFirstDescendantNode(NamespaceUseClause::class) ?? $node; - } - - $imports = $this->resolveImportTable($node); - - if (isset($imports[0][$word])) { - return $imports[0][$word]->__toString(); - } - - if (!isset($word[0])) { - return $word; - } - - if ($word[0] !== '\\') { - $namespace = $this->resolveNamespace($node); - if ($namespace) { - return $namespace .'\\'.$word; - } - } - - return $word; - } - - /** - * Tolerant parser will resolve a docblock comment as the root node, not - * the node to which the comment belongs. Here we attempt to get the import - * table from the current node, if that fails then we just do whatever we - * can to get an import table. - */ - private function resolveImportTable(Node $node): array - { - try { - return $node->getImportTablesForCurrentScope(); - } catch (Exception) { - } - - foreach ($node->getDescendantNodes() as $node) { - try { - $imports = $node->getImportTablesForCurrentScope(); - if (empty($imports[0])) { - continue; - } - return $imports; - } catch (Exception) { - } - } - - return [ [], [], [] ]; - } - - /** - * As with resolve import table, we try our best. - */ - private function resolveNamespace(Node $node): string - { - try { - return $this->namespaceFromNode($node); - } catch (Exception) { - } - - foreach ($node->getDescendantNodes() as $node) { - try { - return $this->namespaceFromNode($node); - } catch (Exception) { - } - } - - return ''; - } - - private function namespaceFromNode(Node $node): string - { - if (null === $node->getNamespaceDefinition()) { - throw new Exception('Locate something with a namespace instead'); - } - - $name = $node->getNamespaceDefinition()->name; - - if (!$name instanceof QualifiedName) { - return ''; - } - - return $name->__toString(); - } -} diff --git a/lib/WorseReferenceFinder/WorseReflectionDefinitionLocator.php b/lib/WorseReferenceFinder/WorseReflectionDefinitionLocator.php deleted file mode 100644 index 74a288d4d8..0000000000 --- a/lib/WorseReferenceFinder/WorseReflectionDefinitionLocator.php +++ /dev/null @@ -1,279 +0,0 @@ -language()->isPhp()) { - throw new CouldNotLocateDefinition('I only work with PHP files'); - } - - $this->cache->purge(); - - try { - $offset = $this->reflector->reflectOffset( - $textDocument, - $byteOffset->toInt() - ); - } catch (NotFound $notFound) { - throw new CouldNotLocateDefinition($notFound->getMessage(), 0, $notFound); - } - - $typeLocations = []; - $typeLocations = $this->gotoDefinition($textDocument, $offset); - - if ($typeLocations->count() === 0) { - throw new CouldNotLocateDefinition('No definition(s) found'); - } - - return $typeLocations; - } - - private function gotoDefinition(TextDocument $document, ReflectionOffset $offset): TypeLocations - { - $nodeContext = $offset->nodeContext(); - - if ($nodeContext instanceof MemberDeclarationContext) { - return $this->gotoMethodDeclaration($nodeContext); - } - return match ($nodeContext->symbol()->symbolType()) { - Symbol::METHOD, Symbol::PROPERTY, Symbol::CONSTANT, Symbol::CASE => $this->gotoMember($nodeContext), - Symbol::CLASS_ => $this->gotoClass($nodeContext), - Symbol::FUNCTION => $this->gotoFunction($nodeContext), - Symbol::DECLARED_CONSTANT => $this->gotoDeclaredConstant($nodeContext), - default => throw new CouldNotLocateDefinition(sprintf( - 'Do not know how to goto definition of symbol type "%s"', - $nodeContext->symbol()->symbolType() - )), - }; - } - - private function gotoClass(NodeContext $nodeContext): TypeLocations - { - $className = $nodeContext->type(); - - if (!$className instanceof ClassType) { - throw new CouldNotLocateDefinition(sprintf( - 'member container type is not a class type, it is a "%s"', - get_class($className) - )); - } - - try { - $class = $this->reflector->reflectClassLike( - $className->name() - ); - } catch (NotFound $e) { - throw new CouldNotLocateDefinition($e->getMessage(), 0, $e); - } - - $uri = $class->sourceCode()->uri(); - - if (null === $uri) { - throw new CouldNotLocateDefinition(sprintf( - 'The source code for class "%s" has no path associated with it.', - $class->name() - )); - } - - return new TypeLocations([new TypeLocation($className, new Location( - $uri, - $class->position() - ))]); - } - - private function gotoFunction(NodeContext $nodeContext): TypeLocations - { - $functionName = $nodeContext->symbol()->name(); - - try { - $function = $this->reflector->reflectFunction($functionName); - } catch (NotFound $e) { - throw new CouldNotLocateDefinition($e->getMessage(), 0, $e); - } - - $uri = $function->sourceCode()->uri(); - - if (null === $uri) { - throw new CouldNotLocateDefinition(sprintf( - 'The source code for function "%s" has no path associated with it.', - $function->name() - )); - } - - return new TypeLocations([ - new TypeLocation(TypeFactory::unknown(), new Location( - $uri, - $function->position() - )) - ]); - } - - private function gotoDeclaredConstant(NodeContext $nodeContext): TypeLocations - { - $constantName = $nodeContext->symbol()->name(); - - try { - $constant = $this->reflector->reflectConstant($constantName); - } catch (NotFound $e) { - throw new CouldNotLocateDefinition($e->getMessage(), 0, $e); - } - - $uri = $constant->sourceCode()->uri(); - - if (null === $uri) { - throw new CouldNotLocateDefinition(sprintf( - 'The source code for constant "%s" has no path associated with it.', - $constant->name() - )); - } - - return new TypeLocations([ - new TypeLocation(TypeFactory::unknown(), new Location( - $uri, - $constant->position() - )) - ]); - } - - private function gotoMember(NodeContext $nodeContext): TypeLocations - { - $symbolName = $nodeContext->symbol()->name(); - $symbolType = $nodeContext->symbol()->symbolType(); - $containerType = $nodeContext->containerType(); - - $locations = []; - foreach ($containerType->expandTypes()->classLike() as $namedType) { - try { - $containingClass = $this->reflector->reflectClassLike($namedType->name()); - } catch (NotFound) { - continue; - } - - if ($symbolType === Symbol::PROPERTY && $containingClass instanceof ReflectionInterface) { - throw new CouldNotLocateDefinition(sprintf('Symbol is a property and class "%s" is an interface', (string) $containingClass->name())); - } - - switch ($symbolType) { - case Symbol::METHOD: - $members = $containingClass->methods(); - break; - case Symbol::CONSTANT: - if ($containingClass instanceof ReflectionEnum) { - $members = $containingClass->cases(); - if ($members->has($symbolName)) { - break; - } - } - $members = $containingClass->constants(); - break; - case Symbol::PROPERTY: - if ( - !$containingClass instanceof ReflectionClass || $containingClass instanceof ReflectionTrait || $containingClass instanceof ReflectionEnum) { - throw new CouldNotLocateDefinition(sprintf( - 'ClassLike "%s" has no properties!', - $containingClass::class - )); - } - $members = $containingClass->properties(); - break; - default: - throw new CouldNotLocateDefinition(sprintf( - 'Unhandled symbol type "%s"', - $symbolType - )); - } - - if (false === $members->has($symbolName)) { - continue; - } - - $member = $members->get($symbolName); - - $uri = $member->declaringClass()->sourceCode()->uri(); - - if (null === $uri) { - throw new CouldNotLocateDefinition(sprintf( - 'The source code for class "%s" has no path associated with it.', - (string) $containingClass->name() - )); - } - - $locations[] = new TypeLocation( - $namedType, - new Location($uri, $member->position()) - ); - } - - return new TypeLocations($locations); - } - - private function gotoMethodDeclaration(MemberDeclarationContext $nodeContext): TypeLocations - { - try { - $class = $this->reflector->reflectClass($nodeContext->classType()->name()); - } catch (NotFound) { - return new TypeLocations([]); - } - - // find first parent definition or return declaring class - $member = (function (string $name) use ($class) { - foreach ((new ClassHierarchyResolver())->resolve($class) as $currentClass) { - if ($currentClass->ownMembers()->has($name)) { - return $currentClass->ownMembers()->byName($name)->first(); - } - } - return null; - })($nodeContext->name()); - - if (null === $member) { - return new TypeLocations([]); - } - - - $uri = $member->declaringClass()->sourceCode()->uri(); - - if (null === $uri) { - throw new CouldNotLocateDefinition(sprintf( - 'The source code for class "%s" has no path associated with it.', - (string) $member->declaringClass()->name() - )); - } - - return new TypeLocations([new TypeLocation( - $nodeContext->classType(), - new Location($uri, $member->position()) - )]); - } -} diff --git a/lib/WorseReferenceFinder/WorseReflectionTypeLocator.php b/lib/WorseReferenceFinder/WorseReflectionTypeLocator.php deleted file mode 100644 index 5da3fe0337..0000000000 --- a/lib/WorseReferenceFinder/WorseReflectionTypeLocator.php +++ /dev/null @@ -1,80 +0,0 @@ -language()->isPhp()) { - throw new UnsupportedDocument('I only work with PHP files'); - } - - $type = $this->reflector->reflectOffset( - $textDocument, - $byteOffset->toInt() - )->nodeContext()->type(); - - $typeLocations = []; - foreach ($type->expandTypes() as $type) { - if ($type instanceof ArrayType) { - $type = $type->iterableValueType(); - } - - if (!$type instanceof ClassType) { - continue; - } - $typeLocations[] = new TypeLocation($type, $this->gotoType($type)); - } - - return new TypeLocations($typeLocations); - } - - private function gotoType(Type $type): Location - { - $className = $this->resolveClassName($type); - - try { - $class = $this->reflector->reflectClassLike($className->full()); - } catch (NotFound $e) { - throw new CouldNotLocateType($e->getMessage(), 0, $e); - } - - $textDocument = $class->sourceCode(); - - return new Location($textDocument->uriOrThrow(), $class->position()); - } - - private function resolveClassName(Type $type): ClassName - { - foreach ($type->expandTypes()->classLike() as $type) { - return $type->name(); - } - - throw new CouldNotLocateType(sprintf( - 'Cannot goto to primitive type %s "%s"', - get_class($type), - $type->__toString() - )); - } -} diff --git a/lib/WorseReflection/Bridge/Composer/ComposerSourceLocator.php b/lib/WorseReflection/Bridge/Composer/ComposerSourceLocator.php deleted file mode 100644 index 6754a50ee5..0000000000 --- a/lib/WorseReflection/Bridge/Composer/ComposerSourceLocator.php +++ /dev/null @@ -1,31 +0,0 @@ -classLoader->findFile((string) $className); - - if (false === $path) { - throw new SourceNotFound(sprintf( - 'Composer could not locate file for class "%s"', - $className->full() - )); - } - - return TextDocumentBuilder::fromUri($path)->build(); - } -} diff --git a/lib/WorseReflection/Bridge/Phpactor/ClassToFileSourceLocator.php b/lib/WorseReflection/Bridge/Phpactor/ClassToFileSourceLocator.php deleted file mode 100644 index d0d1680ba0..0000000000 --- a/lib/WorseReflection/Bridge/Phpactor/ClassToFileSourceLocator.php +++ /dev/null @@ -1,35 +0,0 @@ -converter->classToFileCandidates(ClassName::fromString((string) $name)); - - if ($candidates->noneFound()) { - throw new SourceNotFound(sprintf('Could not locate a candidate for "%s"', (string) $name)); - } - - foreach ($candidates as $candidate) { - if (file_exists((string) $candidate)) { - return TextDocumentBuilder::fromUri((string) $candidate)->build(); - } - } - - throw new SourceNotFound($name); - } -} diff --git a/lib/WorseReflection/Bridge/Phpactor/DocblockParser/CachedParserFactory.php b/lib/WorseReflection/Bridge/Phpactor/DocblockParser/CachedParserFactory.php deleted file mode 100644 index 316d5795b6..0000000000 --- a/lib/WorseReflection/Bridge/Phpactor/DocblockParser/CachedParserFactory.php +++ /dev/null @@ -1,29 +0,0 @@ -cache->getOrSet('docblock_' . $docblock, function () use ($docblock, $scope) { - return $this->innerFactory->create($docblock, $scope); - }); - } -} diff --git a/lib/WorseReflection/Bridge/Phpactor/DocblockParser/DocblockParserFactory.php b/lib/WorseReflection/Bridge/Phpactor/DocblockParser/DocblockParserFactory.php deleted file mode 100644 index b8121dfcc7..0000000000 --- a/lib/WorseReflection/Bridge/Phpactor/DocblockParser/DocblockParserFactory.php +++ /dev/null @@ -1,64 +0,0 @@ -parser->parse($this->lexer->lex($docblock)); - assert($node instanceof ParserDocblock); - return new ParsedDocblock( - $node, - new TypeConverter($this->reflector, $scope), - $docblock - ); - } -} diff --git a/lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php b/lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php deleted file mode 100644 index 183c266543..0000000000 --- a/lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php +++ /dev/null @@ -1,348 +0,0 @@ -node; - } - - /** - * @return Types - */ - public function types(): Types - { - $types = []; - foreach ($this->node->descendantElements(TypeNode::class) as $type) { - if (!$type instanceof TypeNode) { - continue; - } - $types[] = $this->typeConverter->convert($type); - } - - return new Types($types); - } - - public function typeAliases(): DocBlockTypeAliases - { - $types = []; - foreach ($this->node->descendantElements(TypeAliasTag::class) as $tag) { - $types[] = new DocBlockTypeAlias( - $this->typeConverter->convert($tag->alias)->toPhpString(), - $this->typeConverter->convert($tag->type), - ); - } - - return new DocBlockTypeAliases($types); - } - - public function methodType(string $methodName): Type - { - foreach ($this->node->tags(MethodTag::class) as $methodTag) { - assert($methodTag instanceof MethodTag); - if ($methodTag->methodName() !== $methodName) { - continue; - } - $this->convertType($methodTag->type); - } - - return TypeFactory::undefined(); - } - - public function inherits(): bool - { - return false; - } - - public function vars(): DocBlockVars - { - $vars = []; - foreach ($this->node->tags(VarTag::class) as $varTag) { - assert($varTag instanceof VarTag); - $vars[] = new DocBlockVar( - $varTag->variable ? ltrim($varTag->name() ?? '', '$') : '', - $this->convertType($varTag->type), - ); - } - - return new DocBlockVars($vars); - } - - public function params(): DocBlockParams - { - $params = []; - foreach ($this->node->tags(ParamTag::class) as $paramTag) { - $params[] = new DocBlockParam( - $paramTag->paramName() ? ltrim( - /** @phpstan-ignore-next-line */ - $paramTag->paramName() ?? '', - '$' - ) : '', - $this->convertType($paramTag->type), - ); - } - - return new DocBlockParams($params); - } - - public function parameterType(string $paramName): Type - { - $types = []; - foreach ($this->node->tags(ParamTag::class) as $paramTag) { - assert($paramTag instanceof ParamTag); - if (ltrim($paramTag->paramName() ?? '', '$') !== $paramName) { - continue; - } - return $this->convertType($paramTag->type); - } - - return TypeFactory::undefined(); - } - - public function propertyType(string $propertyName): Type - { - $types = []; - foreach ($this->node->tags(PropertyTag::class) as $propertyTag) { - assert($propertyTag instanceof PropertyTag); - if (ltrim($propertyTag->propertyName(), '$') !== $propertyName) { - continue; - } - return $this->convertType($propertyTag->type); - } - - return TypeFactory::undefined(); - } - - public function formatted(): string - { - return implode("\n", array_map(function (string $line) { - return preg_replace('{^\s+}', '', $line); - }, explode("\n", $this->node->prose()))); - } - - public function returnType(): Type - { - foreach ($this->node->tags(ReturnTag::class) as $tag) { - assert($tag instanceof ReturnTag); - return $this->convertType($tag->type()); - } - - return TypeFactory::undefined(); - } - - public function raw(): string - { - return $this->raw; - } - - public function isDefined(): bool - { - return true; - } - - public function properties(ReflectionClassLike $declaringClass): CoreReflectionPropertyCollection - { - $properties = []; - foreach ($this->node->tags(PropertyTag::class) as $propertyTag) { - assert($propertyTag instanceof PropertyTag); - $type = $this->convertType($propertyTag->type); - $property = new VirtualReflectionProperty( - $declaringClass->position(), - $declaringClass, - $declaringClass, - ltrim($propertyTag->propertyName() ?? '', '$'), - new ConcreteFrame(), - $this, - $declaringClass->scope(), - Visibility::public(), - $type, - $type, - new Deprecation(false), - ); - $properties[] = $property; - } - - return CoreReflectionPropertyCollection::fromReflectionProperties($properties); - } - - public function methods(ReflectionClassLike $declaringClass): CoreReflectionMethodCollection - { - $methods = []; - foreach ($this->node->tags(MethodTag::class) as $methodTag) { - assert($methodTag instanceof MethodTag); - $params = ReflectionParameterCollection::empty(); - $method = new VirtualReflectionMethod( - $declaringClass->position(), - $declaringClass, - $declaringClass, - $methodTag->methodName() ?? '', - new ConcreteFrame(), - $this, - $declaringClass->scope(), - Visibility::public(), - $this->convertType($methodTag->type), - $this->convertType($methodTag->type), - $params, - NodeText::fromString(''), - false, - $methodTag->static ? true : false, - new Deprecation(false), - ); - $this->addParameters($method, $params, $methodTag->parameters); - $methods[] = $method; - } - - return CoreReflectionMethodCollection::fromReflectionMethods($methods); - } - - public function deprecation(): Deprecation - { - foreach ($this->node->tags(DeprecatedTag::class) as $deprecatedTag) { - assert($deprecatedTag instanceof DeprecatedTag); - return new Deprecation(true, $deprecatedTag->text()); - } - - return new Deprecation(false); - } - - public function templateMap(): TemplateMap - { - $map = []; - foreach ($this->node->tags(TemplateTag::class) as $templateTag) { - assert($templateTag instanceof TemplateTag); - $map[$templateTag->placeholder()] = $this->convertType($templateTag->type); - } - return new TemplateMap($map); - } - - public function extends(): array - { - $extends = []; - foreach ($this->node->tags(ExtendsTag::class) as $extendsTag) { - assert($extendsTag instanceof ExtendsTag); - $extends[] = $this->convertType($extendsTag->type); - } - return $extends; - } - - public function implements(): array - { - $implements = []; - foreach ($this->node->tags(ImplementsTag::class) as $implementsTag) { - assert($implementsTag instanceof ImplementsTag); - $implements = array_merge($implements, array_map(function (TypeNode $type) { - return $this->convertType($type); - }, $implementsTag->types())); - } - return $implements; - } - - public function mixins(): array - { - $mixins = []; - foreach ($this->node->tags(MixinTag::class) as $mixinTag) { - assert($mixinTag instanceof MixinTag); - $mixins[] = $this->convertType($mixinTag->class); - } - return $mixins; - } - - public function node(): ParserDocblock - { - return $this->node; - } - - public function assertions(): array - { - $assertions = []; - foreach ($this->node->tags(AssertTag::class) as $assert) { - if (!$assert->paramName) { - continue; - } - $assertions[] = new DocBlockTypeAssertion( - ltrim($assert->paramName->toString(), '$'), - $this->convertType($assert->type), - $assert->negationOrEquality?->value === '!', - ); - } - return $assertions; - } - - private function addParameters(VirtualReflectionMethod $method, ReflectionParameterCollection $collection, ?ParameterList $parameterList): void - { - if (null === $parameterList) { - return; - } - foreach ($parameterList->parameters() as $index => $parameterTag) { - assert($parameterTag instanceof ParameterTag); - $type = $this->convertType($parameterTag->type); - $collection->add(new VirtualReflectionParameter( - ltrim($parameterTag->parameterName() ?? '', '$'), - $method, - $type, - $type, - DefaultValue::undefined(), - false, - $method->scope(), - $method->position(), - $index - )); - } - } - - private function convertType(?TypeNode $type): Type - { - return $this->typeConverter->convert($type); - } -} diff --git a/lib/WorseReflection/Bridge/Phpactor/DocblockParser/TypeConverter.php b/lib/WorseReflection/Bridge/Phpactor/DocblockParser/TypeConverter.php deleted file mode 100644 index e7f78b0440..0000000000 --- a/lib/WorseReflection/Bridge/Phpactor/DocblockParser/TypeConverter.php +++ /dev/null @@ -1,430 +0,0 @@ -convertScalar($type->toString()); - } - if ($type instanceof ListNode) { - return $this->convertList($type); - } - if ($type instanceof ListBracketsNode) { - return $this->convertListBrackets($type); - } - if ($type instanceof ArrayNode) { - return $this->convertArray($type); - } - if ($type instanceof ArrayShapeNode) { - return $this->convertArrayShape($type); - } - if ($type instanceof UnionNode) { - return $this->convertUnion($type); - } - if ($type instanceof IntersectionNode) { - return $this->convertIntersection($type); - } - if ($type instanceof GenericNode) { - $node = $this->convertGeneric($type); - return $node; - } - if ($type instanceof ClassNode) { - return $this->convertClass($type); - } - if ($type instanceof ThisNode) { - return $this->convertThis($type); - } - if ($type instanceof NullNode) { - return new NullType(); - } - if ($type instanceof NullableNode) { - return $this->convertNullable($type); - } - - if ($type instanceof CallableNode) { - return $this->convertCallable($type); - } - - if ($type instanceof ParenthesizedType) { - return $this->convertParenthesized($type); - } - if ($type instanceof LiteralStringNode) { - return $this->convertLiteralString($type); - } - if ($type instanceof LiteralIntegerNode) { - return $this->convertLiteralInteger($type); - } - if ($type instanceof LiteralFloatNode) { - return $this->convertLiteralFloat($type); - } - if ($type instanceof ConstantNode) { - return $this->convertConstant($type); - } - if ($type instanceof ConditionalNode) { - return $this->convertConditional($type); - } - - return new MissingType(); - } - - private function convertScalar(string $type): Type - { - if ($type === 'int') { - return new IntType(); - } - if ($type === 'string') { - return new StringType(); - } - if ($type === 'class-string') { - return new ClassStringType(); - } - if ($type === 'float') { - return new FloatType(); - } - if ($type === 'mixed') { - return new MixedType(); - } - if ($type === 'bool') { - return new BooleanType(); - } - if ($type === 'false') { - return new FalseType(); - } - if ($type === 'callable') { - return new CallableType([], new MissingType()); - } - - return new MissingType(); - } - - private function convertArray(ArrayNode $type): Type - { - return new ArrayType(new MissingType()); - } - - private function convertList(ListNode $type): Type - { - return new ListType(new MixedType()); - } - - private function convertUnion(UnionNode $union): Type - { - return new UnionType(...array_map( - fn (Node $node) => $this->convert($node), - iterator_to_array($union->types->types()) - )); - } - - private function convertIntersection(IntersectionNode $type): Type - { - return new IntersectionType(...array_map( - fn (Node $node) => $this->convert($node), - iterator_to_array($type->types->types()) - )); - } - - private function convertGeneric(GenericNode $type): Type - { - if ($type->type instanceof ArrayNode) { - $parameters = array_values(iterator_to_array($type->parameters()->types())); - if (count($parameters) === 1) { - return new ArrayType( - null, - $this->convert($parameters[0]) - ); - } - if (count($parameters) === 2) { - return new ArrayType( - $this->convert($parameters[0]), - $this->convert($parameters[1]), - ); - } - return new ArrayType(new MissingType()); - } - if ($type->type instanceof ScalarNode && $type->type->name->value === 'int') { - $parameters = array_values(iterator_to_array($type->parameters()->types())); - if (count($parameters) === 2) { - $start = $this->convert($parameters[0]); - $end = $this->convert($parameters[1]); - if ($start instanceof ClassType) { - if ($start->name()->short() === 'min') { - $start = null; - } - } - if ($end instanceof ClassType) { - if ($end->name()->short() === 'max') { - $end = null; - } - } - return new IntRangeType( - $start, - $end, - ); - } - } - - if ($type->type instanceof ListNode) { - $parameters = array_values(iterator_to_array($type->parameters()->types())); - if (count($parameters) === 1) { - return new ListType( - $this->convert($parameters[0]) - ); - } - return new ListType(new MissingType()); - } - - if ($type->type instanceof ClassNode && $type->type->name->value === 'iterable') { - $parameters = array_values(iterator_to_array($type->parameters()->types())); - - if (count($parameters) === 1) { - return new PseudoIterableType( - new ArrayKeyType(), - $this->convert($parameters[0]) - ); - } - if (count($parameters) === 2) { - return new PseudoIterableType( - $this->convert($parameters[0]), - $this->convert($parameters[1]), - ); - } - return new PseudoIterableType(); - } - - $classType = $this->convert($type->type); - - if ($classType instanceof ClassStringType) { - $parameters = $type->parameters(); - if ($parameters->types()->count()) { - return new ClassStringType( - ClassName::fromString($this->convert( - $parameters->types()->first() - )->__toString()) - ); - } - return $classType; - } - - if (!$classType instanceof ClassType) { - return new MissingType(); - } - - $parameters = iterator_to_array($type->parameters()->types()); - - return new GenericClassType( - $this->reflector, - $classType->name(), - array_map( - fn (TypeNode $node) => $this->convert($node), - $parameters - ) - ); - } - - private function convertClass(ClassNode $typeNode): Type - { - $name = $typeNode->name()->toString(); - - if ($name === 'never') { - return new NeverType(); - } - - if ($name === 'static') { - return new StaticType(); - } - - if ($name === 'self') { - return new SelfType(); - } - - if ($name === 'iterable') { - return new PseudoIterableType(); - } - - if ($name === 'object') { - return new ObjectType(); - } - - if ($name === 'resource') { - return new ResourceType(); - } - - if ($name === 'void') { - return new VoidType(); - } - - if ($name === 'positive-int') { - return new IntPositive(); - } - - if ($name === 'negative-int') { - return new IntNegative(); - } - $type = new ReflectedClassType( - $this->reflector, - ClassName::fromString( - $typeNode->name()->toString() - ) - ); - - return $this->scope->resolveFullyQualifiedName($type); - } - - private function convertListBrackets(ListBracketsNode $type): Type - { - return new ArrayType($this->convert($type->type)); - } - - private function convertThis(ThisNode $type): Type - { - return new ThisType(); - } - - /** - * @return Type&InvokeableType - */ - private function convertCallable(CallableNode $callableNode): Type - { - $parameters = array_map(function (TypeNode $type) { - return $this->convert($type); - }, $callableNode->parameters ? iterator_to_array($callableNode->parameters->types()) : []); - - $type = $this->convert($callableNode->type); - - if ($callableNode->name && $callableNode->name->toString() === 'Closure') { - return new ClosureType($this->reflector, $parameters, $type); - } - - return new CallableType($parameters, $type); - } - - private function convertArrayShape(ArrayShapeNode $type): ArrayShapeType - { - $typeMap = []; - foreach (array_values($type->arrayKeyValueList->arrayKeyValues()) as $index => $keyValue) { - $key = $keyValue->key ? $keyValue->key->value : $index; - $typeMap[$key] = $this->convert($keyValue->type); - } - - return new ArrayShapeType($typeMap); - } - - private function convertParenthesized(ParenthesizedType $type): Type - { - $innerType = $this->convert($type->node); - return new PhpactorParenthesizedType($innerType); - } - - private function convertLiteralString(LiteralStringNode $type): Type - { - $quote = substr($type->token->value, 0, 1); - $string = trim($type->token->value, $quote); - - return new StringLiteralType($string); - } - - private function convertLiteralInteger(LiteralIntegerNode $type): Type - { - if ((int)$type->token->value === PHP_INT_MAX) { - return new IntMaxType(); - } - return new IntLiteralType((int)$type->token->value); - } - - private function convertLiteralFloat(LiteralFloatNode $type): Type - { - return new FloatLiteralType((float)$type->token->value); - } - - private function convertConstant(ConstantNode $type): Type - { - $classType = $this->convert($type->name); - - return new GlobbedConstantUnionType($classType, $type->constant->value); - } - - private function convertNullable(NullableNode $type): Type - { - return new NullableType($this->convert($type->type)); - } - - private function convertConditional(ConditionalNode $type): Type - { - return new ConditionalType($type->variable->name()->toString(), $this->convert($type->isType), $this->convert($type->left), $this->convert($type->right)); - } -} diff --git a/lib/WorseReflection/Bridge/Phpactor/MemberProvider/DocblockMemberProvider.php b/lib/WorseReflection/Bridge/Phpactor/MemberProvider/DocblockMemberProvider.php deleted file mode 100644 index ce7d14e7d4..0000000000 --- a/lib/WorseReflection/Bridge/Phpactor/MemberProvider/DocblockMemberProvider.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - public function provideMembers(ServiceLocator $locator, ReflectionClassLike $class): ReflectionMemberCollection - { - return ChainReflectionMemberCollection::fromCollections([ - $class->docblock()->methods($class), - $class->docblock()->properties($class), - ]); - } -} diff --git a/lib/WorseReflection/Bridge/PsrLog/ArrayLogger.php b/lib/WorseReflection/Bridge/PsrLog/ArrayLogger.php deleted file mode 100644 index c0f53a0c65..0000000000 --- a/lib/WorseReflection/Bridge/PsrLog/ArrayLogger.php +++ /dev/null @@ -1,21 +0,0 @@ -messages[] = $message; - } - - public function messages(): array - { - return $this->messages; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/AstProvider/TolerantAstProvider.php b/lib/WorseReflection/Bridge/TolerantParser/AstProvider/TolerantAstProvider.php deleted file mode 100644 index 1dab887451..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/AstProvider/TolerantAstProvider.php +++ /dev/null @@ -1,42 +0,0 @@ -parser->parseSourceFile( - $document->__toString(), - $document->uri()?->__toString(), - ); - $this->logger->info(sprintf( - 'PARS %s %s', - number_format(microtime(true) - $start, 4), - $document->uri()?->__toString() ?? '' . (new Error())->getTraceAsString(), - )); - - return $node; - } - - public function parseString(string $string): SourceFileNode - { - return $this->parser->parseSourceFile($string); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/AssignmentToMissingPropertyDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/AssignmentToMissingPropertyDiagnostic.php deleted file mode 100644 index 3537b833cb..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/AssignmentToMissingPropertyDiagnostic.php +++ /dev/null @@ -1,64 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return DiagnosticSeverity::WARNING(); - } - - public function message(): string - { - return sprintf('Property "%s" has not been defined', $this->propertyName); - } - - public function classType(): string - { - return $this->classType; - } - - public function propertyName(): string - { - return $this->propertyName; - } - - public function propertyType(): Type - { - return $this->propertyType; - } - - public function isSubscriptAssignment(): bool - { - return $this->isSubscriptAssignment; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'assignment_to_missing_property'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/AssignmentToMissingPropertyProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/AssignmentToMissingPropertyProvider.php deleted file mode 100644 index f1b690bd09..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/AssignmentToMissingPropertyProvider.php +++ /dev/null @@ -1,178 +0,0 @@ -bar = 'foo'; - } - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals( - 'Property "bar" has not been defined', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'does not report assignment for existing property', - source: <<<'PHP' - bar = 'foo'; - } - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - } - - public function exit(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - if (!$node instanceof AssignmentExpression) { - return; - } - - $memberAccess = $node->leftOperand; - $accessExpression = null; - if ($memberAccess instanceof SubscriptExpression) { - /** @phpstan-ignore-next-line Access expression is NULL if list addition */ - $accessExpression = $memberAccess->accessExpression ?: $memberAccess; - $memberAccess = $memberAccess->postfixExpression; - } - - if (!$memberAccess instanceof MemberAccessExpression) { - return; - } - - $deref = $memberAccess->dereferencableExpression; - - if (!$deref instanceof Variable) { - return; - } - - if ($deref->getText() !== '$this') { - return; - } - - $memberNameToken = $memberAccess->memberName; - - if (!$memberNameToken instanceof Token) { - return; - } - - $memberName = $memberNameToken->getText($node->getFileContents()); - - if (!is_string($memberName)) { - return; - } - - $rightOperand = $node->rightOperand; - - if (!$rightOperand instanceof Expression) { - return; - } - - $classNode = NodeUtil::nodeContainerClassLikeDeclaration($node); - - if (null === $classNode) { - return; - } - - try { - $class = $resolver->reflector()->reflectClassLike($classNode->getNamespacedName()->__toString()); - } catch (NotFound) { - return; - } - - if (!$class instanceof ReflectionTrait && !$class instanceof ReflectionClass) { - return; - } - - if ($class->properties()->has($memberName)) { - return; - } - - yield new AssignmentToMissingPropertyDiagnostic( - ByteOffsetRange::fromInts( - $node->getStartPosition(), - $node->getEndPosition() - ), - $class->name()->__toString(), - $memberName, - $this->resolvePropertyType($resolver, $frame, $rightOperand, $accessExpression), - $accessExpression ? true : false, - ); - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } - - public function name(): string - { - return 'assignment_to_missing_property'; - } - - private function resolvePropertyType( - NodeContextResolver $resolver, - Frame $frame, - Expression $rightOperand, - Node|MissingToken|null $accessExpression - ): Type { - $type = $resolver->resolveNode($frame, $rightOperand)->type(); - - if (!$accessExpression instanceof Node) { - return $type; - } - - return new ArrayType( - $accessExpression instanceof SubscriptExpression ? null : $resolver->resolveNode($frame, $accessExpression)->type(), - $type - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DeprecatedUsageDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DeprecatedUsageDiagnostic.php deleted file mode 100644 index 4a1a93fa06..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DeprecatedUsageDiagnostic.php +++ /dev/null @@ -1,48 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return DiagnosticSeverity::WARNING(); - } - - public function message(): string - { - if (!$this->message) { - return sprintf('Call to deprecated %s "%s"', $this->memberType, $this->memberName); - } - - return sprintf('Call to deprecated %s "%s": %s', $this->memberType, $this->memberName, $this->message); - } - - public function tags(): array - { - return [DiagnosticTag::DEPRECATED]; - } - - public function code(): string - { - return 'deprecated_usage'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DeprecatedUsageDiagnosticProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DeprecatedUsageDiagnosticProvider.php deleted file mode 100644 index 7b2477f3cc..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DeprecatedUsageDiagnosticProvider.php +++ /dev/null @@ -1,277 +0,0 @@ -resolveNode($frame, $node); - - if ($resolved instanceof MemberAccessContext) { - yield from $this->memberAccessDiagnostics($resolved); - } - if ($resolved instanceof ClassLikeContext) { - yield from $this->classLikeDiagnostics($resolved); - } - if ($resolved instanceof FunctionCallContext) { - yield from $this->functionDiagnostics($resolved); - } - } - - public function exit(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } - - public function examples(): iterable - { - yield new DiagnosticExample( - title: 'deprecated class', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'deprecated constant', - source: <<<'PHP' - at(0)->message()); - } - ); - - yield new DiagnosticExample( - title: 'deprecated enum', - source: <<<'PHP' - at(0)->message()); - } - ); - - yield new DiagnosticExample( - title: 'deprecated function', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'deprecated method', - source: <<<'PHP' - deprecated(); - $this->notDeprecated(); - } - - /** @deprecated This is deprecated */ - public function deprecated(): void {} - - public function notDeprecated(): void {} - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals('Call to deprecated method "deprecated": This is deprecated', $diagnostics->at(0)->message()); - } - ); - - yield new DiagnosticExample( - title: 'deprecated on trait', - source: <<<'PHP' - deprecated(); - $this->notDeprecated(); - } - - public function notDeprecated(): void {} - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals('Call to deprecated method "deprecated": This is deprecated', $diagnostics->at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'deprecated on property', - source: <<<'PHP' - deprecated; - $ba = $this->notDeprecated; - } - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals('Call to deprecated property "deprecated": This is deprecated', $diagnostics->at(0)->message()); - } - ); - } - - /** - * @param MemberAccessContext $resolved - * @return Generator - */ - private function memberAccessDiagnostics(MemberAccessContext $resolved): Generator - { - $member = $resolved->accessedMember(); - if (!$member->deprecation()->isDefined()) { - return; - } - - yield new DeprecatedUsageDiagnostic( - $resolved->memberNameRange(), - $member->name(), - $member->deprecation()->message(), - $member->memberType(), - ); - } - /** - * @return Generator - */ - private function classLikeDiagnostics(ClassLikeContext $resolved): Generator - { - $reflectionClass = $resolved->classLike(); - if (!$reflectionClass->deprecation()->isDefined()) { - return; - } - - yield new DeprecatedUsageDiagnostic( - $resolved->range(), - $reflectionClass->name(), - $reflectionClass->deprecation()->message(), - $reflectionClass->classLikeType(), - ); - } - /** - * @return Generator - */ - private function functionDiagnostics(FunctionCallContext $resolved): Generator - { - $reflectionFunction = $resolved->function(); - if (!$reflectionFunction->docblock()->deprecation()->isDefined()) { - return; - } - - yield new DeprecatedUsageDiagnostic( - $resolved->range(), - $reflectionFunction->name(), - $reflectionFunction->docblock()->deprecation()->message(), - 'function', - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/Docblock/ClassGenericDiagnosticHelper.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/Docblock/ClassGenericDiagnosticHelper.php deleted file mode 100644 index 065499de11..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/Docblock/ClassGenericDiagnosticHelper.php +++ /dev/null @@ -1,141 +0,0 @@ - - */ - public function diagnosticsForExtends( - ClassReflector $reflector, - ByteOffsetRange $range, - ReflectionClassLike $class, - ?ReflectionClassLike $parentClass - ): Generator { - if ($class instanceof ReflectionClass) { - yield from $this->fromReflectionClass($reflector, $range, $class, $parentClass, $class->docblock()->extends(), '@extends'); - } - } - /** - * @return Generator - */ - public function diagnosticsForImplements(Reflector $reflector, ByteOffsetRange $range, ReflectionClassLike $class, ?ReflectionClassLike $genericClass): Generator - { - if ($class instanceof ReflectionClass) { - yield from $this->fromReflectionClass( - $reflector, - $range, - $class, - $genericClass, - $class->docblock()->implements(), - '@implements' - ); - } - } - - /** - * @return Generator - * @param Type[] $genericTypes - */ - private function fromReflectionClass( - ClassReflector $reflector, - ByteOffsetRange $range, - ReflectionClassLike $class, - ?ReflectionClassLike $parentClass, - array $genericTypes, - string $tagName - ): Generator { - if (!$parentClass) { - return; - } - - $templateMap = $parentClass->templateMap(); - - if (!count($templateMap)) { - return; - } - - $genericTypes = array_filter( - $genericTypes, - fn (Type $extendTagType) => $parentClass->type()->accepts($extendTagType)->isTrue() - ); - - $defaultGenericType = new GenericClassType( - $reflector, - $parentClass->name(), - array_map( - fn (Type $type) => $type instanceof MissingType ? new MixedType() : $type, - $templateMap->toArguments(), - ) - ); - - if (0 === count($genericTypes)) { - yield new DocblockMissingClassGenericDiagnostic( - $range, - $class->name(), - $defaultGenericType, - $tagName - ); - return; - } - - - $extendTagType = $genericTypes[array_key_first($genericTypes)]; - - // if generic uses a templateed type, then replace the templated type - // with the type restriction if it exists (e.g. replace T with Foo if - // @template T of Foo) - if ($extendTagType instanceof GenericClassType) { - $classTemplateMap = $class->templateMap(); - $extendTagType = $extendTagType->withArguments(array_map(function (Type $type) use ($classTemplateMap) { - return $classTemplateMap->getOrGiven($type); - }, $extendTagType->arguments())); - } - - if ($parentClass->type()->upcastToGeneric()->accepts($extendTagType)->isFalse()) { - yield new DocblockIncorrectClassGenericDiagnostic( - $range, - $extendTagType, - $defaultGenericType, - $tagName - ); - return; - } - - if (!$extendTagType instanceof GenericClassType) { - yield new DocblockIncorrectClassGenericDiagnostic( - $range, - $extendTagType, - $defaultGenericType, - $tagName - ); - return; - } - - if ($defaultGenericType->accepts($extendTagType)->isTrue()) { - return; - } - - yield new DocblockIncorrectClassGenericDiagnostic( - $range, - $extendTagType, - $defaultGenericType, - $tagName - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockIncorrectClassGenericDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockIncorrectClassGenericDiagnostic.php deleted file mode 100644 index f3f4c6ddd8..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockIncorrectClassGenericDiagnostic.php +++ /dev/null @@ -1,51 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return DiagnosticSeverity::WARNING(); - } - - public function message(): string - { - return sprintf( - 'Generic tag `%s %s` should be compatible with `%s %s`', - $this->tagName, - $this->givenType->short(), - $this->tagName, - $this->correctType->short() - ); - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'docblock_incorrect_class_generic'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingClassGenericDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingClassGenericDiagnostic.php deleted file mode 100644 index 77a27f2c5a..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingClassGenericDiagnostic.php +++ /dev/null @@ -1,65 +0,0 @@ -missingGenericType; - } - - public function range(): ByteOffsetRange - { - return $this->range; - } - - public function severity(): DiagnosticSeverity - { - return DiagnosticSeverity::WARNING(); - } - - public function className(): ClassName - { - return $this->className; - } - - public function message(): string - { - return sprintf( - 'Missing generic tag `%s %s`', - $this->tagName, - $this->missingGenericType->short() - ); - } - - public function isExtends(): bool - { - return $this->tagName === '@extends'; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'docblock_missing_class_generic'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingExtendsTagProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingExtendsTagProvider.php deleted file mode 100644 index c0cc11cb5f..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingExtendsTagProvider.php +++ /dev/null @@ -1,353 +0,0 @@ -name) { - return; - } - - $range = ByteOffsetRange::fromInts( - $node->name->getStartPosition(), - $node->name->getEndPosition() - ); - - try { - $class = $resolver->reflector()->reflectClassLike($node->getNamespacedName()->__toString()); - } catch (NotFound) { - return; - } - - if ($class instanceof ReflectionClass) { - yield from $this->helper->diagnosticsForExtends($resolver->reflector(), $range, $class, $class->parent()); - } - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } - - public function examples(): iterable - { - yield new DiagnosticExample( - title: 'extends class requiring generic annotation', - source: <<<'PHP' - `', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'does not provide enough arguments', - source: <<<'PHP' - - */ - class Foobar extends NeedGeneric - { - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals( - 'Generic tag `@extends NeedGeneric` should be compatible with `@extends NeedGeneric`', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'does not provide any arguments', - source: <<<'PHP' - `', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'provides empty arguments', - source: <<<'PHP' - - */ - class Foobar extends NeedGeneric - { - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals( - 'Missing generic tag `@extends NeedGeneric`', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'wrong class', - source: <<<'PHP' - - */ - class Foobar extends NeedGeneric - { - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals( - 'Missing generic tag `@extends NeedGeneric`', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'does not provide multiple arguments', - source: <<<'PHP' - - */ - class Foobar extends NeedGeneric - { - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals( - 'Generic tag `@extends NeedGeneric` should be compatible with `@extends NeedGeneric`', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'extends class not requiring generic annotation', - source: <<<'PHP' - - */ - class Foobar extends NeedGeneric - { - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - - yield new DiagnosticExample( - title: 'considers the namespace', - source: <<<'PHP' - - */ - class ScheduleFactory extends Factory - { - } - - class Schedule extends Model - { - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'extend with typed templated argument', - source: <<<'PHP' - - */ - class ScheduleFactory extends Factory {} - - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'extend with unconstrained argument', - source: <<<'PHP' - - */ - class ScheduleFactory extends Factory {} - - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingImplementsTagProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingImplementsTagProvider.php deleted file mode 100644 index e25472ee7a..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingImplementsTagProvider.php +++ /dev/null @@ -1,186 +0,0 @@ -name) { - return; - } - - $range = ByteOffsetRange::fromInts( - $node->name->getStartPosition(), - $node->name->getEndPosition() - ); - - try { - $class = $resolver->reflector()->reflectClassLike($node->getNamespacedName()->__toString()); - } catch (NotFound) { - return; - } - - if ($class instanceof ReflectionClass) { - /** @phpstan-ignore-next-line TP Lies */ - foreach ($node->classInterfaceClause?->interfaceNameList?->getChildNodes() ?? [] as $implementedInterface) { - if (!$implementedInterface instanceof QualifiedName) { - continue; - } - try { - $name = (string)$implementedInterface->getResolvedName(); - $implementedInterface = $resolver->reflector()->reflectClassLike($name); - } catch (NotFound) { - continue; - } - if (!$implementedInterface instanceof ReflectionInterface) { - continue; - } - yield from $this->helper->diagnosticsForImplements($resolver->reflector(), $range, $class, $implementedInterface); - } - } - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } - - public function examples(): iterable - { - yield new DiagnosticExample( - title: 'implements class requiring generic annotation', - source: <<<'PHP' - `', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'does not provide enough arguments', - source: <<<'PHP' - - */ - class Foobar implements NeedGeneric - { - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals( - 'Generic tag `@implements NeedGeneric` should be compatible with `@implements NeedGeneric`', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'provides one but not another', - source: <<<'PHP' - - */ - class Foobar implements NeedGeneric1, NeedGeneric2 - { - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals( - 'Missing generic tag `@implements NeedGeneric2`', - $diagnostics->at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'iterator', - source: <<<'PHP' - - */ - class Foobar implements IteratorAggregate - { - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingParamDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingParamDiagnostic.php deleted file mode 100644 index 5e061d6eb3..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingParamDiagnostic.php +++ /dev/null @@ -1,67 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return $this->severity; - } - - public function message(): string - { - return $this->message; - } - - public function classType(): string - { - return $this->classType; - } - - public function methodName(): string - { - return $this->methodName; - } - - public function paramName(): string - { - return $this->paramName; - } - - public function paramType(): Type - { - return $this->paramType; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'docblock_missing_param'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingParamProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingParamProvider.php deleted file mode 100644 index 0083e4bab5..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingParamProvider.php +++ /dev/null @@ -1,324 +0,0 @@ -name) { - return; - } - - $declaration = NodeUtil::nodeContainerClassLikeDeclaration($node); - - if (null === $declaration) { - return; - } - - try { - $class = $resolver->reflector()->reflectClassLike($declaration->getNamespacedName()->__toString()); - $methodName = $node->name->getText($node->getFileContents()); - if (!is_string($methodName)) { - return; - } - $method = $class->methods()->get($methodName); - } catch (NotFound) { - return; - } - - // do not try it for overriden methods - if ($method->original()->declaringClass()->name() != $class->name()) { - return; - } - - $docblock = $method->docblock(); - $docblockParams = $docblock->params(); - $docblockVars = new DocBlockVars([]); - $missingParams = []; - - foreach ($method->parameters() as $parameter) { - $type = $parameter->type(); - $type = $this->upcastType($type, $resolver); - $parameterType = $parameter->type(); - - if ($docblockParams->has($parameter->name())) { - continue; - } - - if ($method->name() === '__construct') { - $vars = $parameter->docblock()->vars(); - if ($vars->count() > 0) { - continue; - } - } - - if ($parameter->isVariadic()) { - if ($type instanceof ArrayType) { - $type = $type->iterableValueType(); - } - if ($parameterType instanceof ArrayType) { - $parameterType = $parameterType->iterableValueType(); - } - } - - if ($type instanceof ArrayType) { - $type = new ArrayType(TypeFactory::int(), TypeFactory::mixed()); - } - if ($type::class === PseudoIterableType::class) { - $type = new PseudoIterableType(TypeFactory::int(), TypeFactory::mixed()); - } - - // replace with "mixed" - $type = $type->map(fn (Type $type) => $type instanceof MissingType ? new MixedType() : $type); - - if ($type->__toString() === $parameterType->__toString()) { - continue; - } - - yield new DocblockMissingParamDiagnostic( - ByteOffsetRange::fromInts( - $parameter->position()->start()->toInt(), - $parameter->position()->end()->toInt() - ), - sprintf( - 'Method "%s" is missing @param $%s', - $methodName, - $parameter->name(), - ), - DiagnosticSeverity::WARNING(), - $class->name()->__toString(), - $methodName, - $parameter->name(), - $type, - ); - } - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } - - public function examples(): iterable - { - yield new DiagnosticExample( - title: 'closure', - source: <<<'PHP' - byClass(DocblockMissingParamDiagnostic::class); - Assert::assertCount(1, $diagnostics); - Assert::assertEquals('Method "foo" is missing @param $foobar', $diagnostics->at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'generator', - source: <<<'PHP' - byClass(DocblockMissingParamDiagnostic::class); - Assert::assertCount(1, $diagnostics); - Assert::assertEquals('Method "foo" is missing @param $foobar', $diagnostics->at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'iterable', - source: <<<'PHP' - byClass(DocblockMissingParamDiagnostic::class); - Assert::assertCount(1, $diagnostics); - Assert::assertEquals('Method "foo" is missing @param $foobar', $diagnostics->at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'array', - source: <<<'PHP' - byClass(DocblockMissingParamDiagnostic::class); - Assert::assertCount(1, $diagnostics); - Assert::assertEquals('Method "foo" is missing @param $foobar', $diagnostics->at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'no false positive for union of scalars', - source: <<<'PHP' - $foobar - */ - public function foo(array $foobar) { - } - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'no false positive array shape with string literals', - source: <<<'PHP' - - */ - private array $foobar, - private array $barfoo - ) { - } - } - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'does not report diagnostic on method with @param', - source: <<<'PHP' - byClass(DocblockMissingParamDiagnostic::class); - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'variadic', - source: <<<'PHP' - name()->__toString() === 'Closure') { - return new ClosureType($resolver->reflector(), [], TypeFactory::void()); - } - - return $type->upcastToGeneric(); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingReturnTypeDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingReturnTypeDiagnostic.php deleted file mode 100644 index 36fd8187b1..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingReturnTypeDiagnostic.php +++ /dev/null @@ -1,60 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return $this->severity; - } - - public function message(): string - { - return $this->message; - } - - public function classType(): string - { - return $this->classType; - } - - public function methodName(): string - { - return $this->methodName; - } - - public function actualReturnType(): string - { - return $this->actualReturnType; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'docblock_missing_return_type'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingReturnTypeProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingReturnTypeProvider.php deleted file mode 100644 index 147bce446e..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/DocblockMissingReturnTypeProvider.php +++ /dev/null @@ -1,155 +0,0 @@ -name) { - return; - } - - $declaration = NodeUtil::nodeContainerClassLikeDeclaration($node); - - if (null === $declaration) { - return; - } - - try { - $class = $resolver->reflector()->reflectClassLike($declaration->getNamespacedName()->__toString()); - $methodName = $node->name->getText($node->getFileContents()); - if (!is_string($methodName)) { - return; - } - $method = $class->methods()->get($methodName); - } catch (NotFound) { - return; - } - - $docblockType = $method->docblock()->returnType(); - $actualReturnType = $frame->returnType()->generalize(); - $claimedReturnType = $method->inferredType(); - $phpReturnType = $method->type(); - - // if there is already a return type, ignore. phpactor's guess - // will currently likely be wrong often. - if ($method->docblock()->returnType()->isDefined()) { - return; - } - - // do not try it for overriden methods - if ($method->original()->declaringClass()->name() != $class->name()) { - return; - } - - if ($method->name() === '__construct' || $method->name() === '__destruct') { - return; - } - - // it's void - if (false === $actualReturnType->isDefined()) { - return; - } - - if ($claimedReturnType->isDefined() - && !$claimedReturnType->isClass() - && !$claimedReturnType->isArray() - && !$claimedReturnType->isClosure() - && !$claimedReturnType->isIterable() - ) { - return; - } - - - if ($actualReturnType->isClosure()) { - yield new DocblockMissingReturnTypeDiagnostic( - $method->nameRange(), - sprintf( - 'Method "%s" is missing docblock return type: %s', - $methodName, - $actualReturnType->__toString(), - ), - DiagnosticSeverity::WARNING(), - $class->name()->__toString(), - $methodName, - $actualReturnType->__toString(), - ); - return; - } - - if ($claimedReturnType->isClass() && !$actualReturnType instanceof GenericClassType) { - return; - } - - if ($actualReturnType->isMixed() && ($claimedReturnType->isArray() || $claimedReturnType->isIterable())) { - return; - } - - // the docblock matches the generalized return type - // it's OK - if ($claimedReturnType->equals($actualReturnType)) { - return; - } - - yield new DocblockMissingReturnTypeDiagnostic( - $method->nameRange(), - sprintf( - 'Method "%s" is missing docblock return type: %s', - $methodName, - $actualReturnType->__toString(), - ), - DiagnosticSeverity::WARNING(), - $class->name()->__toString(), - $methodName, - $actualReturnType->__toString(), - ); - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } - - public function examples(): iterable - { - yield new DiagnosticExample( - title: 'method without return type', - source: <<<'PHP' - range; - } - - public function severity(): DiagnosticSeverity - { - return $this->severity; - } - - public function message(): string - { - return $this->message; - } - - public function classType(): string - { - return $this->classType; - } - - public function methodName(): string - { - return $this->methodName; - } - - public function memberType(): string - { - return $this->memberType; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'missing_member'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingMemberProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingMemberProvider.php deleted file mode 100644 index 109c5ad065..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingMemberProvider.php +++ /dev/null @@ -1,316 +0,0 @@ -parent instanceof CallExpression) - ) { - return; - } - - $memberName = null; - $memberType = null; - if ($node instanceof ScopedPropertyAccessExpression) { - $memberName = $node->memberName; - } elseif ($node->callableExpression instanceof MemberAccessExpression) { - $memberName = $node->callableExpression->memberName; - } elseif ($node->callableExpression instanceof ScopedPropertyAccessExpression) { - $memberName = $node->callableExpression->memberName; - } - - if (!($memberName instanceof Token)) { - return; - } - - $containerType = $resolver->resolveNode($frame, $node)->containerType(); - - if (!$containerType->isDefined()) { - return; - } - - if (!$containerType instanceof ReflectedClassType) { - return; - } - - $reflection = $containerType->reflectionOrNull(); - if (null === $reflection) { - return; - } - - $methodName = $memberName->getText($node->getFileContents()); - if (!is_string($methodName)) { - return; - } - - $memberTypes = (function (ReflectionClassLike $reflection) use ($node) { - if ($node instanceof ScopedPropertyAccessExpression) { - $types = [ReflectionMember::TYPE_CONSTANT]; - - if ($reflection instanceof ReflectionEnum) { - $types[] = ReflectionMember::TYPE_CASE; - } - - return $types; - } - return [ReflectionMember::TYPE_METHOD]; - })($reflection); - - - $found = false; - foreach ($memberTypes as $memberType) { - try { - $containerType->members()->byMemberType($memberType)->get($methodName); - } catch (NotFound) { - continue; - } - $found = true; - } - - if (!$found) { - yield new MissingMemberDiagnostic( - ByteOffsetRange::fromInts( - $memberName->getStartPosition(), - $memberName->getEndPosition() - ), - sprintf( - '%s "%s" does not exist on %s "%s"', - ucfirst($memberType), - $methodName, - $reflection->classLikeType(), - $containerType->__toString() - ), - DiagnosticSeverity::ERROR(), - $containerType->name()->__toString(), - $methodName, - $memberType, - ); - } - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } - - public function examples(): iterable - { - yield new DiagnosticExample( - title: 'inlined type', - source: <<<'PHP' - isInvokable()) { - } - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'does not report call on type inferred previously in expressio', - source: <<<'PHP' - isInvokable()) { - } - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'missing method on instance ', - source: <<<'PHP' - bar(); - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(1, $diagnostics); - Assert::assertEquals('Method "bar" does not exist on class "Foobar"', $diagnostics->at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'missing method for static invocation', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'missing enum case', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'enum contains const and case', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'missing property on class is not supported yet', - source: <<<'PHP' - foo = 12; - $f->barfoo = 'string'; - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingReturnTypeDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingReturnTypeDiagnostic.php deleted file mode 100644 index 3b6bf156c8..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingReturnTypeDiagnostic.php +++ /dev/null @@ -1,68 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return DiagnosticSeverity::WARNING(); - } - - public function message(): string - { - if (!$this->returnType->isDefined()) { - return sprintf( - 'Method "%s" is missing return type and the type could not be determined', - $this->methodName - ); - } - return sprintf( - 'Missing return type `%s`', - $this->returnType->toPhpString(), - ); - } - - public function classType(): string - { - return $this->classType; - } - - public function methodName(): string - { - return $this->methodName; - } - - public function returnType(): Type - { - return $this->returnType; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'missing_return_type'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingReturnTypeProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingReturnTypeProvider.php deleted file mode 100644 index 66a1a74e17..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingReturnTypeProvider.php +++ /dev/null @@ -1,214 +0,0 @@ -at(0)->message() - ); - } - ); - yield new DiagnosticExample( - title: 'does not report missing return type on _construct', - source: <<<'PHP' - at(0)->message() - ); - } - ); - } - public function exit(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - if (!$node instanceof MethodDeclaration) { - return; - } - - $methodName = NodeUtil::nameFromTokenOrNode($node, $node->name); - - if (!$methodName) { - return; - } - - if ($node->returnTypeList) { - return; - } - - $type = $resolver->resolveNode($frame, $node)->containerType(); - - if (!$type instanceof ReflectedClassType) { - return; - } - - $reflection = $type->reflectionOrNull(); - - if (!$reflection) { - return; - } - - // if it's an interface we can't determine the return type - if ($reflection instanceof ReflectionInterface) { - return; - } - - $methods = $reflection->methods()->belongingTo($reflection->name())->byName($methodName); - - if (0 === count($methods)) { - return; - } - - $method = $methods->first(); - - if ($method->isAbstract()) { - return; - } - - if ($method->name() === '__construct') { - return; - } - - if ($method->name() === '__destruct') { - return; - } - - if ($method->type()->isDefined()) { - return; - } - - if ($method->docblock()->returnType()->isMixed()) { - return; - } - - if ($method->class()->templateMap()->has($method->docblock()->returnType()->__toString())) { - return; - } - - $returnType = $frame->returnType(); - - $docblockReturnType = $method->docblock()->returnType(); - if ($docblockReturnType->isDefined() && $docblockReturnType->accepts($returnType)->isTrue()) { - return; - } - - yield new MissingReturnTypeDiagnostic( - $method->nameRange(), - $reflection->name()->__toString(), - $methodName, - $returnType->generalize()->reduce() - ); - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableDiagnostic.php deleted file mode 100644 index f0839ed646..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableDiagnostic.php +++ /dev/null @@ -1,75 +0,0 @@ - $suggestions - */ - public function __construct( - private ByteOffsetRange $byteOffsetRange, - private string $varName, - private array $suggestions - ) { - } - - public function range(): ByteOffsetRange - { - return $this->byteOffsetRange; - } - - public function severity(): DiagnosticSeverity - { - return DiagnosticSeverity::ERROR(); - } - - public function message(): string - { - if (count($this->suggestions) === 0) { - return sprintf( - 'Undefined variable "$%s"', - $this->varName - ); - } - $suggestString = implode('", "$', $this->suggestions); - if (count($this->suggestions) === 1) { - return sprintf( - 'Undefined variable "$%s", did you mean "$%s"', - $this->varName, - $suggestString - ); - } - return sprintf( - 'Undefined variable "$%s", did you mean one of "$%s"', - $this->varName, - $suggestString - ); - } - /** - * @return list - */ - public function suggestions(): array - { - return $this->suggestions; - } - - public function undefinedVariableName(): string - { - return $this->varName; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'undefined_variable'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php deleted file mode 100644 index 360d95ecf6..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php +++ /dev/null @@ -1,532 +0,0 @@ -at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'property', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'from vardoc', - source: <<<'PHP' - bar; - } - - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'is catch receiver', - source: <<<'PHP' - at(0)->message()); - } - ); - - yield new DiagnosticExample( - title: 'after for loop', - source: <<<'PHP' - $data) { - $list[$index] = $data; - } - - return $list; - PHP, - valid: false, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - - yield new DiagnosticExample( - title: 'static', - source: <<<'PHP' - "; - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - - yield new DiagnosticExample( - title: 'local globals', - source: <<<'PHP' - foo($var1, $var2); - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'multiple @var declarations followed by binary expression in statement', - source: <<<'PHP' - 10, 'b' => 2]]; - foreach ($test as ['a' => $a, 'b' => $b]) {} - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: '@var declaration in global context', - source: <<<'PHP' - parent instanceof PropertyElement) { - return []; - } - if ($node->parent instanceof ScopedPropertyAccessExpression) { - return []; - } - - if (!$name = $node->getName()) { - return []; - } - - $global = SuperGlobals::list()[$name] ?? null; - - if ($global) { - return []; - } - - foreach ($frame->locals()->byName($name) as $variable) { - if ($variable->wasDefinition()) { - return []; - } - } - - yield new UndefinedVariableDiagnostic( - NodeUtil::byteOffsetRangeForNode($node), - $name, - array_filter(array_map(function (PhpactorVariable $var) { - return $var->name(); - }, $frame->locals()->definitionsOnly()->mostRecent()->toArray()), function (string $candidate) use ($name) { - return levenshtein($name, $candidate) < $this->suggestionLevensteinDistance; - }) - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnresolvableNameDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnresolvableNameDiagnostic.php deleted file mode 100644 index b9940b24a9..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnresolvableNameDiagnostic.php +++ /dev/null @@ -1,72 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return DiagnosticSeverity::ERROR(); - } - - public function message(): string - { - return sprintf('%s "%s" not found', ucfirst($this->type), $this->name->head()->__toString()); - } - - /** - * @return self::TYPE_* - */ - public function type(): string - { - return $this->type; - } - - public function name(): Name - { - return $this->name; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return 'unresolved_name'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnresolvableNameProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnresolvableNameProvider.php deleted file mode 100644 index bb60628925..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnresolvableNameProvider.php +++ /dev/null @@ -1,370 +0,0 @@ - - */ - private array $functionCache = []; - - public function __construct(private bool $importGlobals) - { - } - - public function exit(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - if (!$node instanceof QualifiedName) { - return; - } - - $name = $node; - - // Tolerant parser does not resolve names for constructs that define symbol names or aliases - $resolvedName = TolerantQualifiedNameResolver::getResolvedName($name); - - // strange getResolvedName method returns a string if this is a - // reserved name (e.g. static, iterable). do not return these as - // "unresolved" - if ($resolvedName && !$resolvedName instanceof ResolvedName) { - return; - } - - // Parser returns "NULL" for unqualified namespaced function / constant - // names, but will return the FQN for references... - if (!$resolvedName && $name->parent instanceof CallExpression) { - yield from $this->forFunction( - $resolver->reflector(), - $name->getNamespacedName()->__toString(), - $name, - ); - return; - } - - // if the tolerant parser did not provide the resolved name (because of - // bug) then use the namespaced name. - if (!$resolvedName) { - $resolvedName = $name->getNamespacedName(); - } - - if (count($resolvedName->getNameParts()) == 0) { - return; - } - - // Function names in global namespace have a "resolved name" - // (inconsistent parser behavior) - if ($name->parent instanceof CallExpression) { - yield from $this->forFunction( - $resolver->reflector(), - $name->getResolvedName() ?? $name->getText(), - $name, - ); - return; - } - - $parent = $name->parent; - if ( - !$parent instanceof ClassBaseClause && - !$parent instanceof QualifiedNameList && - !$parent instanceof ObjectCreationExpression && - !$parent instanceof ScopedPropertyAccessExpression && - !$parent instanceof FunctionDeclaration && - !$parent instanceof MethodDeclaration && - !$parent instanceof Attribute && - !($parent instanceof BinaryExpression && $parent->operator->kind === TokenKind::InstanceOfKeyword) - ) { - return; - } - - yield from $this->forClass( - $resolver->reflector(), - $resolvedName, - $name, - ); - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - if ($node instanceof SourceFileNode) { - $this->functionCache = []; - } - return []; - } - - public function examples(): iterable - { - yield new DiagnosticExample( - title: 'class name constant unresolvable', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'reserved names', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'instanceof class', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'unresolvable class', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'unresolvable namespaced function', - source: <<<'PHP' - at(0)->message()); - } - ); - } - - /** - * @return iterable - */ - private function forFunction(FunctionReflector $reflector, string $fqn, QualifiedName $name): iterable - { - $fqn = PhpactorFullyQualifiedName::fromString($fqn); - if (isset($this->functionCache[$fqn->__toString()])) { - } - - try { - // see comment for appendUnresolvedClassName - $source = $reflector->sourceCodeForFunction($fqn->__toString()); - if (!$this->nameContainedInSource('function', $source, $fqn->head()->__toString())) { - throw new NotFound(); - } - } catch (NotFound) { - // if we are not importing globals then check the global namespace - if (false === $this->importGlobals) { - try { - $source = $reflector->sourceCodeForFunction($fqn->head()->__toString()); - if ($this->nameContainedInSource('function', $source, $fqn->head()->__toString())) { - return; - } - } catch (NotFound) { - } - } - $this->functionCache[$fqn->__toString()] = true; - - yield UnresolvableNameDiagnostic::forFunction( - ByteOffsetRange::fromInts($name->getStartPosition(), $name->getEndPosition()), - $fqn, - ); - } - } - - private function nameContainedInSource(string $declarationPattern, TextDocument $source, string $nameText): bool - { - $lastPart = explode('\\', $nameText); - $last = $lastPart[array_key_last($lastPart)]; - - if ($source->__toString() === '') { - return false; - } - - return (bool)preg_match(sprintf('{%s\s+%s}', $declarationPattern, $last), $source->__toString()); - } - - /** - * @return iterable - */ - private function forClass(ClassReflector $reflector, string $fqn, QualifiedName $name): iterable - { - $fqn = PhpactorFullyQualifiedName::fromString($fqn); - if (isset($this->functionCache[$fqn->__toString()])) { - } - - try { - // we could reflect the class here but it's much more expensive - // than simply locating the source, however locating the source - // does not _guarantee_ that the name exists, so we additionally - // ensure that at least the short name of the FQN is located in - // the source code. - $source = $reflector->sourceCodeForClassLike($fqn->__toString()); - if (!$this->nameContainedInSource('(class|trait|interface|enum)', $source, $fqn->head()->__toString())) { - throw new NotFound(); - } - } catch (NotFound) { - $this->functionCache[$fqn->__toString()] = true; - yield UnresolvableNameDiagnostic::forClass( - ByteOffsetRange::fromInts($name->getStartPosition(), $name->getEndPosition()), - $fqn, - ); - } - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportDiagnostic.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportDiagnostic.php deleted file mode 100644 index 3fb97847a3..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportDiagnostic.php +++ /dev/null @@ -1,52 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return DiagnosticSeverity::WARNING(); - } - - public function message(): string - { - return sprintf('Name "%s" is imported but not used', $this->name); - } - - public function name(): string - { - return $this->name; - } - - public function tags(): array - { - return [DiagnosticTag::UNNECESSARY]; - } - - public function code(): string - { - return 'unused_import'; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportProvider.php deleted file mode 100644 index dd5ad052c7..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportProvider.php +++ /dev/null @@ -1,509 +0,0 @@ - - */ - private array $usedPrefixes = []; - - /** - * @var array - */ - private array $imported = []; - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - $docblock = $resolver->docblockFactory()->create( - $node->getLeadingCommentAndWhitespaceText(), - new ReflectionScope($resolver->reflector(), $node) - ); - - if ($docblock instanceof ParsedDocblock) { - $this->extractDocblockNames($docblock->rawNode(), $resolver, $node); - } - - if ($node instanceof QualifiedName && !$node->parent instanceof NamespaceUseClause && !$node->parent instanceof NamespaceDefinition && !$node->parent instanceof NamespaceUseGroupClause) { - $prefix = $node->getNameParts()[0]; - if (!$prefix instanceof Token) { - return []; - } - $usedPrefix = $this->prefixedName($node, (string)$prefix->getText($node->getFileContents())); - $this->usedPrefixes[$usedPrefix] = true; - return []; - } - - if ($node instanceof NamespaceUseClause) { - if ($node->groupClauses) { - foreach ($node->groupClauses->children as $groupClause) { - if (!$groupClause instanceof NamespaceUseGroupClause) { - continue; - } - $useClause = $groupClause->parent->parent; - if (!$useClause instanceof NamespaceUseClause) { - continue; - } - - $this->imported[$this->prefixedName($groupClause, $groupClause->__toString())] = $groupClause; - } - return []; - } - - $prefix = (function (Node $clause): string { - /** @phpstan-ignore-next-line TP lies */ - if ($clause->namespaceAliasingClause) { - return $this->prefixedName($clause, (string)$clause->namespaceAliasingClause->name->getText($clause->getFileContents())); - } - /** @phpstan-ignore-next-line TP lies */ - $lastPart = $this->lastPart((string)$clause->namespaceName); - return $this->prefixedName($clause, $lastPart); - })($node); - - $this->imported[$prefix] = $node; - return []; - } - - return []; - } - - public function exit(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - if (!$node instanceof SourceFileNode) { - return []; - } - - $contents = $node->getFileContents(); - - foreach ($this->imported as $importedName => $imported) { - if (isset($this->usedPrefixes[$importedName])) { - continue; - } - - // see if the imported name is used by an annotation - if ($this->usedByAnnotation($contents, $importedName, $imported)) { - continue; - } - - // scan all usages and check if imported name is used relatively - foreach (array_keys($this->usedPrefixes) as $prefix) { - if (0 === strpos($prefix, $importedName . '\\')) { - continue 2; - } - } - - yield UnusedImportDiagnostic::for( - ByteOffsetRange::fromInts($imported->getStartPosition(), $imported->getEndPosition()), - explode(':', $importedName)[1] - ); - } - - $this->imported = []; - $this->usedPrefixes = []; - - return []; - } - - public function examples(): iterable - { - yield new DiagnosticExample( - title: 'aliased import', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'aliased for used', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'compact namespaced use', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'gh-1866', - source: <<<'PHP' - name instanceof StringLiteralType) { - } - return Name::fromString($this->name); - } - } - PHP, - valid: true, - assertion: function (Diagnostics $diagnostics): void { - Assert::assertCount(0, $diagnostics); - } - ); - yield new DiagnosticExample( - title: 'namespaced unused imports', - source: <<<'PHP' - at(0)->message()); - } - ); - yield new DiagnosticExample( - title: 'used by complex docblock', - source: <<<'PHP' - getNamespaceName($node)); - foreach ($docblock->descendantElements(ClassNode::class) as $type) { - $this->usedPrefixes[$prefix . $type->toString()] = true; - } - foreach ($docblock->descendantElements(CallableNode::class) as $type) { - assert($type instanceof CallableNode); - if ($type->name->toString() === 'Closure') { - $this->usedPrefixes[$prefix . 'Closure'] = true; - } - } - } - - /** - * @param Node|Token $node - */ - private function usedByAnnotation(string $contents, string $imported, $node): bool - { - $imported = explode(':', $imported)[1]; - return str_contains($contents, '@' . $imported); - } - - /** @phpstan-ignore-next-line TP lies */ - private function lastPart(string $name): string - { - $parts = array_filter(explode('\\', $name)); - if (!$parts) { - return ''; - } - return $parts[array_key_last($parts)]; - } - - private function prefixedName(Node $node, string $name): string - { - return sprintf('%s:%s', $this->getNamespaceName($node), $name); - } - - private function getNamespaceName(Node $node): string - { - $definition = $node->getNamespaceDefinition(); - if (null === $definition) { - return ''; - } - if (!$definition->name instanceof QualifiedName) { - return ''; - } - return (string)$definition->name; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php b/lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php deleted file mode 100644 index 741b06e176..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php +++ /dev/null @@ -1,140 +0,0 @@ - - */ - private const UNRESOLVABLE_KEYWORD = ['self', 'static', 'parent', 'iterable']; - - /** - * @see \Microsoft\PhpParser\Node\QualifiedName::getResolvedName - */ - public static function getResolvedName($node, $namespaceDefinition = null) - { - // Name resolution not applicable to constructs that define symbol names or aliases. - if (($node->parent instanceof NamespaceDefinition && $node->parent->name->getStartPosition() === $node->getStartPosition()) || - $node->parent instanceof NamespaceUseDeclaration || - $node->parent instanceof NamespaceUseClause || - $node->parent instanceof NamespaceUseGroupClause || - //$node->parent->parent instanceof Node\TraitUseClause || - $node->parent instanceof TraitSelectOrAliasClause || - ($node->parent instanceof TraitSelectOrAliasClause && - ($node->parent->asOrInsteadOfKeyword == null || $node->parent->asOrInsteadOfKeyword->kind === TokenKind::AsKeyword)) - ) { - return null; - } - - if (array_search($lowerText = strtolower($node->getText()), self::UNRESOLVABLE_KEYWORD) !== false) { - return $lowerText; - } - - // FULLY QUALIFIED NAMES - // - resolve to the name without leading namespace separator. - if ($node->isFullyQualifiedName()) { - return ResolvedName::buildName($node->nameParts, $node->getFileContents()); - } - - // RELATIVE NAMES - // - resolve to the name with namespace replaced by the current namespace. - // - if current namespace is global, strip leading namespace\ prefix. - if ($node->isRelativeName()) { - return $node->getNamespacedName(); - } - - [$namespaceImportTable, $functionImportTable, $constImportTable] = $node->getImportTablesForCurrentScope(); - - // QUALIFIED NAMES - // - first segment of the name is translated according to the current class/namespace import table. - // - If no import rule applies, the current namespace is prepended to the name. - if ($node->isQualifiedName()) { - return self::tryResolveFromImportTable($node, $namespaceImportTable) ?? $node->getNamespacedName(); - } - - // UNQUALIFIED NAMES - // - translated according to the current import table for the respective symbol type. - // (class-like => namespace import table, constant => const import table, function => function import table) - // - if no import rule applies: - // - all symbol types: if current namespace is global, resolve to global namespace. - // - class-like symbols: resolve from current namespace. - // - function or const: resolved at runtime (from current namespace, with fallback to global namespace). - if (self::isConstantName($node)) { - $resolvedName = self::tryResolveFromImportTable($node, $constImportTable, /* case-sensitive */ true); - $namespaceDefinition = $node->getNamespaceDefinition(); - if ($namespaceDefinition !== null && $namespaceDefinition->name === null) { - $resolvedName = $resolvedName ?? ResolvedName::buildName($node->nameParts, $node->getFileContents()); - } - return $resolvedName; - } elseif ($node->parent instanceof CallExpression) { - $resolvedName = self::tryResolveFromImportTable($node, $functionImportTable); - if (($namespaceDefinition = $node->getNamespaceDefinition()) === null || $namespaceDefinition->name === null) { - $resolvedName = $resolvedName ?? ResolvedName::buildName($node->nameParts, $node->getFileContents()); - } - return $resolvedName; - } - - return self::tryResolveFromImportTable($node, $namespaceImportTable) ?? $node->getNamespacedName(); - } - - /** - * @param ResolvedName[] $importTable - * @return null - */ - private static function tryResolveFromImportTable($node, $importTable, bool $isCaseSensitive = false) - { - $content = $node->getFileContents(); - $index = $node->nameParts[0]->getText($content); - // if (!$isCaseSensitive) { - // $index = strtolower($index); - // } - if (isset($importTable[$index])) { - $resolvedName = $importTable[$index]; - $resolvedName->addNameParts(\array_slice($node->nameParts, 1), $content); - return $resolvedName; - } - return null; - } - - private static function isConstantName($node) : bool - { - return - ($node->parent instanceof ExpressionStatement || $node->parent instanceof Expression) && - !( - $node->parent instanceof MemberAccessExpression || $node->parent instanceof CallExpression || - $node->parent instanceof ObjectCreationExpression || - $node->parent instanceof ScopedPropertyAccessExpression || $node->parent instanceof AnonymousFunctionCreationExpression || - ($node->parent instanceof BinaryExpression && $node->parent->operator->kind === TokenKind::InstanceOfKeyword) - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectedNode.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectedNode.php deleted file mode 100644 index f89f80dd9a..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectedNode.php +++ /dev/null @@ -1,28 +0,0 @@ -node()->getStartPosition(), - $this->node()->getEndPosition() - ); - } - - public function scope(): CoreReflectionScope - { - return new ReflectionScope($this->serviceLocator()->reflector(), $this->node()); - } - - abstract protected function node(): Node; - - abstract protected function serviceLocator(): ServiceLocator; -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionClass.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionClass.php deleted file mode 100644 index b890a9ddcd..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionClass.php +++ /dev/null @@ -1,84 +0,0 @@ -docblock()->deprecation(); - } - - public function templateMap(): TemplateMap - { - return $this->docblock()->templateMap(); - } - - public function type(): ReflectedClassType - { - return TypeFactory::reflectedClass($this->serviceLocator()->reflector(), $this->name()); - } - - abstract public function classLikeType(): string; - - protected function resolveTraitMethods( - TraitImports $traitImports, - ReflectionClassLike $contextClass, - ReflectionTraitCollection $traits - ): PhpactorReflectionMethodCollection { - $methods = PhpactorReflectionMethodCollection::empty(); - - foreach ($traitImports as $traitImport) { - try { - $trait = $traits->get($traitImport->name()); - } catch (NotFound) { - continue; - } - - $traitMethods = []; - foreach ($trait->methods($contextClass) as $method) { - if (false === $traitImport->hasAliasFor($method->name())) { - $traitMethods[] = $method; - continue; - } - - $traitAlias = $traitImport->getAlias($method->name()); - - $virtualMethod = VirtualReflectionMethod::fromReflectionMethod($trait->methods()->get($traitAlias->originalName())) - ->withName($traitAlias->newName()) - ->withVisibility($traitAlias->visiblity($method->visibility())); - - $traitMethods[] = $virtualMethod; - } - $methods = $methods->merge(PhpactorReflectionMethodCollection::fromReflectionMethods($traitMethods)); - } - - return $methods; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionClassMember.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionClassMember.php deleted file mode 100644 index a8f7d30421..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionClassMember.php +++ /dev/null @@ -1,144 +0,0 @@ -node()->getFirstAncestor(ClassLike::class); - - assert($classDeclaration instanceof NamespacedNameInterface); - - $class = $classDeclaration->getNamespacedName(); - - if (null === $class) { - throw new InvalidArgumentException(sprintf( - 'Could not locate class-like ancestor node for member "%s"', - $this->name() - )); - } - - return $this->serviceLocator()->reflector()->reflectClassLike(ClassName::fromString($class)); - } - - public function original(): ReflectionMember - { - return (new OriginalMethodResolver())->resolveOriginalMember($this); - } - - public function frame(): Frame - { - return new LazyFrame($this->serviceLocator()->frameBuilder(), $this->node()); - } - - public function docblock(): DocBlock - { - return $this->serviceLocator()->docblockFactory()->create( - $this->node()->getLeadingCommentAndWhitespaceText(), - $this->scope() - ); - } - - public function visibility(): Visibility - { - $node = $this->node(); - if (!$node instanceof PropertyDeclaration && !$node instanceof ClassConstDeclaration && !$node instanceof MethodDeclaration) { - return Visibility::public(); - } - foreach ($node->modifiers as $token) { - if ($token->kind === TokenKind::PrivateKeyword) { - return Visibility::private(); - } - - if ($token->kind === TokenKind::ProtectedKeyword) { - return Visibility::protected(); - } - } - - return Visibility::public(); - } - - public function deprecation(): Deprecation - { - return $this->docblock()->deprecation(); - } - - public function position(): ByteOffsetRange - { - if (null === $this->node()->getFirstChildNode(AttributeGroup::class)) { - return parent::position(); - } - - $tokenKind = match ($this->memberType()) { - ReflectionMember::TYPE_PROPERTY => TokenKind::VariableName, - ReflectionMember::TYPE_METHOD => TokenKind::FunctionKeyword, - ReflectionMember::TYPE_CONSTANT => TokenKind::ConstKeyword, - ReflectionMember::TYPE_CASE => TokenKind::CaseKeyword, - }; - - $name = $this->findDescendantNamedToken($tokenKind); - - if (null === $name) { - return parent::position(); - } - - return ByteOffsetRange::fromInts( - $name->getStartPosition(), - $this->node()->getEndPosition() - ); - } - - abstract protected function serviceLocator(): ServiceLocator; - - private function findDescendantNamedToken(int $tokenBeforeKind): ?Token - { - $found = false; - - foreach ($this->node()->getDescendantTokens() as $token) { - if (true === $found) { - if ($tokenBeforeKind !== TokenKind::ConstKeyword) { - return $token->kind === TokenKind::Name ? $token : null; - } - - if ($token->kind === TokenKind::Name && $token->getText($this->node()->getFileContents()) === $this->name()) { - return $token; - } - } - - if ($token->kind !== $tokenBeforeKind) { - continue; - } - - if ($tokenBeforeKind === TokenKind::VariableName) { - return $token; - } - - $found = true; - } - - return null; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionMethodCall.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionMethodCall.php deleted file mode 100644 index 659c1b900b..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionMethodCall.php +++ /dev/null @@ -1,150 +0,0 @@ -node->getStartPosition(), - $this->node->getEndPosition() - ); - } - - public function class(): ReflectionClassLike - { - $info = $this->services->nodeContextResolver()->resolveNode($this->frame, $this->node); - $containerType = $info->containerType(); - - if (!$containerType instanceof ReflectedClassType) { - throw new CouldNotResolveNode(sprintf( - 'Class for member "%s" could not be determined', - $this->name() - )); - } - - $reflection = $containerType->reflectionOrNull(); - - if (null === $reflection) { - throw new CouldNotResolveNode(sprintf( - 'Class for member "%s" could not be determined', - $this->name() - )); - } - - return $reflection; - } - - abstract public function isStatic(): bool; - - public function arguments(): ReflectionArgumentCollection - { - if (null === $this->callExpression()->argumentExpressionList) { - return ReflectionArgumentCollection::empty(); - } - - return ReflectionArgumentCollection::fromArgumentListAndFrame( - $this->services, - $this->callExpression()->argumentExpressionList, - $this->frame - ); - } - - public function name(): string - { - return NodeUtil::nameFromTokenOrNode($this->node, $this->node->memberName); - } - - - public function method(): ReflectionMethod - { - $class = $this->class(); - return $class->methods()->get($this->name()); - } - - public function inferredReturnType(): Type - { - $return = $this->node->getFirstAncestor(ReturnStatement::class); - if ($return) { - $functionLike = $this->containingFunctionLike(); - if (null === $functionLike) { - return new MissingType(); - } - return $this->class()->scope()->resolveLocalType($functionLike->inferredType()); - } - - return new MissingType(); - } - - public function scope(): ReflectionScope - { - return new ReflectionScope($this->services->reflector(), $this->node); - } - public function nameRange(): ByteOffsetRange - { - $memberName = $this->node->memberName; - return ByteOffsetRange::fromInts( - $memberName->getStartPosition(), - $memberName->getEndPosition() - ); - } - - private function callExpression(): CallExpression - { - if (!$this->node->parent instanceof CallExpression) { - throw new RuntimeException('Method call is not a child of a call expression'); - } - - return $this->node->parent; - } - - private function containingFunctionLike(): ?ReflectionFunctionLike - { - $method = $this->node->getFirstAncestor(MethodDeclaration::class); - if ($method instanceof MethodDeclaration) { - $class = $this->class(); - if ($class instanceof ReflectionClass) { - try { - return $class->methods()->get($method->getName()); - } catch (ItemNotFound) { - } - } - } - - return null; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ClassInvocation.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ClassInvocation.php deleted file mode 100644 index e6eb0272d9..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ClassInvocation.php +++ /dev/null @@ -1,12 +0,0 @@ -node->name instanceof Token) { - return NodeUtil::nameFromTokenOrQualifiedName($this->node, $this->node->name); - } - - if ($this->node->expression instanceof Variable) { - $name = $this->node->expression->name->getText($this->node->getFileContents()); - - if (is_string($name) && str_starts_with($name, '$')) { - return substr($name, 1); - } - - return (string)$name; - } - - $type = $this->type(); - - if (!$type instanceof MissingType) { - $stringify = function (Type $type) { - $type = $type->stripNullable(); - if ($type instanceof ClassType) { - return lcfirst($type->short()); - } - return lcfirst($type->toPhpString()); - }; - if ($type instanceof AggregateType) { - return lcfirst(implode('', array_map(ucfirst(...), array_map($stringify, $type->types)))); - } - return $stringify($type); - } - - - return 'argument' . $this->index(); - } - - public function type(): Type - { - return $this->nodeContext()->type(); - } - - public function value(): mixed - { - return TypeUtil::valueOrNull($this->nodeContext()->type()); - } - - public function position(): ByteOffsetRange - { - return ByteOffsetRange::fromInts( - $this->node->getStartPosition(), - $this->node->getEndPosition() - ); - } - - public function nodeContext(): NodeContext - { - return $this->services->nodeContextResolver()->resolveNode($this->frame, $this->node); - } - - private function index(): int - { - $index = 0; - - /** @var ArgumentExpressionList $parent */ - $parent = $this->node->parent; - - foreach ($parent->getElements() as $element) { - if ($element === $this->node) { - return $index; - } - $index ++; - } - - throw new RuntimeException( - 'Could not find myself in the list of my parents children' - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionAttribute.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionAttribute.php deleted file mode 100644 index 01f76df4ec..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionAttribute.php +++ /dev/null @@ -1,70 +0,0 @@ -locator->reflector(), $this->node); - } - - public function position(): ByteOffsetRange - { - return ByteOffsetRange::fromInts( - $this->node->getStartPosition(), - $this->node->getEndPosition() - ); - } - - public function class(): ReflectionClassLike - { - $type = $this->locator->nodeContextResolver()->resolveNode($this->frame, $this->node->name)->type(); - - if (!$type instanceof ReflectedClassType) { - throw new CouldNotResolveNode(sprintf('Expceted "%s" but got "%s"', ReflectedClassType::class, get_class($type))); - } - - $reflection = $type->reflectionOrNull(); - - if (null === $reflection) { - throw new CouldNotResolveNode( - 'Could not reflect class' - ); - } - - return $reflection; - } - - public function arguments(): ReflectionArgumentCollection - { - if (null === $this->node->argumentExpressionList) { - return ReflectionArgumentCollection::empty(); - } - - return ReflectionArgumentCollection::fromArgumentListAndFrame( - $this->locator, - $this->node->argumentExpressionList, - $this->frame - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionClass.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionClass.php deleted file mode 100644 index 121e314c46..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionClass.php +++ /dev/null @@ -1,371 +0,0 @@ - $visited - */ - public function __construct( - private ServiceLocator $serviceLocator, - private TextDocument $sourceCode, - private ClassDeclaration $node, - private array $visited = [] - ) { - } - - public function isAbstract(): bool - { - $modifier = $this->node->abstractOrFinalModifier; - - /** @phpstan-ignore-next-line */ - if (!$modifier) { - return false; - } - - return $modifier->kind === TokenKind::AbstractKeyword; - } - - /** - * @return ReflectionMemberCollection - */ - public function members(): ReflectionMemberCollection - { - if ($this->members) { - return $this->members; - } - $members = ClassLikeReflectionMemberCollection::empty(); - $providedMembers = ClassLikeReflectionMemberCollection::empty(); - foreach ($this->hierarchy() as $reflectionClassLike) { - $classLikeMembers = $reflectionClassLike->ownMembers(); - /** @phpstan-ignore-next-line collection IS compatible */ - $providedMembers = $providedMembers->merge($this->serviceLocator->methodProviders()->provideMembers( - $this->serviceLocator, - $reflectionClassLike - )); - - // only inerit public and protected properties from parent classes - if ($reflectionClassLike !== $this && !$reflectionClassLike instanceof ReflectionTrait) { - $classLikeMembers = $classLikeMembers->byVisibilities([Visibility::public(), Visibility::protected()]); - } - - // we only take constants from interfaces, methods must be implemented. - if ($reflectionClassLike instanceof ReflectionInterface) { - /** @phpstan-ignore-next-line collection IS compatible */ - $members = $members->merge($classLikeMembers->constants()); - /** @phpstan-ignore-next-line collection IS compatible */ - $members = $members->merge($classLikeMembers->virtual()); - continue; - } - - /** @phpstan-ignore-next-line Constants is compatible with this */ - $members = $members->merge($classLikeMembers); - - // we need to account for traits renaming aliases - if ($reflectionClassLike instanceof ReflectionTrait) { - $traitImports = TraitImports::forClassDeclaration($this->node); - /** @phpstan-ignore-next-line collection IS compatible */ - $members = $members->merge($this->resolveTraitMethods($traitImports, $this, $this->traits())); - continue; - } - } - $members = $members->merge($providedMembers); - $this->members = $members->map(fn (ReflectionMember $member) => $member->withClass($this)); - - return $this->members; - } - - public function ownMembers(): ReflectionMemberCollection - { - if ($this->ownMembers) { - return $this->ownMembers; - } - $this->ownMembers = ClassLikeReflectionMemberCollection::fromClassMemberDeclarations( - $this->serviceLocator, - $this->node, - $this - ); - return $this->ownMembers; - } - - public function constants(): ReflectionConstantCollection - { - return $this->members()->constants(); - } - - public function parent(): ?CoreReflectionClass - { - if ($this->parent) { - return $this->parent; - } - - /** @phpstan-ignore-next-line */ - if (!$this->node->classBaseClause) { - return null; - } - - $baseClass = $this->node->classBaseClause->baseClass; - - // incomplete class - if (!$baseClass instanceof QualifiedName) { - return null; - } - - try { - $className = ClassName::fromString((string) $this->node->classBaseClause->baseClass->getResolvedName()); - - // prevent infinite loops - if ($className == $this->name()) { - return null; - } - - $reflectedClass = $this->serviceLocator->reflector()->reflectClassLike( - $className, - $this->visited, - ); - - if (!$reflectedClass instanceof CoreReflectionClass) { - $this->serviceLocator->logger()->warning(sprintf( - 'Class cannot extend interface. Class "%s" extends interface or trait "%s"', - $this->name(), - $reflectedClass->name() - )); - return null; - } - - $this->parent = $reflectedClass; - - return $reflectedClass; - } catch (NotFound) { - return null; - } - } - - public function properties(?ReflectionClassLike $contextClass = null): ReflectionPropertyCollection - { - return $this->members()->properties(); - } - - public function methods(?ReflectionClassLike $contextClass = null): ReflectionMethodCollection - { - return $this->members()->methods(); - } - - public function interfaces(): ReflectionInterfaceCollection - { - if ($this->interfaces) { - return $this->interfaces; - } - - $parentInterfaces = null; - foreach ($this->ancestors() as $ancestor) { - $parentInterfaces = $ancestor->interfaces(); - } - - $interfaces = ReflectionInterfaceCollection::fromClassDeclaration($this->serviceLocator, $this->node); - - if ($parentInterfaces) { - $interfaces = $parentInterfaces->merge($interfaces); - } - - foreach ($interfaces as $interface) { - $interfaces = $interfaces->merge($interface->parents()); - } - - $this->interfaces = $interfaces; - - return $interfaces; - } - - /** - * @return ReflectionTraitCollection - */ - public function traits(): ReflectionTraitCollection - { - if ($this->traits) { - return $this->traits; - } - $parentTraits = null; - - if ($this->parent()) { - $parentTraits = $this->parent()->traits(); - } - - $traits = ReflectionTraitCollection::fromClassDeclaration($this->serviceLocator, $this->node); - - if ($parentTraits) { - $traits = $parentTraits->merge($traits); - } - - $this->traits = $traits; - - return $traits; - } - - public function memberListPosition(): ByteOffsetRange - { - return ByteOffsetRange::fromInts( - $this->node->classMembers->openBrace->start, - $this->node->classMembers->openBrace->start + $this->node->classMembers->openBrace->length - ); - } - - public function name(): ClassName - { - if ($this->name) { - return $this->name; - } - $this->name = ClassName::fromString((string) $this->node->getNamespacedName()); - return $this->name; - } - - public function isInstanceOf(ClassName $className): bool - { - if ($className == $this->name()) { - return true; - } - - // do not try and reflect the parents if we can locally see that it is - // an instance of the given class - $baseClause = $this->node->classBaseClause; - if ($baseClause instanceof ClassBaseClause) { - NodeUtil::qualfiiedNameIs($baseClause->baseClass, $className->__toString()); - } - - // do not try and reflect the parents if we can locally see that it is - // an instance of the given class - $baseClause = $this->node->classInterfaceClause; - if ($baseClause instanceof ClassInterfaceClause) { - if (NodeUtil::qualifiedNameListContains($baseClause->interfaceNameList, $className->__toString())) { - return true; - } - } - - if ($this->ancestors()->has((string)$className)) { - return true; - } - - return $this->interfaces()->has((string) $className); - } - - public function sourceCode(): TextDocument - { - return $this->sourceCode; - } - - public function isConcrete(): bool - { - return !$this->isAbstract(); - } - - public function docblock(): DocBlock - { - return $this->serviceLocator->docblockFactory()->create( - $this->node()->getLeadingCommentAndWhitespaceText(), - $this->scope() - ); - } - - public function ancestors(): ReflectionClassCollection - { - if ($this->ancestors) { - return $this->ancestors; - } - $ancestors = []; - $class = $this; - - while ($parent = $class->parent()) { - if (isset($ancestors[$parent->name()->full()])) { - unset($ancestors[$parent->name()->full()]); - break; - } - - $ancestors[$parent->name()->full()] = $parent; - - $class = $parent; - } - - $this->ancestors = ReflectionClassCollection::fromReflections($ancestors); - return $this->ancestors; - } - - public function isFinal(): bool - { - $modifier = $this->node->abstractOrFinalModifier; - - /** @phpstan-ignore-next-line */ - if (!$modifier) { - return false; - } - - return $modifier->kind === TokenKind::FinalKeyword; - } - - public function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } - - public function hierarchy(): ReflectionClassLikeCollection - { - return ReflectionClassLikeCollection::fromReflections((new ClassHierarchyResolver())->resolve($this)); - } - - public function classLikeType(): string - { - return 'class'; - } - - protected function node(): Node - { - return $this->node; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionConstant.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionConstant.php deleted file mode 100644 index e48ae1ddc7..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionConstant.php +++ /dev/null @@ -1,106 +0,0 @@ -resolver = new DeclaredMemberTypeResolver($serviceLocator->reflector()); - } - - public function name(): string - { - return (string)$this->node->getName(); - } - - public function nameRange(): ByteOffsetRange - { - return ByteOffsetRange::fromInts( - $this->node->name->getStartPosition(), - $this->node->name->getEndPosition() - ); - } - - public function type(): Type - { - // if constant has an explicit type then use that - if ($this->declaration->typeDeclarationList) { - return $this->resolver->resolve($this->declaration, $this->declaration->typeDeclarationList); - } - - // @deprecated for B/C we should return undefined rather than infer type from the value - // in order to be consistent with other class members - return $this->inferredType(); - } - - public function class(): ReflectionClassLike - { - return $this->class; - } - - public function inferredType(): Type - { - $value = $this->serviceLocator->nodeContextResolver()->resolveNode(new ConcreteFrame(), $this->node->assignment); - return $value->type(); - } - - public function isVirtual(): bool - { - return false; - } - - public function value() - { - return TypeUtil::valueOrNull($this->serviceLocator() - ->nodeContextResolver() - ->resolveNode( - new ConcreteFrame(), - $this->node->assignment - )->type()); - } - - public function memberType(): string - { - return ReflectionMember::TYPE_CONSTANT; - } - - public function withClass(ReflectionClassLike $class): ReflectionMember - { - return new self($this->serviceLocator, $class, $this->declaration, $this->node); - } - - public function isStatic(): bool - { - return true; - } - - protected function node(): Node - { - return $this->declaration; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionConstantAccess.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionConstantAccess.php deleted file mode 100644 index aa77ee2bb3..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionConstantAccess.php +++ /dev/null @@ -1,41 +0,0 @@ -node->getStartPosition(), - $this->node->getEndPosition() - ); - } - - public function name(): string - { - return NodeUtil::nameFromTokenOrNode($this->node, $this->node->memberName); - } - - public function nameRange(): ByteOffsetRange - { - $memberName = $this->node->memberName; - return ByteOffsetRange::fromInts( - $memberName->getStartPosition(), - $memberName->getEndPosition() - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionDeclaredConstant.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionDeclaredConstant.php deleted file mode 100644 index b4236a5a3e..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionDeclaredConstant.php +++ /dev/null @@ -1,92 +0,0 @@ -bindArguments(); - } - - public function name(): Name - { - return Name::fromString($this->name); - } - - public function type(): Type - { - // gh-2913: do not try and lookup constant values in order to avoid - // infinite loops. - // - // @phpstan-ignore instanceof.alwaysFalse - if ($this->value->expression instanceof QualifiedName) { - return TypeFactory::unknown(); - } - return $this->serviceLocator->nodeContextResolver()->resolveNode(new ConcreteFrame(), $this->value)->type(); - } - - public function sourceCode(): TextDocument - { - return $this->sourceCode; - } - - public function docblock(): DocBlock - { - return $this->serviceLocator->docblockFactory()->create($this->node->getLeadingCommentAndWhitespaceText(), $this->scope()); - } - - protected function node(): Node - { - return $this->node; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } - - private function bindArguments(): void - { - $arguments = $this->node->argumentExpressionList; - if (!$arguments) { - return; - } - $arguments = iterator_to_array($arguments->getElements()); - if (!is_array($arguments)) { - return; - } - if (isset($arguments[0]) && $arguments[0] instanceof ArgumentExpression) { - if (!$arguments[0]->expression instanceof StringLiteral) { - $this->name = '?'; - } else { - $this->name = $arguments[0]->expression->getStringContentsText(); - } - } - - if (isset($arguments[1]) && $arguments[1] instanceof ArgumentExpression) { - $this->value = $arguments[1]; - } - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php deleted file mode 100644 index a5072c2c82..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php +++ /dev/null @@ -1,158 +0,0 @@ -members()->methods(); - } - - public function cases(): ReflectionEnumCaseCollection - { - return $this->ownMembers()->enumCases(); - } - - /** - * @return ReflectionMemberCollection - */ - public function members(): ReflectionMemberCollection - { - $members = ClassLikeReflectionMemberCollection::empty(); - /** @phpstan-ignore-next-line Constants is compatible with this */ - $members = $members->merge($this->ownMembers()); - foreach ($this->traits() as $trait) { - /** @phpstan-ignore-next-line Constants is compatible with this */ - $members = $members->merge($trait->members()); - } - try { - $enumType = $this->isBacked() ? 'BackedEnum' : 'UnitEnum'; - $interface = $this->serviceLocator()->reflector()->reflectInterface($enumType); - $enumMethods = $interface->members(); - /** @phpstan-ignore-next-line It is fine */ - return $members->merge($enumMethods)->map( - fn (ReflectionMember $member) => $member->withClass($this) - ); - } catch (NotFound) { - } - - return $members; - } - - public function ownMembers(): ReflectionMemberCollection - { - return ClassLikeReflectionMemberCollection::fromEnumMemberDeclarations( - $this->serviceLocator, - $this->node, - $this - ); - } - - public function properties(): CoreReflectionPropertyCollection - { - return $this->members()->properties(); - } - - public function name(): ClassName - { - return ClassName::fromString((string) $this->node()->getNamespacedName()); - } - - public function sourceCode(): TextDocument - { - return $this->sourceCode; - } - - public function isInstanceOf(ClassName $className): bool - { - if ($className == $this->name()) { - return true; - } - - return false; - } - - public function docblock(): DocBlock - { - return $this->serviceLocator->docblockFactory()->create( - $this->node()->getLeadingCommentAndWhitespaceText(), - $this->scope() - ); - } - - public function isBacked(): bool - { - return $this->node->enumType !== null; - } - - public function backedType(): Type - { - return NodeUtil::typeFromQualfiedNameLike($this->serviceLocator()->reflector(), $this->node, $this->node->enumType); - } - - public function classLikeType(): string - { - return 'enum'; - } - - public function traits(): ReflectionTraitCollection - { - if ($this->traits) { - return $this->traits; - } - - $traits = ReflectionTraitCollection::fromEnumDeclaration($this->serviceLocator, $this->node); - - $this->traits = $traits; - - return $traits; - } - - public function constants(): ReflectionConstantCollection - { - return $this->members()->constants(); - } - - /** - * @return EnumDeclaration - */ - protected function node(): Node - { - return $this->node; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnumCase.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnumCase.php deleted file mode 100644 index 1f6ca42f9b..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnumCase.php +++ /dev/null @@ -1,124 +0,0 @@ -node->name; - if ($name instanceof Token) { - return (string)$name->getText($this->node->getFileContents()); - } - if ($name instanceof QualifiedName) { - return $name->__toString(); - } - - throw new RuntimeException('This should not happen'); - } - - public function nameRange(): ByteOffsetRange - { - $name = $this->node->name; - return ByteOffsetRange::fromInts($name->getStartPosition(), $name->getEndPosition()); - } - - public function type(): Type - { - if ($this->class()->isBacked()) { - return TypeFactory::enumBackedCaseType($this->serviceLocator()->reflector(), $this->class()->type(), $this->name(), $this->value()); - } - return TypeFactory::enumCaseType($this->serviceLocator()->reflector(), $this->class()->type(), $this->name()); - } - - /** - * @return ReflectionEnum - */ - public function class(): ReflectionClassLike - { - return $this->enum; - } - - public function inferredType(): Type - { - if (TypeFactory::unknown() !== $this->type()) { - return $this->type(); - } - - return TypeFactory::undefined(); - } - - public function isVirtual(): bool - { - return false; - } - - - public function value(): Type - { - if ($this->node->assignment === null) { - return new MissingType(); - } - - return $this->serviceLocator() - ->nodeContextResolver() - ->resolveNode( - new ConcreteFrame(), - $this->node->assignment - )->type(); - } - - public function memberType(): string - { - return ReflectionMember::TYPE_CASE; - } - - public function withClass(ReflectionClassLike $class): ReflectionMember - { - if (!$class instanceof ReflectionEnum) { - throw new RuntimeException( - 'Cannot make case member part of a non-enum reflection' - ); - } - - return new self($this->serviceLocator, $class, $this->node); - } - - public function isStatic(): bool - { - return true; - } - - protected function node(): Node - { - return $this->node; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionFunction.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionFunction.php deleted file mode 100644 index da63fb6cca..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionFunction.php +++ /dev/null @@ -1,91 +0,0 @@ -node->getNamespacedName()->getNameParts()); - } - - public function frame(): Frame - { - return $this->serviceLocator->frameBuilder()->build($this->node()); - } - - public function docblock(): DocBlock - { - return $this->serviceLocator->docblockFactory()->create( - $this->node()->getLeadingCommentAndWhitespaceText(), - $this->scope() - ); - } - - public function inferredType(): Type - { - return (new FunctionReturnTypeResolver($this))->resolve(); - } - - public function type(): Type - { - $type = NodeUtil::typeFromQualfiedNameLike( - $this->serviceLocator->reflector(), - $this->node, - $this->node->returnTypeList - ); - - if ($this->node->questionToken) { - return TypeFactory::nullable($type); - } - - return $type; - } - - public function parameters(): TolerantReflectionParameterCollection - { - return TolerantReflectionParameterCollection::fromFunctionDeclaration($this->serviceLocator, $this->node, $this); - } - - public function body(): NodeText - { - return NodeText::fromString($this->node->__toString()); - } - - public function sourceCode(): TextDocument - { - return $this->sourceCode; - } - - protected function node(): Node - { - return $this->node; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionInterface.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionInterface.php deleted file mode 100644 index f2a2922ae4..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionInterface.php +++ /dev/null @@ -1,168 +0,0 @@ - $visited - */ - public function __construct( - private ServiceLocator $serviceLocator, - private TextDocument $sourceCode, - private InterfaceDeclaration $node, - private array $visited = [] - ) { - } - - /** - * @return ReflectionMemberCollection - */ - public function members(): ReflectionMemberCollection - { - if ($this->members) { - return $this->members; - } - $members = ClassLikeReflectionMemberCollection::empty(); - foreach ($this->hierarchy() as $reflectionClassLike) { - /** @phpstan-ignore-next-line */ - $members = $members->merge($reflectionClassLike->ownMembers()); - } - - $this->members = $members->map(fn (ReflectionMember $member) => $member->withClass($this)); - return $this->members; - } - - public function ownMembers(): ReflectionMemberCollection - { - if ($this->ownMembers) { - return $this->ownMembers; - } - $members = ClassLikeReflectionMemberCollection::fromInterfaceMemberDeclarations( - $this->serviceLocator, - $this->node, - $this - ); - /** @phpstan-ignore-next-line collection IS compatible */ - $members = $members->merge($this->serviceLocator->methodProviders()->provideMembers( - $this->serviceLocator, - $this - )); - $this->ownMembers = $members; - return $this->ownMembers; - } - - public function constants(): CoreReflectionConstantCollection - { - return $this->members()->constants(); - } - - public function parents(): CoreReflectionInterfaceCollection - { - if ($this->parents) { - return $this->parents; - } - - $this->parents = CoreReflectionInterfaceCollection::fromInterfaceDeclaration($this->serviceLocator, $this->node, $this->visited); - - return $this->parents; - } - - public function isInstanceOf(ClassName $className): bool - { - if ($className == $this->name()) { - return true; - } - - // do not try and reflect the parents if we can locally see that it is - // an instance of the given class - $baseClause = $this->node->interfaceBaseClause; - if ($baseClause instanceof InterfaceBaseClause) { - if (NodeUtil::qualifiedNameListContains($baseClause->interfaceNameList, $className->__toString())) { - return true; - } - } - - foreach ($this->parents() as $parent) { - if ($parent->isInstanceOf($className)) { - return true; - } - } - - return false; - } - - public function methods(?ReflectionClassLike $contextClass = null): CoreReflectionMethodCollection - { - return $this->members()->methods(); - } - - public function name(): ClassName - { - return ClassName::fromString((string) $this->node()->getNamespacedName()); - } - - public function sourceCode(): TextDocument - { - return $this->sourceCode; - } - - public function docblock(): DocBlock - { - return $this->serviceLocator->docblockFactory()->create( - $this->node()->getLeadingCommentAndWhitespaceText(), - $this->scope() - ); - } - - public function hierarchy(): ReflectionClassLikeCollection - { - return ReflectionClassLikeCollection::fromReflections((new ClassHierarchyResolver())->resolve($this)); - } - - public function classLikeType(): string - { - return 'interface'; - } - - /** - * @return InterfaceDeclaration - */ - protected function node(): Node - { - return $this->node; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMatchExpression.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMatchExpression.php deleted file mode 100644 index b17f8a6123..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMatchExpression.php +++ /dev/null @@ -1,46 +0,0 @@ -node->getStartPosition(), - $this->node->getEndPosition() - ); - } - - public function expressionType(): Type - { - if ($this->node->expression === null) { - return TypeFactory::unknown(); - } - $expr = $this->services->nodeContextResolver()->resolveNode($this->frame, $this->node->expression); - return $expr->type(); - } - public function scope(): ReflectionScope - { - return new ReflectionScope($this->services->reflector(), $this->node); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethod.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethod.php deleted file mode 100644 index b290fe5a90..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethod.php +++ /dev/null @@ -1,180 +0,0 @@ -returnTypeResolver = new MethodTypeResolver($this); - $this->memberTypeResolver = new DeclaredMemberTypeResolver($this->serviceLocator->reflector()); - $this->typeContextualiser = new MemberTypeContextualiser(); - } - - public function name(): string - { - if ($this->name) { - return $this->name; - } - $this->name = (string)$this->node->getName(); - return $this->name; - } - - public function nameRange(): ByteOffsetRange - { - $name = $this->node->name; - return ByteOffsetRange::fromInts($name->getStartPosition(), $name->getEndPosition()); - } - - public function declaringClass(): ReflectionClassLike - { - $classDeclaration = $this->node->getFirstAncestor(ClassLike::class); - - assert($classDeclaration instanceof NamespacedNameInterface); - $class = $classDeclaration->getNamespacedName(); - - - /** @phpstan-ignore-next-line */ - if (null === $class) { - throw new InvalidArgumentException(sprintf( - 'Could not locate class-like ancestor node for method "%s"', - $this->name() - )); - } - - - $className = ClassName::fromString($class); - if ($className == $this->class()->name()) { - return $this->class(); - } - return $this->serviceLocator->reflector()->reflectClassLike($className); - } - - public function parameters(): CoreReflectionParameterCollection - { - return CoreReflectionParameterCollection::fromMethodDeclaration($this->serviceLocator, $this->node, $this); - } - - public function inferredType(): Type - { - $type = $this->typeContextualiser->contextualise( - $this->declaringClass(), - $this->class(), - $this->returnTypeResolver->resolve($this->class()) - ); - - if (($type->isDefined())) { - return $type; - } - - return $this->type(); - } - - /** - * @deprecated use type() - */ - public function returnType(): Type - { - return $this->type(); - } - - public function type(): Type - { - return $this->memberTypeResolver->resolve( - $this->node, - $this->node->returnTypeList, - $this->class()->name(), - $this->node->questionToken ? true : false - ); - } - - public function body(): NodeText - { - $statement = $this->node->compoundStatementOrSemicolon; - if (!$statement instanceof CompoundStatementNode) { - return NodeText::fromString(''); - } - $statements = $statement->statements; - return NodeText::fromString(implode("\n", array_reduce($statements, function ($acc, $statement) { - $acc[] = (string) $statement->getText(); - return $acc; - }, []))); - } - - public function class(): ReflectionClassLike - { - return $this->class; - } - - public function isStatic(): bool - { - return $this->node->isStatic(); - } - - public function isAbstract(): bool - { - foreach ($this->node->modifiers as $token) { - if ($token->kind === TokenKind::AbstractKeyword) { - return true; - } - } - - return false; - } - - public function isVirtual(): bool - { - return false; - } - - public function memberType(): string - { - return ReflectionMember::TYPE_METHOD; - } - - public function withClass(ReflectionClassLike $class): ReflectionMember - { - return new self($this->serviceLocator, $class, $this->node); - } - - protected function node(): Node - { - return $this->node; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethodCall.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethodCall.php deleted file mode 100644 index 5270a84f1a..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethodCall.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public function methodCalls(): NavigatorElementCollection - { - $calls = []; - foreach ($this->node->getDescendantNodes() as $node) { - if ($node instanceof ScopedPropertyAccessExpression) { - if (!$node->parent instanceof CallExpression) { - continue; - } - $calls[] = new ReflectionStaticMethodCall($this->locator, new ConcreteFrame(), $node); - continue; - } - if ($node instanceof MemberAccessExpression) { - if (!$node->parent instanceof CallExpression) { - continue; - } - $calls[] = new ReflectionMethodCall($this->locator, new ConcreteFrame(), $node); - continue; - } - } - return new NavigatorElementCollection($calls); - } - - public function at(ByteOffset $offset): self - { - return new self($this->locator, $this->node->getDescendantNodeAtPosition($offset->toInt())); - } - - /** - * @return NavigatorElementCollection - */ - public function propertyAccesses(): NavigatorElementCollection - { - $elements = []; - foreach ($this->node->getDescendantNodes() as $node) { - if ($node instanceof ScopedPropertyAccessExpression) { - $elements[] = new ReflectionStaticMemberAccess($this->locator, new ConcreteFrame(), $node); - continue; - } - if (!$node instanceof MemberAccessExpression) { - continue; - } - if ($node->parent instanceof CallExpression) { - continue; - } - - $elements[] = new ReflectionPropertyAccess($node); - } - - return new NavigatorElementCollection($elements); - } - - /** - * @return NavigatorElementCollection - */ - public function constantAccesses(): NavigatorElementCollection - { - $elements = []; - foreach ($this->node->getDescendantNodes() as $node) { - if (!$node instanceof ScopedPropertyAccessExpression) { - continue; - } - if ($node->parent instanceof CallExpression) { - continue; - } - - $elements[] = new ReflectionConstantAccess($node); - } - return new NavigatorElementCollection($elements); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionObjectCreationExpression.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionObjectCreationExpression.php deleted file mode 100644 index d171d5b2c4..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionObjectCreationExpression.php +++ /dev/null @@ -1,70 +0,0 @@ -locator->reflector(), $this->node); - } - - public function position(): ByteOffsetRange - { - return ByteOffsetRange::fromInts( - $this->node->getStartPosition(), - $this->node->getEndPosition() - ); - } - - public function class(): ReflectionClassLike - { - $type = $this->locator->nodeContextResolver()->resolveNode($this->frame, $this->node->classTypeDesignator)->type(); - - if (!$type instanceof ReflectedClassType) { - throw new CouldNotResolveNode(sprintf('Expceted "%s" but got "%s"', ReflectedClassType::class, get_class($type))); - } - - $reflection = $type->reflectionOrNull(); - - if (null === $reflection) { - throw new CouldNotResolveNode( - 'Could not reflect class' - ); - } - - return $reflection; - } - - public function arguments(): ReflectionArgumentCollection - { - if (null === $this->node->argumentExpressionList) { - return ReflectionArgumentCollection::empty(); - } - - return ReflectionArgumentCollection::fromArgumentListAndFrame( - $this->locator, - $this->node->argumentExpressionList, - $this->frame - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionOffset.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionOffset.php deleted file mode 100644 index e135a3e4a5..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionOffset.php +++ /dev/null @@ -1,31 +0,0 @@ -frame; - } - - public function nodeContext(): NodeContext - { - return $this->nodeContext; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionParameter.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionParameter.php deleted file mode 100644 index 8b560dcd44..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionParameter.php +++ /dev/null @@ -1,126 +0,0 @@ -memberTypeResolver = new DeclaredMemberTypeResolver($serviceLocator->reflector()); - } - - public function name(): string - { - if (null === $this->parameter->getName()) { - $this->serviceLocator->logger()->warning(sprintf( - 'Parameter has no variable at offset "%s"', - $this->parameter->getStartPosition() - )); - return ''; - } - - return $this->parameter->getName(); - } - - public function type(): Type - { - $className = $this->functionLike instanceof ReflectionMethod ? $this->functionLike->class()->name() : null; - - $type = $this->memberTypeResolver->resolve( - $this->parameter, - $this->parameter->typeDeclarationList, - $className, - $this->parameter->questionToken ? true : false - ); - - if ($this->parameter->dotDotDotToken) { - return new ArrayType(null, $type); - } - - return $type; - } - - public function inferredType(): Type - { - return (new ParameterTypeResolver( - $this, - new GenericMapResolver( - $this->serviceLocator()->reflector() - ) - ))->resolve(); - } - - public function default(): DefaultValue - { - if (null === $this->parameter->default) { - return DefaultValue::undefined(); - } - $value = $this->serviceLocator->nodeContextResolver()->resolveNode(new ConcreteFrame(), $this->parameter->default)->type(); - - return DefaultValue::fromValue(TypeUtil::valueOrNull($value)); - } - - public function byReference(): bool - { - return (bool) $this->parameter->byRefToken; - } - - public function functionLike(): ReflectionFunctionLike - { - return $this->functionLike; - } - - public function isPromoted(): bool - { - return $this->parameter->visibilityToken !== null; - } - - public function isVariadic(): bool - { - return $this->parameter->dotDotDotToken !== null; - } - - public function index(): int - { - return $this->index; - } - - public function docblock(): DocBlock - { - return $this->serviceLocator()->docblockFactory()->create( - $this->parameter->getLeadingCommentAndWhitespaceText(), - $this->scope() - ); - } - - protected function node(): Node - { - return $this->parameter; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionPromotedProperty.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionPromotedProperty.php deleted file mode 100644 index 1ed53e5d1a..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionPromotedProperty.php +++ /dev/null @@ -1,159 +0,0 @@ -typeResolver = new PropertyTypeResolver($this); - $this->memberTypeResolver = new DeclaredMemberTypeResolver($this->serviceLocator->reflector()); - } - - public function declaringClass(): ReflectionClassLike - { - /** @var NamespacedNameInterface $classDeclaration */ - $classDeclaration = $this->parameter->getFirstAncestor(ClassDeclaration::class, TraitDeclaration::class); - $class = $classDeclaration->getNamespacedName(); - - /** @phpstan-ignore-next-line */ - if (null === $class) { - throw new InvalidArgumentException(sprintf( - 'Could not locate class-like ancestor node for method "%s"', - $this->name() - )); - } - - return $this->serviceLocator->reflector()->reflectClassLike(ClassName::fromString($class)); - } - - public function name(): string - { - if ($this->name) { - return $this->name; - } - - $this->name = (string) $this->parameter->getName(); - return $this->name; - } - - public function nameRange(): ByteOffsetRange - { - return ByteOffsetRange::fromInts( - $this->parameter->variableName->getStartPosition() + 1, // return the range after the `$` - $this->parameter->variableName->getEndPosition(), - ); - } - - public function inferredType(): Type - { - $type = $this->typeResolver->resolve(); - - if ($type->isDefined()) { - return $type; - } - - return $type; - } - - public function type(): Type - { - if (!$this->parameter->typeDeclarationList) { - return TypeFactory::undefined(); - } - - return $this->memberTypeResolver->resolveTypes( - $this->parameter, - $this->parameter->typeDeclarationList, - $this->class()->name(), - $this->parameter->questionToken ? true : false - ); - } - - public function class(): ReflectionClassLike - { - return $this->class; - } - - public function isStatic(): bool - { - return false; - } - - public function isVirtual(): bool - { - return false; - } - - public function memberType(): string - { - return ReflectionMember::TYPE_PROPERTY; - } - - public function isPromoted(): bool - { - return true; - } - - public function visibility(): Visibility - { - $node = $this->parameter; - - if (!$node->visibilityToken) { - return Visibility::public(); - } - - if ($node->visibilityToken->kind === TokenKind::PrivateKeyword) { - return Visibility::private(); - } - - if ($node->visibilityToken->kind === TokenKind::ProtectedKeyword) { - return Visibility::protected(); - } - - return Visibility::public(); - } - - public function withClass(ReflectionClassLike $class): ReflectionMember - { - return new self($this->serviceLocator, $class, $this->parameter); - } - - protected function node(): Node - { - return $this->parameter; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionProperty.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionProperty.php deleted file mode 100644 index 7fc7cd3111..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionProperty.php +++ /dev/null @@ -1,138 +0,0 @@ -typeResolver = new PropertyTypeResolver($this); - $this->memberTypeResolver = new DeclaredMemberTypeResolver($this->serviceLocator->reflector()); - } - - public function declaringClass(): ReflectionClassLike - { - /** @var NamespacedNameInterface|null $classDeclaration */ - $classDeclaration = $this->propertyDeclaration->getFirstAncestor(ClassDeclaration::class, TraitDeclaration::class); - $class = $classDeclaration?->getNamespacedName(); - - if (null === $class) { - throw new InvalidArgumentException(sprintf( - 'Could not locate class-like ancestor node for method "%s"', - $this->name() - )); - } - - return $this->serviceLocator->reflector()->reflectClassLike(ClassName::fromString($class)); - } - - public function name(): string - { - if ($this->name) { - return $this->name; - } - $this->name = (string) $this->variable->getName(); - return $this->name; - } - - public function nameRange(): ByteOffsetRange - { - return ByteOffsetRange::fromInts( - $this->variable->getStartPosition() + 1, // do not return the $ - $this->variable->getEndPosition(), - ); - } - - public function inferredType(): Type - { - $type = $this->typeResolver->resolve(); - - if (($type->isDefined())) { - return $type; - } - - return $this->memberTypeResolver->resolveTypes( - $this->propertyDeclaration, - $this->propertyDeclaration->typeDeclarationList, - $this->class()->name(), - $this->propertyDeclaration->questionToken ? true : false - ); - } - - public function type(): Type - { - return $this->memberTypeResolver->resolveTypes( - $this->propertyDeclaration, - $this->propertyDeclaration->typeDeclarationList, - $this->class()->name(), - $this->propertyDeclaration->questionToken ? true : false - ); - } - - public function class(): ReflectionClassLike - { - return $this->class; - } - - public function isStatic(): bool - { - return $this->propertyDeclaration->isStatic(); - } - - public function isVirtual(): bool - { - return false; - } - - public function memberType(): string - { - return ReflectionMember::TYPE_PROPERTY; - } - - public function isPromoted(): bool - { - return false; - } - - public function withClass(ReflectionClassLike $class): ReflectionMember - { - return new self($this->serviceLocator, $class, $this->propertyDeclaration, $this->variable); - } - - protected function node(): Node - { - return $this->propertyDeclaration; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionPropertyAccess.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionPropertyAccess.php deleted file mode 100644 index 6d7ddac24a..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionPropertyAccess.php +++ /dev/null @@ -1,41 +0,0 @@ -node->getStartPosition(), - $this->node->getEndPosition() - ); - } - - public function name(): string - { - return NodeUtil::nameFromTokenOrNode($this->node, $this->node->memberName); - } - - public function nameRange(): ByteOffsetRange - { - $memberName = $this->node->memberName; - return ByteOffsetRange::fromInts( - $memberName->getStartPosition(), - $memberName->getEndPosition() - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionScope.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionScope.php deleted file mode 100644 index ab14d4780d..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionScope.php +++ /dev/null @@ -1,75 +0,0 @@ - - */ - public function nameImports(): NameImports - { - [$nameImports] = $this->node->getImportTablesForCurrentScope(); - return NameImports::fromNames(array_map(function (ResolvedName $name) { - return Name::fromParts($name->getNameParts()); - }, $nameImports)); - } - - public function namespace(): Name - { - $namespaceDefinition = $this->node->getNamespaceDefinition(); - - if (null === $namespaceDefinition) { - return Name::fromString(''); - } - - if (!$namespaceDefinition->name instanceof QualifiedName) { - return Name::fromString(''); - } - - return Name::fromString($namespaceDefinition->name->getText()); - } - - public function resolveFullyQualifiedName($type, ?ReflectionClassLike $class = null): Type - { - $resolver = new NodeToTypeConverter($this->reflector, new ArrayLogger()); - return $resolver->resolve($this->node, $type, $class ? $class->name() : null); - } - - public function resolveLocalName(Name $name): Name - { - return $this->nameImports()->resolveLocalName($name); - } - - public function resolveLocalType(Type $type): Type - { - $union = UnionType::toUnion($type); - foreach ($union->types as $type) { - if ($type instanceof ClassType) { - $type->name = ClassName::fromString($this->nameImports()->resolveLocalName($type->name())->__toString()); - } - } - return $union->reduce(); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionStaticMemberAccess.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionStaticMemberAccess.php deleted file mode 100644 index 26c676a040..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionStaticMemberAccess.php +++ /dev/null @@ -1,79 +0,0 @@ -node->getStartPosition(), - $this->node->getEndPosition() - ); - } - - public function class(): ReflectionClassLike - { - $info = $this->services->nodeContextResolver()->resolveNode($this->frame, $this->node); - $containerType = $info->containerType(); - - if (!$containerType instanceof ReflectedClassType) { - throw new CouldNotResolveNode(sprintf( - 'Class for member "%s" could not be determined', - $this->name() - )); - } - - $reflection = $containerType->reflectionOrNull(); - - if (null === $reflection) { - throw new CouldNotResolveNode(sprintf( - 'Class for member "%s" could not be determined', - $this->name() - )); - } - - return $reflection; - } - - public function name(): string - { - return ltrim(NodeUtil::nameFromTokenOrNode($this->node, $this->node->memberName), '$'); - } - - public function scope(): ReflectionScope - { - return new ReflectionScope($this->services->reflector(), $this->node); - } - - public function nameRange(): ByteOffsetRange - { - $memberName = $this->node->memberName; - return ByteOffsetRange::fromInts( - $memberName->getStartPosition() + 1, - $memberName->getEndPosition() - ); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionStaticMethodCall.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionStaticMethodCall.php deleted file mode 100644 index 110a834de9..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionStaticMethodCall.php +++ /dev/null @@ -1,23 +0,0 @@ - $visited - */ - public function __construct( - private ServiceLocator $serviceLocator, - private TextDocument $sourceCode, - private TraitDeclaration $node, - private array $visited = [] - ) { - } - - public function methods(?ReflectionClassLike $contextClass = null): CoreReflectionMethodCollection - { - return $this->members()->methods(); - } - - /** - * @return ReflectionMemberCollection - */ - public function members(): ReflectionMemberCollection - { - if ($this->members) { - return $this->members; - } - $members = ClassLikeReflectionMemberCollection::empty(); - foreach ((new ClassHierarchyResolver())->resolve($this) as $reflectionClassLike) { - /** @phpstan-ignore-next-line Constants is compatible with this */ - $members = $members->merge($reflectionClassLike->ownMembers()); - } - - $this->members = $members->map(fn (ReflectionMember $member) => $member->withClass($this)); - return $this->members; - } - - public function constants(): ReflectionConstantCollection - { - return $this->members()->constants(); - } - - public function ownMembers(): ReflectionMemberCollection - { - if ($this->ownMembers) { - return $this->ownMembers; - } - $this->ownMembers = ClassLikeReflectionMemberCollection::fromTraitMemberDeclarations( - $this->serviceLocator, - $this->node, - $this - ); - return $this->ownMembers; - } - - public function properties(): CoreReflectionPropertyCollection - { - return $this->members()->properties(); - } - - public function name(): ClassName - { - return ClassName::fromString((string) $this->node()->getNamespacedName()); - } - - public function sourceCode(): TextDocument - { - return $this->sourceCode; - } - - public function isInstanceOf(ClassName $className): bool - { - if ($className == $this->name()) { - return true; - } - - return false; - } - - public function docblock(): DocBlock - { - return $this->serviceLocator->docblockFactory()->create( - $this->node()->getLeadingCommentAndWhitespaceText(), - $this->scope() - ); - } - - public function traits(): PhpactorReflectionTraitCollection - { - return PhpactorReflectionTraitCollection::fromTraitDeclaration($this->serviceLocator, $this->node, $this->visited); - } - - public function classLikeType(): string - { - return 'trait'; - } - /** - * @return TraitDeclaration - */ - protected function node(): Node - { - return $this->node; - } - - protected function serviceLocator(): ServiceLocator - { - return $this->serviceLocator; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitAlias.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitAlias.php deleted file mode 100644 index d6213e4444..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitAlias.php +++ /dev/null @@ -1,30 +0,0 @@ -originalName; - } - - public function visiblity(?Visibility $default = null): Visibility - { - return $this->visiblity ?: $default ?: Visibility::public(); - } - - public function newName(): string - { - return $this->newName; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php deleted file mode 100644 index dbef2c24c0..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php +++ /dev/null @@ -1,32 +0,0 @@ -traitName; - } - - public function traitAliases(): array - { - return $this->traitAliases; - } - - public function getAlias($name): TraitAlias - { - return $this->traitAliases[$name]; - } - - public function hasAliasFor($name): bool - { - return array_key_exists($name, $this->traitAliases); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImports.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImports.php deleted file mode 100644 index c52e3db84d..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImports.php +++ /dev/null @@ -1,162 +0,0 @@ - - */ -final class TraitImports implements Countable, IteratorAggregate -{ - /** - * @var array - */ - private array $imports = []; - - /** - * @param Node[] $declarations - */ - private function __construct(array $declarations) - { - foreach ($declarations as $memberDeclaration) { - if (false === $memberDeclaration instanceof TraitUseClause) { - continue; - } - - if ($memberDeclaration->traitNameList == null) { - continue; - } - - $traitNames = array_filter(array_map(function ($name) { - if (!$name instanceof QualifiedName) { - return null; - } - - return (string) TolerantQualifiedNameResolver::getResolvedName($name); - }, iterator_to_array($memberDeclaration->traitNameList->getElements()))); - - if ($traitNames === []) { - continue; - } - - if (null === $memberDeclaration->traitSelectAndAliasClauses) { - foreach ($traitNames as $traitName) { - $this->imports[$traitName] = new TraitImport($traitName); - } - continue; - } - - foreach ($traitNames as $traitName) { - $aliases = []; - - foreach ($memberDeclaration->traitSelectAndAliasClauses as $selectAndAliasClauses) { - foreach ($selectAndAliasClauses as $clause) { - if (false === $clause instanceof TraitSelectOrAliasClause) { - continue; - } - - // Only support "as" keyword, do not support "insteadof" - // (the last one will win in the reflection class logic - // currently). - if ($clause->asOrInsteadOfKeyword->kind !== TokenKind::AsKeyword) { - continue; - } - - if (!$clause->name instanceof QualifiedName) { - continue; - } - - $targetName = QualifiedNameListUtil::firstQualifiedName($clause->targetNameList); - if (null === $targetName) { - continue; - } - - - $memberName = (string) $clause->name; - $targetName = (string) $targetName; - - $aliases[$memberName] = new TraitAlias( - $memberName, - $this->visiblity($clause), - $targetName - ); - } - } - - $this->imports[$traitName] = new TraitImport($traitName, $aliases); - } - } - } - - public static function forClassDeclaration(ClassDeclaration $classDeclaration): self - { - return new self($classDeclaration->classMembers->classMemberDeclarations); - } - - public static function forTraitDeclaration(TraitDeclaration $traitDeclaration): self - { - return new self($traitDeclaration->traitMembers->traitMemberDeclarations); - } - - public function has(string $name): bool - { - return isset($this->imports[$name]); - } - - public function get(string $name): TraitImport - { - if (!array_key_exists($name, $this->imports)) { - throw new RuntimeException(sprintf( - 'Trait import "%s" does not exist', - $name - )); - } - - return $this->imports[$name]; - } - - public function count(): int - { - return count($this->imports); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->imports); - } - - private function visiblity(TraitSelectOrAliasClause $clause) - { - foreach ($clause->modifiers as $modifier) { - if ($modifier->kind === TokenKind::PrivateKeyword) { - return Visibility::private(); - } - - if ($modifier->kind === TokenKind::ProtectedKeyword) { - return Visibility::protected(); - } - - if ($modifier->kind === TokenKind::PublicKeyword) { - return Visibility::public(); - } - } - - - return null; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolver.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolver.php deleted file mode 100644 index cb76e99d3f..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolver.php +++ /dev/null @@ -1,73 +0,0 @@ -reflector, $tolerantNode, $declaredTypes, $className); - - if (!$nullable) { - return $type; - } - - return TypeFactory::nullable($type); - } - - /** - * @param null|Node|Token $tolerantType - */ - public function resolve(Node $tolerantNode, $tolerantType = null, ?ClassName $className = null, bool $nullable = false): Type - { - $type = $this->doResolve($tolerantType, $tolerantNode, $className); - - if ($nullable) { - return TypeFactory::nullable($type); - } - return $type; - } - - /** - * @param null|Node|Token $tolerantType - */ - private function doResolve($tolerantType, ?Node $tolerantNode, ?ClassName $className = null): Type - { - if (null === $tolerantType) { - return TypeFactory::undefined(); - } - - $type = NodeUtil::typeFromQualfiedNameLike($this->reflector, $tolerantNode, $tolerantType, $className); - - $type = $type->map(function (Type $type) use ($className) { - if ($className && $type instanceof SelfType) { - return new SelfType(TypeFactory::reflectedClass($this->reflector, $className)); - - } - return $type; - }); - - return $type; - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflector/TolerantFactory.php b/lib/WorseReflection/Bridge/TolerantParser/Reflector/TolerantFactory.php deleted file mode 100644 index 2ffb12a445..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflector/TolerantFactory.php +++ /dev/null @@ -1,21 +0,0 @@ -parser); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflector/TolerantSourceCodeReflector.php b/lib/WorseReflection/Bridge/TolerantParser/Reflector/TolerantSourceCodeReflector.php deleted file mode 100644 index c3f8f99ac5..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/Reflector/TolerantSourceCodeReflector.php +++ /dev/null @@ -1,166 +0,0 @@ - $visited - */ - public function reflectClassesIn( - TextDocument $sourceCode, - array $visited = [] - ): TolerantReflectionClassCollection { - $node = $this->parseSourceCode($sourceCode); - return TolerantReflectionClassCollection::fromNode($this->serviceLocator, $sourceCode, $node, $visited); - } - - public function reflectOffset( - TextDocument $sourceCode, - ByteOffset|int $offset - ): ReflectionOffset { - $offset = ByteOffset::fromUnknown($offset); - - $rootNode = $this->parseSourceCode($sourceCode); - $node = $rootNode->getDescendantNodeAtPosition($offset->toInt()); - - $resolver = $this->serviceLocator->nodeContextResolver(); - $start = microtime(true); - $frame = $this->serviceLocator->frameBuilder($resolver)->build($node); - $context = $resolver->resolveNode($frame, $node); - - $this->serviceLocator->logger()->info(sprintf( - 'REFL %s node %s at offset %d (id: %s) resolved with %d cache misses', - number_format(microtime(true) - $start, 4), - get_debug_type($node), - $offset->toInt(), - spl_object_id($node), - $resolver->cacheMisses, - )); - - return TolerantReflectionOffset::fromFrameAndSymbolContext($frame, $context); - } - - /** - * @return Promise> - */ - public function diagnostics(TextDocument $sourceCode): Promise - { - return $this->serviceLocator->cacheForDocument()->getOrSet($sourceCode->uriOrThrow(), 'diagnostics', function () use ($sourceCode) { - return call(function () use ($sourceCode) { - $rootNode = $this->parseSourceCode($sourceCode); - $walker = $this->serviceLocator->newDiagnosticsWalker(); - foreach ($this->serviceLocator->frameBuilder()->withWalker($walker)->buildGenerator($rootNode) as $tick) { - yield delay(0); - } - return $walker->diagnostics(); - }); - }); - } - - public function walk(TextDocument $sourceCode, Walker $walker): Generator - { - $rootNode = $this->parseSourceCode($sourceCode); - return $this->serviceLocator->frameBuilder()->withWalker($walker)->buildGenerator($rootNode); - } - - public function reflectMethodCall( - TextDocument $sourceCode, - ByteOffset|int $offset - ): ReflectionMethodCall { - // see https://github.com/phpactor/phpactor/issues/1445 - $this->serviceLocator->cache()->purge(); - - try { - $reflection = $this->reflectNode($sourceCode, $offset); - } catch (CouldNotResolveNode $notFound) { - throw new MethodCallNotFound($notFound->getMessage(), 0, $notFound); - } - - if (false === $reflection instanceof ReflectionMethodCall) { - throw new MethodCallNotFound(sprintf( - 'Expected method call, got "%s"', - get_class($reflection) - )); - } - - return $reflection; - } - - public function reflectFunctionsIn(TextDocument $sourceCode): TolerantReflectionFunctionCollection - { - $node = $this->parseSourceCode($sourceCode); - return TolerantReflectionFunctionCollection::fromNode($this->serviceLocator, $sourceCode, $node); - } - - public function reflectConstantsIn(TextDocument $sourceCode): ReflectionDeclaredConstantCollection - { - $node = $this->parseSourceCode($sourceCode); - return ReflectionDeclaredConstantCollection::fromNode($this->serviceLocator, $sourceCode, $node); - } - - public function navigate(TextDocument $sourceCode): ReflectionNavigation - { - return new ReflectionNavigation($this->serviceLocator, $this->parseSourceCode($sourceCode)); - } - - public function reflectNodeContext(Node $node): NodeContext - { - $frame = $this->serviceLocator->frameBuilder()->build($node); - return $this->serviceLocator->nodeContextResolver()->resolveNode($frame, $node); - } - - - public function reflectNode( - TextDocument $sourceCode, - ByteOffset|int $offset - ): ReflectionNode { - $offset = ByteOffset::fromUnknown($offset); - - $rootNode = $this->parseSourceCode($sourceCode); - $node = $rootNode->getDescendantNodeAtPosition($offset->toInt()); - - $frame = $this->serviceLocator->frameBuilder()->build($node); - $nodeReflector = new NodeReflector($this->serviceLocator); - - return $nodeReflector->reflectNode($frame, $node); - } - - private function parseSourceCode(TextDocument $sourceCode): SourceFileNode - { - return $this->parser->get($sourceCode); - } -} diff --git a/lib/WorseReflection/Bridge/TolerantParser/TextDocument/NodeToTextDocumentConverter.php b/lib/WorseReflection/Bridge/TolerantParser/TextDocument/NodeToTextDocumentConverter.php deleted file mode 100644 index b14f82c4d4..0000000000 --- a/lib/WorseReflection/Bridge/TolerantParser/TextDocument/NodeToTextDocumentConverter.php +++ /dev/null @@ -1,22 +0,0 @@ -getFileContents()); - $uri = $node->getUri(); - - if ($uri) { - $document->uri($uri); - } - - return $document->build(); - } -} diff --git a/lib/WorseReflection/Core/AstProvider.php b/lib/WorseReflection/Core/AstProvider.php deleted file mode 100644 index aa733c965c..0000000000 --- a/lib/WorseReflection/Core/AstProvider.php +++ /dev/null @@ -1,11 +0,0 @@ -cacheForDocument = $cacheForDocument ?? CacheForDocument::none(); - } - - public function get(TextDocument $document): SourceFileNode - { - if ($document->uri() === null) { - return $this->cache->getOrSet( - 'astanon:' . md5($document), - function () use ($document) { - return $this->astProvider->get($document); - } - ); - } - - return $this->cacheForDocument->getOrSet( - $document->uri(), - 'ast', - function () use ($document) { - return $this->astProvider->get($document); - } - ); - } -} diff --git a/lib/WorseReflection/Core/Cache.php b/lib/WorseReflection/Core/Cache.php deleted file mode 100644 index 37790a6758..0000000000 --- a/lib/WorseReflection/Core/Cache.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ - private array $cache = []; - - public function getOrSet(string $key, Closure $closure) - { - if (isset($this->cache[$key])) { - return $this->cache[$key]->value(); - } - $this->cache[$key] = new CacheEntry($closure()); - return $this->cache[$key]->value(); - } - - public function purge(): void - { - $this->cache = []; - } - - public function get(string $key): ?CacheEntry - { - return $this->cache[$key] ?? null; - } - - public function set(string $key, mixed $value): void - { - $this->cache[$key] = new CacheEntry($value); - } - - public function remove(string $key): void - { - unset($this->cache[$key]); - } -} diff --git a/lib/WorseReflection/Core/Cache/TtlCache.php b/lib/WorseReflection/Core/Cache/TtlCache.php deleted file mode 100644 index e591b5776b..0000000000 --- a/lib/WorseReflection/Core/Cache/TtlCache.php +++ /dev/null @@ -1,97 +0,0 @@ - - */ - private array $cache = []; - - /** - * @var array - */ - private array $expires = []; - - private ?float $epoch = null; - - /** - * @var float $lifetime Lifetime in seconds - */ - public function __construct(private float $lifetime = 5.0) - { - } - - public function getOrSet(string $key, Closure $setter) - { - $this->purgeIfNeeded(); - - $entry = $this->get($key); - - if (null !== $entry) { - return $entry->value(); - } - - $value = $setter(); - $this->set($key, $value); - - return $this->cache[$key]->value(); - } - - public function purge(): void - { - $this->cache = []; - $this->expires = []; - } - - public function get(string $key): ?CacheEntry - { - $this->purgeIfNeeded(); - return $this->cache[$key] ?? null; - } - - public function set(string $key, mixed $value): void - { - $this->cache[$key] = new CacheEntry($value); - $this->expires[$key] = microtime(true) + $this->lifetime; - } - - public function remove(string $key): void - { - unset($this->cache[$key]); - } - - private function purgeIfNeeded(?int $now = null): void - { - $now = $now ?? microtime(true); - - if (null === $this->epoch) { - $this->epoch = $now; - return; - } - - $elapsed = $now - $this->epoch; - - if ($elapsed >= $this->lifetime) { - $this->purgeExpired($now); - $this->epoch = $now; - } - } - - private function purgeExpired(float $now): void - { - foreach ($this->expires as $key => $expires) { - if ($expires > $now) { - continue; - } - - unset($this->expires[$key]); - unset($this->cache[$key]); - } - } -} diff --git a/lib/WorseReflection/Core/CacheEntry.php b/lib/WorseReflection/Core/CacheEntry.php deleted file mode 100644 index f44fd8e873..0000000000 --- a/lib/WorseReflection/Core/CacheEntry.php +++ /dev/null @@ -1,41 +0,0 @@ -value; - } - - /** - * This method is not safe: it does validate or cast - * the value as/to a scalar. - * - * @return scalar - */ - public function scalar(): mixed - { - /** @phpstan-ignore-next-line */ - return $this->value; - } - - /** - * This method is not safe: it does validate or cast - * the value as/to an object. - * - * @template TObject of object - * @param class-string $type - * @return TObject - */ - public function expect(string $type): object - { - /** @phpstan-ignore-next-line */ - return $this->value; - } -} diff --git a/lib/WorseReflection/Core/CacheForDocument.php b/lib/WorseReflection/Core/CacheForDocument.php deleted file mode 100644 index ddf62c2d7e..0000000000 --- a/lib/WorseReflection/Core/CacheForDocument.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ - private array $caches = []; - - /** - * @param Closure(): Cache $cacheFactory - */ - public function __construct(private Closure $cacheFactory) - { - } - - public static function none(): self - { - return new self(fn () => new NullCache()); - } - - /** - * @template T - * @param Closure(): T $setter - * @return T - */ - public function getOrSet(TextDocumentUri $uri, string $key, Closure $setter) - { - return $this->cacheForDocument($uri)->getOrSet($key, $setter); - } - - public function cacheForDocument(TextDocumentUri $uri): Cache - { - if (!isset($this->caches[$uri->__toString()])) { - $this->caches[$uri->__toString()] = ($this->cacheFactory)(); - } - - return $this->caches[$uri->__toString()]; - } - - public function purge(TextDocumentUri $uri): void - { - unset($this->caches[$uri->__toString()]); - } -} diff --git a/lib/WorseReflection/Core/ClassHierarchyResolver.php b/lib/WorseReflection/Core/ClassHierarchyResolver.php deleted file mode 100644 index 620d02d2ca..0000000000 --- a/lib/WorseReflection/Core/ClassHierarchyResolver.php +++ /dev/null @@ -1,128 +0,0 @@ - $mode - */ - public function __construct( - private int $mode = self::INCLUDE_TRAIT | self::INCLUDE_INTERFACE | self::INCLUDE_PARENT | self::INCLUDE_MIXIN - ) { - } - - /** - * Returns an ordered list of all classes in the heierarchy with the base - * class being first and the provided class being last. - * - * @param array $resolved - * @return ReflectionClassLike[] - */ - public function resolve(ReflectionClassLike $classLike, array $resolved = []): array - { - return array_reverse($this->doResolve($classLike, $resolved)); - } - - /** - * @param array $resolved - * @return ReflectionClassLike[] - */ - public function doResolve(ReflectionClassLike $classLike, array $resolved = []): array - { - if (isset($resolved[$classLike->name()->__toString()])) { - return $resolved; - } - $resolved[$classLike->name()->__toString()] = $classLike; - - if ($classLike instanceof ReflectionClass) { - return $this->resolveReflectionClass($classLike, $resolved); - } - - if ($classLike instanceof ReflectionInterface) { - return $this->resolveReflectionInterface($classLike, $resolved); - } - - if ($classLike instanceof ReflectionTrait) { - return $this->resolveReflectionTrait($classLike, $resolved); - } - - return $resolved; - } - - /** - * @param array $resolved - * @return ReflectionClassLike[] - */ - private function resolveReflectionInterface(ReflectionInterface $classLike, array $resolved): array - { - foreach ($classLike->parents() as $interface) { - $resolved = $this->doResolve($interface, $resolved); - } - return $resolved; - } - - /** - * @param array $resolved - * @return ReflectionClassLike[] - */ - private function resolveReflectionClass(ReflectionClass $classLike, array $resolved): array - { - if ($this->mode & self::INCLUDE_PARENT) { - $parent = $classLike->parent(); - if ($parent) { - $resolved = $this->doResolve($parent, $resolved); - } - } - - if ($this->mode & self::INCLUDE_INTERFACE) { - foreach ($classLike->interfaces() as $interface) { - $resolved = $this->doResolve($interface, $resolved); - } - } - - if ($this->mode & self::INCLUDE_TRAIT) { - foreach ($classLike->traits() as $interface) { - $resolved = $this->doResolve($interface, $resolved); - } - } - - if ($this->mode & self::INCLUDE_MIXIN) { - // consider making this an extension point and "mixins" an extension - foreach ($classLike->docblock()->mixins() as $mixin) { - if ($mixin instanceof ReflectedClassType) { - $reflection = $mixin->reflectionOrNull(); - if ($reflection) { - $resolved = $this->doResolve($reflection, $resolved); - } - } - } - } - - return $resolved; - } - - /** - * @param array $resolved - * @return ReflectionClassLike[] - */ - private function resolveReflectionTrait(ReflectionTrait $classLike, array $resolved): array - { - foreach ($classLike->traits() as $trait) { - $resolved = $this->doResolve($trait, $resolved); - } - - return $resolved; - } -} diff --git a/lib/WorseReflection/Core/ClassName.php b/lib/WorseReflection/Core/ClassName.php deleted file mode 100644 index ed22ff78ea..0000000000 --- a/lib/WorseReflection/Core/ClassName.php +++ /dev/null @@ -1,11 +0,0 @@ -functionStubRegistry = $this->createStubRegistry(); - } - - /** - * @return array - */ - public function createResolvers(): array - { - return [ - QualifiedName::class => new QualifiedNameResolver($this->reflector, $this->functionStubRegistry, $this->nodeTypeConverter), - QualifiedNameList::class => new QualifiedNameListResolver(), - ConstElement::class => new ConstElementResolver(), - EnumCaseDeclaration::class => new EnumCaseDeclarationResolver(), - Parameter::class => new ParameterResolver(), - UseVariableName::class => new UseVariableNameResolver(), - GlobalDeclaration::class => new GlobalDeclarationResolver(), - StaticVariableDeclaration::class => new StaticDeclarationResolver(), - Variable::class => new VariableResolver(), - MemberAccessExpression::class => new MemberAccessExpressionResolver($this->nodeContextFromMemberAccess), - ScopedPropertyAccessExpression::class => new ScopedPropertyAccessResolver($this->nodeContextFromMemberAccess), - CallExpression::class => new CallExpressionResolver($this->genericResolver), - ParenthesizedExpression::class => new ParenthesizedExpressionResolver(), - BinaryExpression::class => new BinaryExpressionResolver(), - UnaryOpExpression::class => new UnaryOpExpressionResolver(), - ClassDeclaration::class => new ClassLikeResolver(), - InterfaceDeclaration::class => new ClassLikeResolver(), - TraitDeclaration::class => new ClassLikeResolver(), - EnumDeclaration::class => new ClassLikeResolver(), - FunctionDeclaration::class => new FunctionDeclarationResolver(), - ObjectCreationExpression::class => new ObjectCreationExpressionResolver($this->genericResolver), - SubscriptExpression::class => new SubscriptExpressionResolver(), - StringLiteral::class => new StringLiteralResolver(), - NumericLiteral::class => new NumericLiteralResolver(), - ReservedWord::class => new ReservedWordResolver(), - ArrayCreationExpression::class => new ArrayCreationExpressionResolver(), - ArgumentExpression::class => new ArgumentExpressionResolver(), - TernaryExpression::class => new TernaryExpressionResolver(), - MethodDeclaration::class => new MethodDeclarationResolver(), - CloneExpression::class => new CloneExpressionResolver(), - AssignmentExpression::class => new AssignmentExpressionResolver(), - CastExpression::class => new CastExpressionResolver(), - ArrowFunctionCreationExpression::class => new ArrowFunctionCreationExpressionResolver(), - AnonymousFunctionCreationExpression::class => new AnonymousFunctionCreationExpressionResolver(), - CatchClause::class => new CatchClauseResolver(), - ForeachStatement::class => new ForeachStatementResolver(), - IfStatementNode::class => new IfStatementResolver(), - CompoundStatementNode::class => new CompoundStatementResolver(), - ExpressionStatement::class => new ExpressionStatementResolver(), - SourceFileNode::class => new SourceFileNodeResolver(), - ReturnStatement::class => new ReturnStatementResolver(), - YieldExpression::class => new YieldExpressionResolver(), - PostfixUpdateExpression::class => new PostfixUpdateExpressionResolver(), - ]; - } - - private function createStubRegistry(): FunctionStubRegistry - { - return new FunctionStubRegistry([ - 'array_sum' => new ArraySumStub(), - 'in_array' => new InArrayStub(), - 'iterator_to_array' => new IteratorToArrayStub(), - 'is_null' => new IsSomethingStub(TypeFactory::null()), - 'is_float' => new IsSomethingStub(TypeFactory::float()), - 'is_int' => new IsSomethingStub(TypeFactory::int()), - 'is_string' => new IsSomethingStub(TypeFactory::string()), - 'is_callable' => new IsSomethingStub(TypeFactory::callable()), - 'array_map' => new ArrayMapStub(), - 'reset' => new ResetStub(), - 'array_shift' => new ArrayShiftStub(), - 'array_pop' => new ArrayPopStub(), - 'array_reduce' => new ArrayReduceStub(), - 'array_merge' => new ArrayMergeStub(), - 'assert' => new AssertStub(), - ]); - } -} diff --git a/lib/WorseReflection/Core/DefaultValue.php b/lib/WorseReflection/Core/DefaultValue.php deleted file mode 100644 index 4d245a936f..0000000000 --- a/lib/WorseReflection/Core/DefaultValue.php +++ /dev/null @@ -1,35 +0,0 @@ -undefined = true; - - return $new; - } - - public function isDefined(): bool - { - return false === $this->undefined; - } - - public function value() - { - return $this->value; - } -} diff --git a/lib/WorseReflection/Core/Deprecation.php b/lib/WorseReflection/Core/Deprecation.php deleted file mode 100644 index bd7a352c58..0000000000 --- a/lib/WorseReflection/Core/Deprecation.php +++ /dev/null @@ -1,22 +0,0 @@ -isDefined; - } - - public function message(): string - { - return $this->message ?? ''; - } -} diff --git a/lib/WorseReflection/Core/Diagnostic.php b/lib/WorseReflection/Core/Diagnostic.php deleted file mode 100644 index df56c45749..0000000000 --- a/lib/WorseReflection/Core/Diagnostic.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ - public function tags(): array; -} diff --git a/lib/WorseReflection/Core/DiagnosticExample.php b/lib/WorseReflection/Core/DiagnosticExample.php deleted file mode 100644 index 84f9d49fba..0000000000 --- a/lib/WorseReflection/Core/DiagnosticExample.php +++ /dev/null @@ -1,21 +0,0 @@ -): void $assertion - */ - public function __construct( - public string $title, - public string $source, - public bool $valid, - public Closure $assertion, - public ?string $minPhpVersion = null - ) { - } - -} diff --git a/lib/WorseReflection/Core/DiagnosticProvider.php b/lib/WorseReflection/Core/DiagnosticProvider.php deleted file mode 100644 index 3d24b46d22..0000000000 --- a/lib/WorseReflection/Core/DiagnosticProvider.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable; - - /** - * @return iterable - */ - public function exit(NodeContextResolver $resolver, Frame $frame, Node $node): iterable; - - /** - * @return iterable - */ - public function examples(): iterable; -} diff --git a/lib/WorseReflection/Core/DiagnosticProvider/BareDiagnostic.php b/lib/WorseReflection/Core/DiagnosticProvider/BareDiagnostic.php deleted file mode 100644 index 594b44620f..0000000000 --- a/lib/WorseReflection/Core/DiagnosticProvider/BareDiagnostic.php +++ /dev/null @@ -1,43 +0,0 @@ -range; - } - - public function severity(): DiagnosticSeverity - { - return $this->severity; - } - - public function message(): string - { - return $this->message; - } - - public function tags(): array - { - return []; - } - - public function code(): string - { - return $this->code; - } -} diff --git a/lib/WorseReflection/Core/DiagnosticProvider/InMemoryDiagnosticProvider.php b/lib/WorseReflection/Core/DiagnosticProvider/InMemoryDiagnosticProvider.php deleted file mode 100644 index befd42d78f..0000000000 --- a/lib/WorseReflection/Core/DiagnosticProvider/InMemoryDiagnosticProvider.php +++ /dev/null @@ -1,39 +0,0 @@ -diagnostics as $diagnostic) { - yield $diagnostic; - } - } - - public function enter(NodeContextResolver $resolver, Frame $frame, Node $node): iterable - { - return []; - } -} diff --git a/lib/WorseReflection/Core/DiagnosticSeverity.php b/lib/WorseReflection/Core/DiagnosticSeverity.php deleted file mode 100644 index 3d2c4256ff..0000000000 --- a/lib/WorseReflection/Core/DiagnosticSeverity.php +++ /dev/null @@ -1,82 +0,0 @@ -level) { - case self::HINT: - return 'HINT'; - case self::ERROR: - return 'ERROR'; - case self::WARNING: - return 'WARN'; - case self::INFORMATION: - return 'INFO'; - } - } - - public function isError(): bool - { - return $this->level === self::ERROR; - } - - public function isWarning(): bool - { - return $this->level === self::WARNING; - } - - public function isHint(): bool - { - return $this->level === self::HINT; - } -} diff --git a/lib/WorseReflection/Core/DiagnosticTag.php b/lib/WorseReflection/Core/DiagnosticTag.php deleted file mode 100644 index e7f7d866ad..0000000000 --- a/lib/WorseReflection/Core/DiagnosticTag.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ -final class Diagnostics implements IteratorAggregate, Countable, Stringable -{ - /** - * @param T[] $diagnostics - */ - public function __construct(private array $diagnostics) - { - } - - public function __toString(): string - { - return implode("\n", array_map(function (Diagnostic $diagnostic) { - return sprintf('[%s] %s', $diagnostic->severity()->toString(), $diagnostic->message()); - }, $this->diagnostics)); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->diagnostics); - } - - public function count(): int - { - return count($this->diagnostics); - } - - /** - * @template TD of Diagnostic - * @param class-string $classFqn - * @return Diagnostics - */ - public function byClass(string $classFqn): self - { - return new self(array_filter($this->diagnostics, fn (Diagnostic $d) => $d instanceof $classFqn)); - } - - /** - * @template DF of Diagnostic - * @param class-string $classFqns - * @return Diagnostics - */ - public function byClasses(string ...$classFqns): self - { - /** @phpstan-ignore-next-line ??? */ - return new self(array_filter( - $this->diagnostics, - function (Diagnostic $d) use ($classFqns) { - foreach ($classFqns as $fqn) { - if ($d instanceof $fqn) { - return true; - } - } - - return false; - } - )); - } - - public function at(int $index): Diagnostic - { - if (!isset($this->diagnostics[$index])) { - throw new RuntimeException(sprintf( - 'Diagnostic at index "%s" does not exist', - $index - )); - } - - return $this->diagnostics[$index]; - } - - /** - * @return Diagnostics - */ - public function withinRange(ByteOffsetRange $byteOffsetRange): self - { - return new self(array_filter( - $this->diagnostics, - fn (Diagnostic $d) => - $d->range()->start()->toInt() >= $byteOffsetRange->start()->toInt() && - $d->range()->end()->toInt() <= $byteOffsetRange->end()->toInt() - )); - } - - /** - * @return Diagnostics - */ - public function containingRange(ByteOffsetRange $byteOffsetRange): self - { - return new self(array_filter( - $this->diagnostics, - fn (Diagnostic $d) => - $d->range()->start()->toInt() <= $byteOffsetRange->start()->toInt() && - $d->range()->end()->toInt() >= $byteOffsetRange->end()->toInt() - )); - } -} diff --git a/lib/WorseReflection/Core/DocBlock/DocBlock.php b/lib/WorseReflection/Core/DocBlock/DocBlock.php deleted file mode 100644 index 9eadfb5456..0000000000 --- a/lib/WorseReflection/Core/DocBlock/DocBlock.php +++ /dev/null @@ -1,63 +0,0 @@ -name; - } - - public function type(): Type - { - return $this->type; - } -} diff --git a/lib/WorseReflection/Core/DocBlock/DocBlockParams.php b/lib/WorseReflection/Core/DocBlock/DocBlockParams.php deleted file mode 100644 index caf4f2a876..0000000000 --- a/lib/WorseReflection/Core/DocBlock/DocBlockParams.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class DocBlockParams implements IteratorAggregate -{ - /** - * @var DocBlockParam[] - */ - private array $params = []; - - /** - * @param DocBlockParam[] $params - */ - public function __construct(array $params) - { - foreach ($params as $param) { - $this->add($param); - } - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->params); - } - - public function has(string $name): bool - { - return isset($this->params[$name]); - } - - private function add(DocBlockParam $param): void - { - $this->params[$param->name()] = $param; - } -} diff --git a/lib/WorseReflection/Core/DocBlock/DocBlockTypeAlias.php b/lib/WorseReflection/Core/DocBlock/DocBlockTypeAlias.php deleted file mode 100644 index 2be8bc20a8..0000000000 --- a/lib/WorseReflection/Core/DocBlock/DocBlockTypeAlias.php +++ /dev/null @@ -1,24 +0,0 @@ -alias; - } - - public function type(): Type - { - return $this->type; - } -} diff --git a/lib/WorseReflection/Core/DocBlock/DocBlockTypeAliases.php b/lib/WorseReflection/Core/DocBlock/DocBlockTypeAliases.php deleted file mode 100644 index 0cf9cb8a34..0000000000 --- a/lib/WorseReflection/Core/DocBlock/DocBlockTypeAliases.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -class DocBlockTypeAliases implements IteratorAggregate -{ - /** - * @var array - */ - private array $aliases = []; - - /** - * @param list $aliases - */ - public function __construct(array $aliases) - { - foreach ($aliases as $alias) { - $this->aliases[$alias->alias()] = $alias; - } - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->aliases); - } - - public function forType(Type $type): ?Type - { - if (isset($this->aliases[$type->toPhpString()])) { - return $this->aliases[$type->toPhpString()]->type(); - } - return null; - } -} diff --git a/lib/WorseReflection/Core/DocBlock/DocBlockTypeAssertion.php b/lib/WorseReflection/Core/DocBlock/DocBlockTypeAssertion.php deleted file mode 100644 index 94be78dede..0000000000 --- a/lib/WorseReflection/Core/DocBlock/DocBlockTypeAssertion.php +++ /dev/null @@ -1,15 +0,0 @@ -name; - } - - public function type(): Type - { - return $this->type; - } -} diff --git a/lib/WorseReflection/Core/DocBlock/DocBlockVars.php b/lib/WorseReflection/Core/DocBlock/DocBlockVars.php deleted file mode 100644 index 46a9fe2033..0000000000 --- a/lib/WorseReflection/Core/DocBlock/DocBlockVars.php +++ /dev/null @@ -1,55 +0,0 @@ - - */ -class DocBlockVars implements IteratorAggregate, Countable -{ - /** - * @var DocBlockVar[] - */ - private array $vars = []; - - /** - * @param DocBlockVar[] $vars - */ - public function __construct(array $vars) - { - foreach ($vars as $var) { - $this->add($var); - } - } - - public function type(): Type - { - foreach ($this->vars as $var) { - return $var->type(); - } - - return TypeFactory::undefined(); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->vars); - } - - public function count(): int - { - return count($this->vars); - } - - private function add(DocBlockVar $var): void - { - $this->vars[] = $var; - } -} diff --git a/lib/WorseReflection/Core/DocBlock/PlainDocblock.php b/lib/WorseReflection/Core/DocBlock/PlainDocblock.php deleted file mode 100644 index f0efa2dd0c..0000000000 --- a/lib/WorseReflection/Core/DocBlock/PlainDocblock.php +++ /dev/null @@ -1,185 +0,0 @@ -raw = $raw; - } - - public function methodType(string $methodName): Type - { - return TypeFactory::undefined(); - } - - public function inherits(): bool - { - return str_contains($this->raw, '@inheritDoc'); - } - - public function vars(): DocBlockVars - { - return new DocBlockVars([]); - } - - public function params(): DocBlockParams - { - return new DocBlockParams([]); - } - - public function parameterType(string $paramName): Type - { - return TypeFactory::undefined(); - } - - public function propertyType(string $methodName): Type - { - return TypeFactory::undefined(); - } - - public function formatted(): string - { - $mode = self::START; - $buffer = ''; - $text = ''; - foreach (str_split(trim($this->raw)) as $char) { - $buffer .= $char; - - switch ($mode) { - case self::START: - if ($buffer === '/*') { - $mode = self::EXTRA_ASTERIX; - $buffer = ''; - } - break; - case self::EXTRA_ASTERIX: - if ($buffer === '*') { - $buffer = ''; - } - $mode = self::TEXT; - break; - case self::TEXT: - if ($char === "\n") { - $text .= "\n"; - $mode = self::LEADING; - break; - } - if ($char === '*') { - $mode = self::WS_OR_TERMINATE; - break; - } - $text .= $char; - break; - case self::LEADING: - if ($char === '*') { - $mode = self::WS_OR_TERMINATE; - } - break; - case self::WHITESPACE: - if ($char !== ' ') { - $mode = self::TEXT; - $text .= $char; - } - break; - case self::WS_OR_TERMINATE: - if ($char === '/') { - return trim($text); - } - - if ($char !== ' ') { - $text = trim($text, ' ') . $char; - $mode = self::TEXT; - break; - } - break; - } - } - - return $text; - } - - public function returnType(): Type - { - return TypeFactory::undefined(); - } - - public function raw(): string - { - return $this->raw; - } - - public function isDefined(): bool - { - return $this->raw !== ''; - } - - public function properties(ReflectionClassLike $declaringClass): CoreReflectionPropertyCollection - { - return CoreReflectionPropertyCollection::empty(); - } - - public function methods(ReflectionClassLike $declaringClass): CoreReflectionMethodCollection - { - return CoreReflectionMethodCollection::empty(); - } - - public function deprecation(): Deprecation - { - return new Deprecation(false); - } - - public function templateMap(): TemplateMap - { - return new TemplateMap([]); - } - - public function extends(): array - { - return []; - } - - public function implements(): array - { - return []; - } - - public function mixins(): array - { - return []; - } - - public function withTypeResolver(TypeResolver $classLikeTypeResolver): DocBlock - { - return $this; - } - - public function typeAliases(): DocBlockTypeAliases - { - return new DocBlockTypeAliases([]); - } - - public function assertions(): array - { - return []; - } -} diff --git a/lib/WorseReflection/Core/Exception/ClassNotFound.php b/lib/WorseReflection/Core/Exception/ClassNotFound.php deleted file mode 100644 index 9b6b2ae5bf..0000000000 --- a/lib/WorseReflection/Core/Exception/ClassNotFound.php +++ /dev/null @@ -1,7 +0,0 @@ - - */ -abstract class Assignments implements Countable, IteratorAggregate -{ - private int $version = 1; - - /** - * @var array - */ - private array $variables = []; - - /** - * @var array> - */ - private array $variablesByName = []; - - /** - * @param array $variables - */ - final protected function __construct(array $variables) - { - $this->variables = $variables; - } - - - public function __toString(): string - { - return implode("\n", array_map(function (Variable $variable) { - return sprintf( - '%s:%s: %s%s', - $variable->name(), - $variable->offset(), - $variable->type()->__toString(), - $variable->wasDefinition() ? ' (definition)' : '', - ); - }, array_values($this->variables))); - } - - public function set(Variable $variable): void - { - $this->version++; - $this->variables[$variable->key()] = $variable; - $this->variablesByName[$variable->name()][$variable->key()] = $variable; - } - - public function add(Variable $variable, int $offset): void - { - $this->version++; - $original = $this->byName($variable->name())->lessThanOrEqualTo($offset)->lastOrNull(); - if ($original === null) { - $this->set($variable); - return; - } - $this->set($variable->withOffset( - $variable->offset() - )->withType($original->type()->addType($variable->type())->clean())); - } - - /** - * Return all variables matching the given name. - * - * When this method is used on the original frame it will return directly, - * if used after other filters it will filter over all variables which can - * be slow. - * - * IMPORTANT: Call this method BEFORE calling greater than / less than etc. - */ - public function byName(string $name): Assignments - { - $name = ltrim($name, '$'); - - // best case - if (isset($this->variablesByName[$name])) { - return new static($this->variablesByName[$name]); - } - - // worst case - return new static(array_filter($this->variables, function (Variable $v) use ($name) { - return $v->name() === $name; - })); - } - - public function lessThanOrEqualTo(int $offset): Assignments - { - return new static(array_filter($this->variables, function (Variable $v) use ($offset) { - return $v->offset() <= $offset; - })); - } - - public function lessThan(int $offset): Assignments - { - return new static(array_filter($this->variables, function (Variable $v) use ($offset) { - return $v->offset() < $offset; - })); - } - - public function greaterThan(int $offset): Assignments - { - return new static(array_filter($this->variables, function (Variable $v) use ($offset) { - return $v->offset() > $offset; - })); - } - - public function greaterThanOrEqualTo(int $offset): Assignments - { - return new static(array_filter($this->variables, function (Variable $v) use ($offset) { - return $v->offset() >= $offset; - })); - } - - public function first(): Variable - { - $first = reset($this->variables); - - if (!$first) { - throw new RuntimeException( - 'Variable collection is empty' - ); - } - - return $first; - } - - public function atIndex(int $index): Variable - { - $variables = array_values($this->variables); - if (!isset($variables[$index])) { - throw new RuntimeException(sprintf( - 'No variable at index "%s"', - $index - )); - } - - return $variables[$index]; - } - - public function last(): Variable - { - $last = end($this->variables); - - if (!$last) { - throw new RuntimeException( - 'Cannot get last, variable collection is empty' - ); - } - - return $last; - } - - public function count(): int - { - return count($this->variables); - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator(array_values($this->variables)); - } - - public function merge(Assignments $variables): void - { - foreach ($variables->variables as $key => $variable) { - $this->variables[$key] = $variable; - } - } - - public function replace(Variable $existing, Variable $replacement): void - { - foreach ($this->variables as $key => $variable) { - if ($variable !== $existing) { - continue; - } - $this->version++; - $this->variables[$key] = $replacement; - foreach ($this->variablesByName[$replacement->name()] ?? [] as $key => $byName) { - if ($byName !== $existing) { - continue; - } - $this->variablesByName[$replacement->name()][$key] = $replacement; - } - } - } - - public function equalTo(int $offset): Assignments - { - return new static(array_filter($this->variables, function (Variable $v) use ($offset) { - return $v->offset() === $offset; - })); - } - - public function not(int $offset): Assignments - { - return new static(array_filter($this->variables, function (Variable $v) use ($offset) { - return $v->offset() !== $offset; - })); - } - - public function assignmentsOnly(): Assignments - { - return new static(array_filter($this->variables, function (Variable $v) { - return $v->wasAssigned(); - })); - } - - public function definitionsOnly(): Assignments - { - return new static(array_filter($this->variables, function (Variable $v) { - return $v->wasDefinition(); - })); - } - - public function lastOrNull(): ?Variable - { - $last = end($this->variables); - - if (!$last) { - return null; - } - - return $last; - } - - public function version(): int - { - return $this->version; - } - - public function mostRecent(): self - { - $mostRecent = []; - foreach ($this->variables as $variable) { - $mostRecent[$variable->name()] = $variable; - } - - return new static($mostRecent); - } - - /** - * @return Variable[] - */ - public function toArray(): array - { - return $this->variables; - } -} diff --git a/lib/WorseReflection/Core/Inference/Context/CallContext.php b/lib/WorseReflection/Core/Inference/Context/CallContext.php deleted file mode 100644 index 210670c420..0000000000 --- a/lib/WorseReflection/Core/Inference/Context/CallContext.php +++ /dev/null @@ -1,13 +0,0 @@ -type()); - } - - public function range(): ByteOffsetRange - { - return $this->byteOffsetRange; - } - - public function classLike(): ReflectionClassLike - { - return $this->class; - } -} diff --git a/lib/WorseReflection/Core/Inference/Context/FunctionCallContext.php b/lib/WorseReflection/Core/Inference/Context/FunctionCallContext.php deleted file mode 100644 index 5b72848c8f..0000000000 --- a/lib/WorseReflection/Core/Inference/Context/FunctionCallContext.php +++ /dev/null @@ -1,60 +0,0 @@ -inferredType()->reduce() - ); - } - - public function range(): ByteOffsetRange - { - return $this->byteOffsetRange; - } - - public function function(): ReflectionFunction - { - return $this->function; - } - - public function arguments(): ?FunctionArguments - { - return $this->arguments; - } - - public static function create( - Name $name, - ByteOffsetRange $byteOffsetRange, - ReflectionFunction $function, - FunctionArguments $arguments, - ): self { - return new self( - Symbol::fromTypeNameAndPosition(Symbol::FUNCTION, $name, $byteOffsetRange), - $byteOffsetRange, - $function, - $arguments, - ); - } - - public function callable(): ReflectionMethod|ReflectionFunction - { - return $this->function; - } -} diff --git a/lib/WorseReflection/Core/Inference/Context/MemberAccessContext.php b/lib/WorseReflection/Core/Inference/Context/MemberAccessContext.php deleted file mode 100644 index 60014ff86e..0000000000 --- a/lib/WorseReflection/Core/Inference/Context/MemberAccessContext.php +++ /dev/null @@ -1,48 +0,0 @@ -member; - } - - public function memberNameRange(): ByteOffsetRange - { - return $this->memberNameRange; - } - - public function arguments(): ?FunctionArguments - { - return $this->arguments; - } -} diff --git a/lib/WorseReflection/Core/Inference/Context/MemberDeclarationContext.php b/lib/WorseReflection/Core/Inference/Context/MemberDeclarationContext.php deleted file mode 100644 index 3c0377de4f..0000000000 --- a/lib/WorseReflection/Core/Inference/Context/MemberDeclarationContext.php +++ /dev/null @@ -1,30 +0,0 @@ -containerType instanceof ClassType) { - throw new RuntimeException('Member declaration must have class as a container type'); - } - return $this->containerType; - } - - public function name(): string - { - return $this->symbol->name(); - } -} diff --git a/lib/WorseReflection/Core/Inference/Context/MethodCallContext.php b/lib/WorseReflection/Core/Inference/Context/MethodCallContext.php deleted file mode 100644 index 3436345ca6..0000000000 --- a/lib/WorseReflection/Core/Inference/Context/MethodCallContext.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ -class MethodCallContext extends MemberAccessContext implements CallContext -{ - public function callable(): ReflectionMethod|ReflectionFunction - { - return $this->member; - } -} diff --git a/lib/WorseReflection/Core/Inference/Frame.php b/lib/WorseReflection/Core/Inference/Frame.php deleted file mode 100644 index 522d0396e0..0000000000 --- a/lib/WorseReflection/Core/Inference/Frame.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ - public function locals(): Assignments; - - public function properties(): Assignments; - - public function problems(): Problems; - - public function parent(): ?Frame; - - public function root(): Frame; - - public function setReturnType(Type $type): Frame; - - public function applyTypeAssertions(TypeAssertions $typeAssertions, int $contextOffset, ?int $createAtOffset = null): void; - public function returnType(): Type; - public function varDocBuffer(): VarDocBuffer; -} diff --git a/lib/WorseReflection/Core/Inference/Frame/ConcreteFrame.php b/lib/WorseReflection/Core/Inference/Frame/ConcreteFrame.php deleted file mode 100644 index 0c7ab725fe..0000000000 --- a/lib/WorseReflection/Core/Inference/Frame/ConcreteFrame.php +++ /dev/null @@ -1,130 +0,0 @@ -properties = $properties ?: PropertyAssignments::create(); - $this->locals = $locals ?: LocalAssignments::create(); - $this->problems = $problems ?: Problems::create(); - $this->varDocBuffer = new VarDocBuffer(); - } - - public function __toString(): string - { - return implode("\n", array_map(function (Assignments $assignments, string $type) { - return $type ."\n:" . $assignments->__toString(); - }, [$this->properties, $this->locals], ['properties', 'locals'])); - } - - public function new(): Frame - { - $frame = new self(null, null, null, $this); - - return $frame; - } - - /** - * @return Assignments - */ - public function locals(): Assignments - { - return $this->locals; - } - - public function properties(): Assignments - { - return $this->properties; - } - - public function problems(): Problems - { - return $this->problems; - } - - public function parent(): ?Frame - { - return $this->parent; - } - - public function root(): Frame - { - if (null === $this->parent) { - return $this; - } - - return $this->parent->root(); - } - - public function setReturnType(Type $type): Frame - { - $this->returnType = $type; - return $this; - } - - public function applyTypeAssertions(TypeAssertions $typeAssertions, int $contextOffset, ?int $createAtOffset = null): void - { - foreach ([ - [ $typeAssertions->properties(), $this->properties() ], - [ $typeAssertions->variables(), $this->locals() ], - ] as [ $typeAssertions, $frameVariables ]) { - - foreach ($typeAssertions as $typeAssertion) { - $original = null; - foreach ($frameVariables->byName($typeAssertion->name())->lessThanOrEqualTo($contextOffset) as $variable) { - $original = $variable; - } - $originalType = $original ? $original->type() : new MissingType(); - - $variable = new Variable( - $typeAssertion->name(), - $createAtOffset ?: $typeAssertion->offset(), - $typeAssertion->apply($originalType), - $typeAssertion->classType(), - ); - - $type = $variable->type(); - - $frameVariables->set($variable); - } - } - } - - public function returnType(): Type - { - return $this->returnType ?: new VoidType(); - } - - public function varDocBuffer(): VarDocBuffer - { - return $this->varDocBuffer; - } -} diff --git a/lib/WorseReflection/Core/Inference/Frame/LazyFrame.php b/lib/WorseReflection/Core/Inference/Frame/LazyFrame.php deleted file mode 100644 index 8005bc8729..0000000000 --- a/lib/WorseReflection/Core/Inference/Frame/LazyFrame.php +++ /dev/null @@ -1,87 +0,0 @@ -frame()->__toString(); - } - - public function new(): Frame - { - return $this->frame()->new(); - } - - public function locals(): Assignments - { - return $this->frame()->locals(); - } - - public function properties(): Assignments - { - return $this->frame()->properties(); - } - - public function problems(): Problems - { - return $this->frame()->problems(); - } - - public function parent(): ?Frame - { - return $this->frame()->parent(); - } - - public function root(): Frame - { - return $this->frame()->root(); - } - - public function setReturnType(Type $type): Frame - { - return $this->frame()->setReturnType($type); - } - - public function applyTypeAssertions(TypeAssertions $typeAssertions, int $contextOffset, ?int $createAtOffset = null): void - { - $this->frame()->applyTypeAssertions($typeAssertions, $contextOffset, $createAtOffset); - } - - public function returnType(): Type - { - return $this->frame()->returnType(); - } - - public function varDocBuffer(): VarDocBuffer - { - return $this->frame()->varDocBuffer(); - } - - private function frame(): Frame - { - if (null !== $this->frame) { - return $this->frame; - } - $this->frame = $this->frameResolver->build($this->node); - return $this->frame; - } -} diff --git a/lib/WorseReflection/Core/Inference/FrameResolver.php b/lib/WorseReflection/Core/Inference/FrameResolver.php deleted file mode 100644 index c0db9d3ba2..0000000000 --- a/lib/WorseReflection/Core/Inference/FrameResolver.php +++ /dev/null @@ -1,238 +0,0 @@ - $nodeWalkers - */ - public function __construct( - private NodeContextResolver $nodeContextResolver, - private array $globalWalkers, - private array $nodeWalkers, - private CacheForDocument $cache, - ) { - } - - /** - * @param Walker[] $walkers - */ - public static function create( - NodeContextResolver $nodeContextResolver, - array $walkers, - CacheForDocument $cache, - ): self { - $globalWalkers = []; - $nodeWalkers = []; - foreach ($walkers as $walker) { - if (empty($walker->nodeFqns())) { - $globalWalkers[] = $walker; - continue; - } - foreach ($walker->nodeFqns() as $key) { - if (!isset($nodeWalkers[$key])) { - $nodeWalkers[$key] = [$walker]; - continue; - } - $nodeWalkers[$key][] = $walker; - } - } - - return new self($nodeContextResolver, $globalWalkers, $nodeWalkers, $cache); - } - - public function build(Node $node): Frame - { - $scopedNode = $this->resolveScopeNode($node); - - if (!$uri = $node->getUri()) { - return $this->doBuild($scopedNode, $node); - } - - $scopeKey = sprintf('scope:%s', spl_object_id($scopedNode)); - $scopeExtentKey = sprintf('scopex:%s', spl_object_id($scopedNode)); - - $cache = $this->cache->cacheForDocument(TextDocumentUri::fromString($uri)); - - if (null !== $scopeExtent = $cache->get($scopeExtentKey)) { - if ($node->getStartPosition() > (int)$scopeExtent->scalar()) { - $cache->remove($scopeKey); - } - } - - return $cache->getOrSet($scopeKey, function () use ($cache, $scopeExtentKey, $scopedNode, $node) { - $cache->set($scopeExtentKey, $node->getStartPosition()); - return $this->doBuild($scopedNode, $node); - }); - } - - /** - * @return Generator - */ - public function buildGenerator(Node $node): Generator - { - return $this->walkNode($this->resolveScopeNode($node), $node); - } - - /** - * @param Node|Token|MissingToken $node - */ - public function resolveNode(Frame $frame, $node): NodeContext - { - $info = $this->nodeContextResolver->resolveNode($frame, $node); - - if ($info->issues()) { - $frame->problems()->add($info); - } - - return $info; - } - - public function reflector(): Reflector - { - return $this->nodeContextResolver->reflector(); - } - - public function withWalker(Walker $walker): self - { - $new = $this; - $new->globalWalkers[] = $walker; - - return $new; - } - - public function withoutWalker(string $className): self - { - $new = $this; - foreach ($this->globalWalkers as $walker) { - if (get_class($walker) === $className) { - continue; - } - $new->globalWalkers[] = $walker; - } - foreach ($this->nodeWalkers as $fqn => $walkers) { - $new->nodeWalkers[$fqn] = array_filter($walkers, fn (Walker $walker) => get_class($walker) !== $className); - } - - return $new; - } - - public function resolver(): NodeContextResolver - { - return $this->nodeContextResolver; - } - - private function doBuild(Node $scopedNode, Node $node): Frame - { - $generator = $this->walkNode($scopedNode, $node); - foreach ($generator as $_) { - } - - $frame = $generator->getReturn(); - if (!$frame) { - throw new RuntimeException( - 'Walker did not return a Frame, this should never happen' - ); - } - - return $frame; - } - - /** - * @return Generator - */ - private function walkNode(Node $node, Node $targetNode, ?Frame $frame = null): Generator - { - if ($frame === null) { - $frame = new ConcreteFrame(); - } - - foreach ($this->globalWalkers as $walker) { - $frame = $walker->enter($this, $frame, $node); - } - - $nodeClass = get_class($node); - - if (isset($this->nodeWalkers[$nodeClass])) { - foreach ($this->nodeWalkers[$nodeClass] as $walker) { - $frame = $walker->enter($this, $frame, $node); - } - } - - foreach ($node->getChildNodes() as $childNode) { - $generator = $this->walkNode($childNode, $targetNode, $frame); - yield from $generator; - if ($found = $generator->getReturn()) { - return $found; - } - yield; - } - - if (isset($this->nodeWalkers[$nodeClass])) { - foreach ($this->nodeWalkers[$nodeClass] as $walker) { - $frame = $walker->exit($this, $frame, $node); - } - } - - foreach ($this->globalWalkers as $walker) { - $frame = $walker->exit($this, $frame, $node); - } - - // if we found what we were looking for then return it - if ($node === $targetNode) { - return $frame; - } - - // we start with the source node and we finish with the source node. - if ($node instanceof SourceFileNode) { - return $frame; - } - - return null; - } - - private function resolveScopeNode(Node $node): Node - { - if ($node instanceof SourceFileNode) { - return $node; - } - - // do not traverse the whole source file for functions - if ($node instanceof FunctionLike) { - return $node; - } - - $scopeNode = $node->getFirstAncestor(AnonymousFunctionCreationExpression::class, FunctionLike::class, SourceFileNode::class); - - if (null === $scopeNode) { - throw new RuntimeException(sprintf( - 'Could not find scope node for "%s", this should not happen.', - get_class($node) - )); - } - - // if this is an anonymous function, traverse the parent scope to - // resolve any potential variable imports. - if ($scopeNode instanceof AnonymousFunctionCreationExpression || $scopeNode instanceof ArrowFunctionCreationExpression) { - return $this->resolveScopeNode($scopeNode->parent); - } - - return $scopeNode; - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionArguments.php b/lib/WorseReflection/Core/Inference/FunctionArguments.php deleted file mode 100644 index 45c6d742fc..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionArguments.php +++ /dev/null @@ -1,95 +0,0 @@ - - */ -class FunctionArguments implements IteratorAggregate, Countable -{ - /** - * @param ArgumentExpression[] $arguments - */ - public function __construct( - private NodeContextResolver $resolver, - private Frame $frame, - private array $arguments - ) { - } - - public function __toString(): string - { - return implode(', ', array_map(function (NodeContext $type) { - return $type->type()->__toString(); - }, iterator_to_array($this->getIterator()))); - } - - public static function fromList(NodeContextResolver $resolver, Frame $frame, ?ArgumentExpressionList $list): self - { - if ($list === null) { - return new self($resolver, $frame, []); - } - return new self($resolver, $frame, array_values(array_filter( - $list->children, - fn ($nodeOrToken) => $nodeOrToken instanceof ArgumentExpression, - ))); - } - - public function has(int $index): bool - { - return isset($this->arguments[$index]); - } - - public function at(int $index): NodeContext - { - if (!isset($this->arguments[$index])) { - return NodeContext::none(); - } - - return $this->resolver->resolveNode($this->frame, $this->arguments[$index]); - } - - public function getIterator(): Traversable - { - foreach ($this->arguments as $argument) { - yield $this->resolver->resolveNode($this->frame, $argument); - } - } - - /** - * @return Types - */ - public function types(): Types - { - return new Types(array_map( - fn (NodeContext $context) => $context->type(), - iterator_to_array($this->getIterator()) - )); - } - - public function count(): int - { - return count($this->arguments); - } - - public function from(int $offset): self - { - $newArgs = []; - foreach ($this->arguments as $argOffset => $argument) { - if ($argOffset < $offset) { - continue; - } - $newArgs[] = $argument; - } - - return new self($this->resolver, $this->frame, $newArgs); - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub.php b/lib/WorseReflection/Core/Inference/FunctionStub.php deleted file mode 100644 index 1ae448a74d..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub.php +++ /dev/null @@ -1,12 +0,0 @@ -at(0)->type()->isDefined()) { - return $context; - } - - $closureType = $args->at(0)->type(); - if (!$closureType instanceof ClosureType) { - return $context; - } - - return $context->withType(TypeFactory::array($closureType->returnType())); - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/ArrayMergeStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/ArrayMergeStub.php deleted file mode 100644 index 91385fc5bd..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/ArrayMergeStub.php +++ /dev/null @@ -1,58 +0,0 @@ -type(); - if (!$type instanceof ArrayLiteral) { - $isLiteralSet = false; - break; - } - $typeMap = array_merge($typeMap, $type->types()); - } - - if ($isLiteralSet) { - return $context->withType(TypeFactory::arrayLiteral($typeMap)); - } - - $keys = $values = []; - foreach ($args as $arg) { - $type = $arg->type(); - if (!$type instanceof ArrayType) { - continue; - } - $keys[] = $type->iterableKeyType(); - $values[] = $type->iterableValueType(); - } - - if ($values) { - return $context->withType( - new ArrayType( - TypeFactory::union(...$keys), - TypeFactory::union(...$values), - ) - ); - } - - return $context->withType(TypeUtil::generalTypeFromTypes($types)); - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/ArrayPopStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/ArrayPopStub.php deleted file mode 100644 index fac619da14..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/ArrayPopStub.php +++ /dev/null @@ -1,44 +0,0 @@ -at(0); - $argType = $arg->type(); - if (!$argType->isArray()) { - return $context; - } - - if ($argType instanceof ArrayLiteral) { - $types = $argType->types(); - $poped = array_pop($types); - - if (null === $poped) { - return $context->withType(TypeFactory::null()); - } - - return $context->withType($poped); - } - - $type = TypeFactory::mixed(); - if ($argType instanceof IterableType) { - $type = $argType->iterableValueType(); - } - - return $context->withType(TypeFactory::union($type, TypeFactory::null())); - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/ArrayReduceStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/ArrayReduceStub.php deleted file mode 100644 index 639bb4e86d..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/ArrayReduceStub.php +++ /dev/null @@ -1,26 +0,0 @@ -at(2)->type(); - if ($initialType->isDefined()) { - return $context->withType($initialType->generalize()); - } - - return $context->withType(TypeFactory::array()); - - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/ArrayShiftStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/ArrayShiftStub.php deleted file mode 100644 index b1ac90bac0..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/ArrayShiftStub.php +++ /dev/null @@ -1,44 +0,0 @@ -at(0); - $argType = $arg->type(); - if (!$argType->isArray()) { - return $context; - } - - if ($argType instanceof ArrayLiteral) { - $types = $argType->types(); - $shifted = array_shift($types); - - if (null === $shifted) { - return $context->withType(TypeFactory::null()); - } - - return $context->withType($shifted); - } - - $type = TypeFactory::mixed(); - if ($argType instanceof IterableType) { - $type = $argType->iterableValueType(); - } - - return $context->withType(TypeFactory::union($type, TypeFactory::null())); - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/ArraySumStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/ArraySumStub.php deleted file mode 100644 index 6a8461e8bb..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/ArraySumStub.php +++ /dev/null @@ -1,29 +0,0 @@ -at(0)->type(); - if ($arg0 instanceof ArrayLiteral) { - $context = $context->withType( - TypeFactory::fromValue(array_sum($arg0->value())) - ); - return $context; - } - - return $context; - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/AssertStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/AssertStub.php deleted file mode 100644 index 9148222cd2..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/AssertStub.php +++ /dev/null @@ -1,20 +0,0 @@ -applyTypeAssertions( - $args->at(0)->typeAssertions(), - $context->symbol()->position()->end()->toInt() - ); - return $context; - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/InArrayStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/InArrayStub.php deleted file mode 100644 index bade32fadc..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/InArrayStub.php +++ /dev/null @@ -1,52 +0,0 @@ -at(0); - - if ($arg0->symbol()->symbolType() !== Symbol::VARIABLE) { - return $context->withType(TypeFactory::array()); - } - - $arrayType = $args->at(1)->type(); - if (!$arrayType instanceof ArrayLiteral) { - return $context->withType(TypeFactory::array()); - } - - $union = TypeFactory::union(...$arrayType->iterableValueTypes()); - return $context->withTypeAssertion( - TypeAssertion::forContext( - $args->at(0), - function (Type $type) use ($union) { - return $union; - }, - function (Type $type) use ($union) { - $type = TypeCombinator::subtract($union, $type); - if (!$type->isDefined()) { - $type = $union->generalize(); - } - - return $type; - } - ) - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/IsSomethingStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/IsSomethingStub.php deleted file mode 100644 index 4c3bd6f803..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/IsSomethingStub.php +++ /dev/null @@ -1,46 +0,0 @@ -at(0); - - $symbol = $arg0->symbol(); - if ($symbol->symbolType() === Symbol::VARIABLE) { - $context = $context->withTypeAssertion(TypeAssertion::variable( - $symbol->name(), - $symbol->position()->start()->toInt(), - fn (Type $type) => TypeCombinator::narrowTo($type, $this->isType), - function (Type $type) { - return TypeCombinator::subtract($this->isType, $type); - } - )); - } - - $argType = $arg0->type(); - - // extract to a variabe as it will not otherwise work with PHP 7.4 - $type = $this->isType; - return $context->withType(new BooleanLiteralType($argType instanceof $type)); - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/IteratorToArrayStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/IteratorToArrayStub.php deleted file mode 100644 index b56666c786..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/IteratorToArrayStub.php +++ /dev/null @@ -1,32 +0,0 @@ -withType(TypeFactory::array()); - if (!$args->at(0)->type()->isDefined()) { - return $context; - } - - $argType = $args->at(0)->type(); - - if ($argType instanceof IterableType) { - return $context->withType(TypeFactory::array($argType->iterableValueType())); - } - - return $context; - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStub/ResetStub.php b/lib/WorseReflection/Core/Inference/FunctionStub/ResetStub.php deleted file mode 100644 index 7fcdbf3b25..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStub/ResetStub.php +++ /dev/null @@ -1,42 +0,0 @@ -at(0)->type(); - if (!$argType->isArray()) { - return $context; - } - - $type = TypeFactory::mixed(); - if ($argType instanceof ArrayLiteral) { - $type = $argType->typeAtOffset(0); - - if (!$type->isDefined()) { - return $context->withType(TypeFactory::boolLiteral(false)); - } - - return $context->withType($type); - } - - if ($argType instanceof IterableType) { - $type = $argType->iterableValueType(); - } - - return $context->withType(TypeFactory::union($type, TypeFactory::boolLiteral(false))); - } -} diff --git a/lib/WorseReflection/Core/Inference/FunctionStubRegistry.php b/lib/WorseReflection/Core/Inference/FunctionStubRegistry.php deleted file mode 100644 index 33e56758f7..0000000000 --- a/lib/WorseReflection/Core/Inference/FunctionStubRegistry.php +++ /dev/null @@ -1,23 +0,0 @@ - $functionMap - */ - public function __construct(private array $functionMap) - { - } - - - public function get(string $name): ?FunctionStub - { - if (!isset($this->functionMap[$name])) { - return null; - } - - return $this->functionMap[$name]; - } -} diff --git a/lib/WorseReflection/Core/Inference/GenericMapResolver.php b/lib/WorseReflection/Core/Inference/GenericMapResolver.php deleted file mode 100644 index 030ed97fe6..0000000000 --- a/lib/WorseReflection/Core/Inference/GenericMapResolver.php +++ /dev/null @@ -1,139 +0,0 @@ -reflector->reflectClassLike($topClass->name()); - } catch (SourceNotFound) { - return null; - } - - $templateMap = $topReflection->templateMap(); - $templateMap = $templateMap->mapArguments($arguments); - - if ($topClass->name() == $bottomClass) { - return $templateMap; - } - - foreach (array_merge( - $topReflection->docblock()->implements(), - $topReflection->docblock()->extends() - ) as $genericClass - ) { - if (!$genericClass instanceof GenericClassType) { - continue; - } - - $genericClass = $genericClass->map(function (Type $type) use ($templateMap) { - if ($templateMap->has($type->short())) { - return $templateMap->get($type->short()); - } - return $type; - }); - - if (!$genericClass instanceof GenericClassType) { - // should not happen - continue; - } - - if (null !== $resolved = $this->resolveClassTemplateMap($genericClass, $bottomClass, $genericClass->arguments())) { - return $resolved; - } - } - - return null; - } - - public function mergeParameters(TemplateMap $templateMap, ReflectionParameterCollection $parameters, FunctionArguments $arguments): TemplateMap - { - foreach ($parameters as $parameter) { - $parameterType = $parameter->inferredType(); - - - if ($parameterType instanceof ClassStringType && $parameterType->className()) { - $this->mapClassString($parameterType, $templateMap, $arguments, $parameter); - return $templateMap; - } - $paramTypes = $parameterType->allTypes(); - $argumentTypes = $arguments->at($parameter->index())->type()->allTypes(); - - foreach ($paramTypes as $index => $paramType) { - $argumentType = $argumentTypes->at($index); - - if ($paramType instanceof ClassStringType && $paramType->className()) { - $this->mapClassString($paramType, $templateMap, $arguments, $parameter); - } - - if ($templateMap->has($paramType->short())) { - $templateMap->replace( - $paramType->short(), - $argumentType->generalize() - ); - } - } - } - return $templateMap; - } - - private function mapClassString(ClassStringType $type, TemplateMap $templateMap, FunctionArguments $arguments, ReflectionParameter $parameter): void - { - $classStringType = $type->className()->short(); - if (!$templateMap->has($classStringType)) { - return; - } - if ($parameter->isVariadic()) { - $arguments = $arguments->from($parameter->index()); - } else { - $arguments = [$arguments->at($parameter->index())]; - } - - $types = []; - foreach ($arguments as $index => $argument) { - $argumentType = $argument->type(); - if ($argumentType instanceof ClassStringType) { - $className = $argumentType->className(); - if (null === $className) { - continue; - } - $types[] = TypeFactory::reflectedClass($this->reflector, $className); - } - - if ($argumentType instanceof StringLiteralType) { - $types[] = TypeFactory::reflectedClass($this->reflector, $argumentType->value()); - } - - if ($types) { - $templateMap->replace($classStringType, UnionType::fromTypes(...$types)); - } - } - } -} diff --git a/lib/WorseReflection/Core/Inference/LocalAssignments.php b/lib/WorseReflection/Core/Inference/LocalAssignments.php deleted file mode 100644 index cdbc96d0da..0000000000 --- a/lib/WorseReflection/Core/Inference/LocalAssignments.php +++ /dev/null @@ -1,16 +0,0 @@ -memberType(self::TYPE_METHODS, $containerType, $info, $name); - } - - public function constantType(Type $containerType, NodeContext $info, string $name): NodeContext - { - return $this->memberType(self::TYPE_CONSTANTS, $containerType, $info, $name); - } - - public function propertyType(Type $containerType, NodeContext $info, string $name): NodeContext - { - if (mb_substr($name, 0, 1) == '$') { - $name = mb_substr($name, 1); - } - return $this->memberType(self::TYPE_PROPERTIES, $containerType, $info, $name); - } - - /** - * @return ReflectionClassLike - */ - private function reflectClassOrNull(ClassType $containerType, string $name) - { - return $this->reflector->reflectClassLike($containerType->name); - } - - private function memberType(string $memberType, Type $containerType, NodeContext $info, string $name) - { - if ($containerType instanceof MissingType) { - return $info->withIssue(sprintf( - 'No type available for containing class "%s" for method "%s"', - (string) $containerType, - $name - )); - } - - if (!$containerType instanceof ClassType) { - return $info->withIssue(sprintf( - 'Containing type is not a class, got "%s"', - (string) $containerType - )); - } - - try { - $class = $this->reflectClassOrNull($containerType, $name); - } catch (NotFound $e) { - $info = $info->withIssue(sprintf( - 'Could not find container class "%s" for "%s"', - (string) $containerType, - $name - )); - - return $info; - } - - $info = $info->withContainerType(TypeFactory::reflectedClass($this->reflector, $class->name())); - - if (!method_exists($class, $memberType)) { - $info = $info->withIssue(sprintf( - 'Container class "%s" has no method "%s"', - (string) $containerType, - $memberType - )); - - return $info; - } - - try { - if (false === $class->$memberType()->has($name)) { - $info = $info->withIssue(sprintf( - 'Class "%s" has no %s named "%s"', - (string) $containerType, - $memberType, - $name - )); - - return $info; - } - } catch (NotFound $e) { - $info = $info->withIssue($e->getMessage()); - return $info; - } - - $member = $class->$memberType()->get($name); - assert($member instanceof ReflectionMember); - $declaringClass = $member->declaringClass(); - $info = $info->withContainerType(TypeFactory::reflectedClass($this->reflector, $declaringClass->name())); - - return $info->withType($member->inferredType()); - } -} diff --git a/lib/WorseReflection/Core/Inference/NodeContext.php b/lib/WorseReflection/Core/Inference/NodeContext.php deleted file mode 100644 index 5400aa5f54..0000000000 --- a/lib/WorseReflection/Core/Inference/NodeContext.php +++ /dev/null @@ -1,151 +0,0 @@ -typeAssertions = new TypeAssertions([]); - } - - public static function for(Symbol $symbol): NodeContext - { - return new self($symbol, TypeFactory::unknown()); - } - - public static function fromType(Type $type): NodeContext - { - return new self(Symbol::unknown(), $type); - } - - public static function none(): NodeContext - { - return new self(Symbol::unknown(), new MissingType()); - } - - public function withContainerType(Type $containerType): NodeContext - { - $new = clone $this; - $new->containerType = $containerType; - - return $new; - } - - public function withTypeAssertions(TypeAssertions $typeAssertions): NodeContext - { - $new = clone $this; - $new->typeAssertions = $typeAssertions; - - return $new; - } - - public function withType(Type $type): NodeContext - { - $new = clone $this; - $new->type = $type; - - return $new; - } - - public function withTypeAssertion(TypeAssertion $typeAssertion): NodeContext - { - $new = clone $this; - $new->typeAssertions = $new->typeAssertions->add($typeAssertion); - - return $new; - } - - public function withScope(ReflectionScope $scope): NodeContext - { - $new = clone $this; - $new->scope = $scope; - - return $new; - } - - public function withIssue(string $message): NodeContext - { - $new = clone $this; - $new->issues[] = $message; - - return $new; - } - - /** - * @param Symbol::* $symbolType - */ - public function withSymbolType($symbolType): self - { - $new = clone $this; - $new->symbol = $this->symbol->withSymbolType($symbolType); - - return $new; - } - - public function withSymbolName(string $symbolName): self - { - $new = clone $this; - $new->symbol = $this->symbol->withSymbolName($symbolName); - - return $new; - } - - public function type(): Type - { - return $this->type ?? new MissingType(); - } - - public function symbol(): Symbol - { - return $this->symbol; - } - - public function containerType(): Type - { - return $this->containerType ?: new MissingType(); - } - - /** - * @return string[] - */ - public function issues(): array - { - return $this->issues; - } - - public function scope(): ReflectionScope - { - return $this->scope; - } - - public function typeAssertions(): TypeAssertions - { - return $this->typeAssertions; - } - - public function negateTypeAssertions(): self - { - foreach ($this->typeAssertions as $typeAssertion) { - $typeAssertion->negate(); - } - - return $this; - } -} diff --git a/lib/WorseReflection/Core/Inference/NodeContextFactory.php b/lib/WorseReflection/Core/Inference/NodeContextFactory.php deleted file mode 100644 index a279e64f09..0000000000 --- a/lib/WorseReflection/Core/Inference/NodeContextFactory.php +++ /dev/null @@ -1,111 +0,0 @@ - Symbol::UNKNOWN, - 'container_type' => null, - 'type' => TypeFactory::unknown(), - ]; - - if ($diff = array_diff(array_keys($config), array_keys($defaultConfig))) { - throw new RuntimeException(sprintf( - 'Invalid keys "%s", valid keys "%s"', - implode('", "', $diff), - implode('", "', array_keys($defaultConfig)) - )); - } - - $config = array_merge($defaultConfig, $config); - $position = ByteOffsetRange::fromInts($start, $end); - $symbol = Symbol::fromTypeNameAndPosition( - $config['symbol_type'], - $symbolName, - $position - ); - - return self::contextFromParameters( - $symbol, - $config['type'], - $config['container_type'], - ); - } - - public static function forVariableAt(Frame $frame, int $start, int $end, string $name): NodeContext - { - $varName = ltrim($name, '$'); - $variables = $frame->locals()->byName($varName)->lessThanOrEqualTo($end); - - if (0 === $variables->count()) { - return NodeContextFactory::create( - $name, - $start, - $end, - [ - 'symbol_type' => Symbol::VARIABLE - ] - ); - } - - $variable = $variables->last(); - - return NodeContextFactory::create( - $name, - $start, - $end, - [ - 'type' => $variable->type(), - 'symbol_type' => Symbol::VARIABLE, - ] - ); - } - - /** - * @param Node|Token $nodeOrToken - */ - public static function forNode($nodeOrToken): NodeContext - { - return self::create( - $nodeOrToken instanceof Token ? (string)Token::getTokenKindNameFromValue($nodeOrToken->kind) : $nodeOrToken->getNodeKindName(), - $nodeOrToken->getStartPosition(), - $nodeOrToken->getEndPosition() - ); - } - - private static function contextFromParameters( - Symbol $symbol, - ?Type $type = null, - ?Type $containerType = null - ): NodeContext { - $context = NodeContext::for($symbol); - - if ($type) { - $context = $context->withType($type); - } - - if ($containerType) { - $context = $context->withContainerType($containerType); - } - - return $context; - } -} diff --git a/lib/WorseReflection/Core/Inference/NodeContextResolver.php b/lib/WorseReflection/Core/Inference/NodeContextResolver.php deleted file mode 100644 index 5f1f3f87eb..0000000000 --- a/lib/WorseReflection/Core/Inference/NodeContextResolver.php +++ /dev/null @@ -1,105 +0,0 @@ - $resolverMap - */ - public function __construct( - private Reflector $reflector, - private DocBlockFactory $docblockFactory, - private LoggerInterface $logger, - private Cache $cache, - private array $resolverMap = [] - ) { - } - - public function withCache(Cache $cache):self - { - return new self($this->reflector, $this->docblockFactory, $this->logger, $cache, $this->resolverMap); - } - - /** - * @param Node|Token|MissingToken $node - */ - public function resolveNode(Frame $frame, $node): NodeContext - { - try { - return $this->doResolveNodeWithCache($frame, $node); - } catch (CouldNotResolveNode $couldNotResolveNode) { - return NodeContextFactory::forNode($node) - ->withIssue($couldNotResolveNode->getMessage()); - } - } - - public function reflector(): Reflector - { - return $this->reflector; - } - - public function docblockFactory(): DocBlockFactory - { - return $this->docblockFactory; - } - - /** - * Cache node look ups. Note that resolvers do not know about their parents - * and will use the node resolver to fetch a parents context. This only - * work if there is a cache. The cache should only have a lifetime of the - * current operation. - * - * @param Node|Token|MissingToken|array $node - */ - private function doResolveNodeWithCache(Frame $frame, $node): NodeContext - { - // somehow we can get an array of missing tokens here instead of an object... - if (!is_object($node)) { - return NodeContext::none(); - } - - $key = 'sc:'.spl_object_id($node); - - return $this->cache->getOrSet($key, function () use ($frame, $node) { - if (false === $node instanceof Node) { - throw new CouldNotResolveNode(sprintf( - 'Non-node class passed to resolveNode, got "%s"', - get_class($node) - )); - } - $this->cacheMisses++; - - $context = $this->doResolveNode($frame, $node); - $context = $context->withScope(new ReflectionScope($this->reflector, $node)); - - return $context; - }); - } - - private function doResolveNode(Frame $frame, Node $node): NodeContext - { - $this->logger->debug(sprintf('Resolving: %s', get_class($node))); - - if (isset($this->resolverMap[get_class($node)])) { - return $this->resolverMap[get_class($node)]->resolve($this, $frame, $node); - } - - throw new CouldNotResolveNode(sprintf( - 'Did not know how to resolve node of type "%s" with text "%s"', - get_class($node), - $node->getText() - )); - } -} diff --git a/lib/WorseReflection/Core/Inference/NodeReflector.php b/lib/WorseReflection/Core/Inference/NodeReflector.php deleted file mode 100644 index 8cde21e6ca..0000000000 --- a/lib/WorseReflection/Core/Inference/NodeReflector.php +++ /dev/null @@ -1,149 +0,0 @@ -reflectMemberAccessExpression($frame, $node); - } - - if ($node instanceof ScopedPropertyAccessExpression) { - return $this->reflectScopedPropertyAccessExpression($frame, $node); - } - - if ($node instanceof ObjectCreationExpression) { - return $this->reflectObjectCreationExpression($frame, $node); - } - if ($node instanceof CallExpression) { - return $this->reflectCallExpression($frame, $node); - } - - if ($node instanceof MatchExpression) { - return $this->reflectMatchExpression($frame, $node); - } - - if ($node->parent instanceof Attribute) { - return $this->reflectAttribute($frame, $node->parent); - } - - throw new CouldNotResolveNode(sprintf( - 'Did not know how to reflect node of type "%s"', - get_class($node) - )); - } - - private function reflectScopedPropertyAccessExpression(Frame $frame, ScopedPropertyAccessExpression $node): ReflectionStaticMemberAccess|ReflectionStaticMethodCall - { - if ($node->parent instanceof CallExpression) { - return $this->reflectStaticMethodCall($frame, $node); - } - - return $this->reflectCaseOrConstant($frame, $node); - } - - private function reflectMemberAccessExpression(Frame $frame, MemberAccessExpression $node): ReflectionMethodCall - { - if ($node->parent instanceof CallExpression) { - return $this->reflectMethodCall($frame, $node); - } - throw new CouldNotResolveNode(sprintf( - 'Did not know how to reflect node of type "%s"', - get_class($node) - )); - } - - private function reflectMethodCall(Frame $frame, MemberAccessExpression $node): ReflectionMethodCall - { - return new ReflectionMethodCall( - $this->services, - $frame, - $node - ); - } - - private function reflectStaticMethodCall(Frame $frame, ScopedPropertyAccessExpression $node): ReflectionStaticMethodCall - { - return new ReflectionStaticMethodCall( - $this->services, - $frame, - $node - ); - } - - private function reflectObjectCreationExpression(Frame $frame, ObjectCreationExpression $node): ReflectionObjectCreationExpression - { - return new PhpactorReflectionObjectCreationExpression( - $this->services, - $frame, - $node - ); - } - - private function reflectAttribute(Frame $frame, Attribute $node): ReflectionNode - { - return new ReflectionAttribute( - $this->services, - $frame, - $node - ); - } - - private function reflectCaseOrConstant(Frame $frame, ScopedPropertyAccessExpression $node): ReflectionStaticMemberAccess - { - return new ReflectionStaticMemberAccess( - $this->services, - $frame, - $node - ); - } - - private function reflectMatchExpression(Frame $frame, MatchExpression $node): ReflectionNode - { - return new ReflectionMatchExpression( - $this->services, - $frame, - $node - ); - } - - private function reflectCallExpression(Frame $frame, CallExpression $node): ReflectionNode - { - if ($node->callableExpression instanceof MemberAccessExpression) { - return new ReflectionMethodCall( - $this->services, - $frame, - $node->callableExpression - ); - } - - throw new CouldNotResolveNode(sprintf( - 'Did not know how to reflect node of type "%s"', - get_class($node) - )); - } -} diff --git a/lib/WorseReflection/Core/Inference/NodeToTypeConverter.php b/lib/WorseReflection/Core/Inference/NodeToTypeConverter.php deleted file mode 100644 index e5deb41d05..0000000000 --- a/lib/WorseReflection/Core/Inference/NodeToTypeConverter.php +++ /dev/null @@ -1,154 +0,0 @@ -getText(); - - /** @var Type $type */ - $type = $type instanceof Type ? $type : TypeFactory::fromStringWithReflector($type, $this->reflector); - - if ($this->isUseDefinition($node)) { - return TypeFactory::fromStringWithReflector((string) $type, $this->reflector); - } - - if ($type instanceof ScalarType) { - return $type; - } - - if ($type instanceof ClassType && $type->name->wasFullyQualified()) { - return $type; - } - - if ($type instanceof SelfType || $type instanceof StaticType) { - return $this->currentClass($node, $currentClass); - } - - if ($type instanceof ClassType && (string) $type == 'parent') { - return $this->parentClass($node); - } - - if ($importedType = $this->fromClassImports($node, $type)) { - return $importedType; - } - - $namespaceDefinition = $node->getNamespaceDefinition(); - if ($type instanceof ClassType && $namespaceDefinition && $namespaceDefinition->name instanceof QualifiedName) { - $className = $type->name->prepend($namespaceDefinition->name->getText()); - $type->name = $className; - - return $type; - } - - return $type; - } - - private function parentClass(Node $node): Type - { - /** @var ClassDeclaration $class */ - $class = $node->getFirstAncestor(ClassDeclaration::class); - - /** @phpstan-ignore-next-line */ - if (null === $class) { - $this->logger->warning('"parent" keyword used outside of class scope'); - return TypeFactory::unknown(); - } - - if (null === $class->classBaseClause) { - $this->logger->warning('"parent" keyword used but class does not extend anything'); - return TypeFactory::unknown(); - } - - - return TypeFactory::fromStringWithReflector( - $class->classBaseClause->baseClass->getResolvedName(), - $this->reflector - ); - } - - private function currentClass(Node $node, ?Name $currentClass = null): Type - { - if ($currentClass) { - return TypeFactory::fromStringWithReflector($currentClass->full(), $this->reflector); - } - $class = $node->getFirstAncestor(ClassLike::class); - - if (null === $class) { - return TypeFactory::unknown(); - } - - assert($class instanceof NamespacedNameInterface); - - return TypeFactory::fromStringWithReflector($class->getNamespacedName(), $this->reflector); - } - - private function isUseDefinition(Node $node): bool - { - return $node->getParent() instanceof NamespaceUseClause; - } - - private function fromClassImports(Node $node, Type $type): ?Type - { - $imports = $node->getImportTablesForCurrentScope(); - $classImports = $imports[0]; - - if (!$type instanceof ClassType) { - return $type; - } - - $className = $type->name->__toString(); - - if (isset($classImports[$className])) { - $type->name = ClassName::fromString((string) $classImports[$className]); - return $type; - } - - if (isset($classImports[$type->name->head()->__toString()])) { - $type->name = ClassName::fromString( - (string) $classImports[(string) $type->name->head()] . '\\' . (string) $type->name->tail() - ); - return $type; - } - - return null; - } -} diff --git a/lib/WorseReflection/Core/Inference/Problems.php b/lib/WorseReflection/Core/Inference/Problems.php deleted file mode 100644 index e34e6fbe5b..0000000000 --- a/lib/WorseReflection/Core/Inference/Problems.php +++ /dev/null @@ -1,79 +0,0 @@ - - */ -final class Problems implements IteratorAggregate, Countable -{ - /** - * @param NodeContext[] $problems - */ - private function __construct(private array $problems = []) - { - } - - public function __toString() - { - $lines = []; - foreach ($this->problems as $symbolInformation) { - $lines[] = sprintf( - '%s:%s %s', - $symbolInformation->symbol()->position()->start()->toInt(), - $symbolInformation->symbol()->position()->end()->toInt(), - implode(', ', $symbolInformation->issues()) - ); - } - - return implode("\n", $lines); - } - - public static function create(): Problems - { - return new self(); - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->problems); - } - - public function add(NodeContext $problem): void - { - $this->problems[] = $problem; - } - - public function none(): bool - { - return count($this->problems) === 0; - } - - public function count(): int - { - return count($this->problems); - } - - /** - * @return NodeContext[] - */ - public function toArray(): array - { - return $this->problems; - } - - public function merge(Problems $problems): self - { - return new self(array_merge( - $this->problems, - $problems->toArray() - )); - } -} diff --git a/lib/WorseReflection/Core/Inference/PropertyAssignments.php b/lib/WorseReflection/Core/Inference/PropertyAssignments.php deleted file mode 100644 index ebc194fd6b..0000000000 --- a/lib/WorseReflection/Core/Inference/PropertyAssignments.php +++ /dev/null @@ -1,16 +0,0 @@ -reflector(), - $node, - $node->returnTypeList - ); - - $args = []; - /** @phpstan-ignore-next-line [TR] No trust */ - if ($node->parameters) { - foreach ($node->parameters->getChildNodes() as $parameter) { - if (!$parameter instanceof Parameter) { - continue; - } - $args[] = $resolver->resolveNode($frame, $parameter)->type(); - } - } - - $type = new ClosureType($resolver->reflector(), $args, $type); - - return NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'type' => $type, - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ArgumentExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ArgumentExpressionResolver.php deleted file mode 100644 index 24850cc85f..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ArgumentExpressionResolver.php +++ /dev/null @@ -1,23 +0,0 @@ -expression === null) { - throw new CouldNotResolveNode('Expression is null'); - } - return $resolver->resolveNode($frame, $node->expression); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ArrayCreationExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ArrayCreationExpressionResolver.php deleted file mode 100644 index 1b2c2cc9e7..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ArrayCreationExpressionResolver.php +++ /dev/null @@ -1,58 +0,0 @@ -arrayElements) { - return NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'type' => TypeFactory::arrayLiteral([]), - ] - ); - } - - foreach ($node->arrayElements->getElements() as $element) { - $value = $resolver->resolveNode($frame, $element->elementValue)->type(); - if ($element->elementKey) { - $key = $resolver->resolveNode($frame, $element->elementKey)->type(); - $keyValue = TypeUtil::valueOrNull($key); - if (null === $keyValue) { - continue; - } - $array[$keyValue] = $value; - continue; - } - - $array[] = $value; - } - - return NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'type' => TypeFactory::arrayLiteral($array), - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ArrowFunctionCreationExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ArrowFunctionCreationExpressionResolver.php deleted file mode 100644 index 40914bd56f..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ArrowFunctionCreationExpressionResolver.php +++ /dev/null @@ -1,51 +0,0 @@ -reflector(), - $node, - $node->returnTypeList - ); - - $args = []; - /** @phpstan-ignore-next-line [TR] No trust */ - if ($node->parameters) { - foreach ($node->parameters->getChildNodes() as $parameter) { - if (!$parameter instanceof Parameter) { - continue; - } - $args[] = $resolver->resolveNode($frame, $parameter)->type(); - } - } - - if (!$returnType->isDefined()) { - $returnType = $resolver->resolveNode($frame, $node->resultExpression)->type()->generalize(); - } - - return NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'type' => new ClosureType($resolver->reflector(), $args, $returnType), - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php deleted file mode 100644 index b2cd2eb108..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php +++ /dev/null @@ -1,296 +0,0 @@ -getStartPosition(), $node->getEndPosition()); - assert($node instanceof AssignmentExpression); - - $rightContext = $resolver->resolveNode($frame, $node->rightOperand); - - if ($this->hasMissingTokens($node)) { - return $context; - } - - if ($node->leftOperand instanceof Variable) { - $this->walkParserVariable($frame, $node->leftOperand, $rightContext); - return $context; - } - - if ($node->leftOperand instanceof ListIntrinsicExpression) { - $this->walkList($frame, $node->leftOperand, $rightContext); - return $context; - } - - if ($node->leftOperand instanceof ArrayCreationExpression) { - $this->walkArrayCreation($frame, $node->leftOperand, $rightContext); - return $context; - } - - if ($node->leftOperand instanceof MemberAccessExpression) { - $this->walkMemberAccessExpression($resolver, $frame, $node->leftOperand, $rightContext); - return $context; - } - - if ($node->leftOperand instanceof SubscriptExpression) { - $this->walkSubscriptExpression($resolver, $frame, $node->leftOperand, $rightContext); - return $context; - } - - return $context; - } - - private function walkParserVariable(Frame $frame, Variable $leftOperand, NodeContext $rightContext): void - { - $name = NodeUtil::nameFromTokenOrNode($leftOperand, $leftOperand->name); - $context = NodeContextFactory::create( - $name, - $leftOperand->getStartPosition(), - $leftOperand->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => $rightContext->type(), - ] - ); - - $frame->locals()->set(WorseVariable::fromSymbolContext($context)->asAssignment()); - } - - private function walkMemberAccessExpression( - NodeContextResolver $resolver, - Frame $frame, - MemberAccessExpression $leftOperand, - NodeContext $typeContext - ): void { - $variable = $leftOperand->dereferencableExpression; - - // we do not track assignments to other classes. - if (false === in_array($variable, [ '$this', 'self' ])) { - return; - } - - $memberNameNode = $leftOperand->memberName; - - // TODO: Sort out this mess. - // If the node is not a token (e.g. it is a variable) then - // evaluate the variable (e.g. $this->$foobar); - if ($memberNameNode instanceof Token) { - $memberName = $memberNameNode->getText($leftOperand->getFileContents()); - /** @phpstan-ignore-next-line */ - } else { - $memberType = $resolver->resolveNode($frame, $memberNameNode)->type(); - - if (!$memberType instanceof StringType) { - return; - } - - $memberName = TypeUtil::valueOrNull($memberType); - } - - $context = NodeContextFactory::create( - (string)$memberName, - $leftOperand->getStartPosition(), - $leftOperand->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => $typeContext->type(), - ] - ); - - $frame->properties()->set(WorseVariable::fromSymbolContext($context)); - } - - private function walkArrayCreation(Frame $frame, ArrayCreationExpression $leftOperand, NodeContext $nodeContext): void - { - $list = $leftOperand->arrayElements; - if (!$list instanceof ArrayElementList) { - return; - } - - $this->walkArrayElements($list->children, $leftOperand, $nodeContext->type(), $frame); - } - - private function walkList(Frame $frame, ListIntrinsicExpression $leftOperand, NodeContext $nodeContext): void - { - $list = $leftOperand->listElements; - if (!$list instanceof ListExpressionList) { - return; - } - - $this->walkArrayElements($list->children, $leftOperand, $nodeContext->type(), $frame); - } - - private function walkSubscriptExpression(NodeContextResolver $resolver, Frame $frame, SubscriptExpression $leftOperand, NodeContext $rightContext): void - { - if ($leftOperand->postfixExpression instanceof Variable) { - foreach ($frame->locals()->byName((string)$leftOperand->postfixExpression->getName()) as $variable) { - $type = $variable->type(); - - if (!$type instanceof ArrayType) { - return; - } - - // array key specified, e.g. `$foo['bar'] = ` - // @phpstan-ignore-next-line TP lies - if ($leftOperand->accessExpression) { - $accessType = $resolver->resolveNode($frame, $leftOperand->accessExpression)->type(); - - if (!$accessType instanceof Literal) { - $frame->locals()->set( - $variable->withType( - new ArrayType(TypeFactory::undefined(), $rightContext->type()) - ) - ); - return; - } - - if ($type instanceof ArrayLiteral) { - $frame->locals()->set( - $variable->withType( - $type->set($accessType->value(), $rightContext->type()) - )->withOffset($leftOperand->getStartPosition()) - ); - } - continue; - } - - // @phpstan-ignore-next-line TP lies - if ($rightContext->type() instanceof Literal) { - $frame->locals()->set( - $variable->withType( - $type->add($rightContext->type()) - )->withOffset($leftOperand->getStartPosition()) - ); - continue; - } - - $frame->locals()->set( - $variable->withType( - TypeFactory::array($rightContext->type()) - )->withOffset($leftOperand->getStartPosition()) - ); - } - } - - if ($leftOperand->postfixExpression instanceof MemberAccessExpression) { - $rightContext = $rightContext->withType(TypeFactory::array()); - $this->walkMemberAccessExpression($resolver, $frame, $leftOperand->postfixExpression, $rightContext); - } - } - - private function hasMissingTokens(AssignmentExpression $node): bool - { - // this would probably never happen ... - if (false === $node->parent instanceof ExpressionStatement) { - return false; - } - - foreach ($node->parent->getDescendantTokens() as $token) { - if ($token instanceof MissingToken) { - return true; - } - } - - return false; - } - - /** - * @param mixed[] $elements - */ - private function walkArrayElements(array $elements, Node $leftOperand, Type $type, Frame $frame): void - { - $index = -1; - foreach ($elements as $element) { - if (!$element instanceof ArrayElement) { - continue; - } - - $index++; - $elementValue = $element->elementValue; - if ($elementValue instanceof ArrayCreationExpression) { - $list = $elementValue->arrayElements; - if (!$list instanceof ArrayElementList) { - return; - } - $accessType = $this->offsetType($type, $index); - $this->walkArrayElements($list->children, $leftOperand, $accessType, $frame); - continue; - } - if (!$elementValue instanceof Variable) { - continue; - } - - /** @phpstan-ignore-next-line */ - if (null === $elementValue || null === $elementValue->name) { - continue; - } - - $varName = NodeUtil::nameFromTokenOrNode($leftOperand, $elementValue->name); - - $variableContext = NodeContextFactory::create( - (string)$varName, - $element->getStartPosition(), - $element->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - ] - ); - - - $variableContext = $variableContext->withType($this->offsetType($type, $index)); - $frame->locals()->set(WorseVariable::fromSymbolContext($variableContext)->asAssignment()); - } - } - - private function offsetType(Type $type, int $index): Type - { - if ($type instanceof ArrayAccessType) { - return $type->typeAtOffset($index); - } - - if ($type instanceof AggregateType) { - $agg = []; - foreach ($type->types as $type) { - $agg[] = $this->offsetType($type, $index); - } - return $type->fromTypes(...$agg); - } - - return new MissingType(); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/BinaryExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/BinaryExpressionResolver.php deleted file mode 100644 index 5dda13e719..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/BinaryExpressionResolver.php +++ /dev/null @@ -1,331 +0,0 @@ -operator->kind; - - $context = NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - ] - ); - - $left = $resolver->resolveNode($frame, $node->leftOperand); - $right = $resolver->resolveNode($frame, $node->rightOperand); - - // merge type assertions from left AND right - $context = $context->withTypeAssertions( - $left->typeAssertions()->merge($right->typeAssertions()) - ); - - // resolve the type of the expression - $context = $context->withType( - $this->walkBinaryExpression( - $left->type(), - $right->type(), - $operator - ) - ); - - // work around for https://github.com/Microsoft/tolerant-php-parser/issues/19#issue-201714377 - // the left hand side of instanceof should be parsed as a variable but it's not. - $leftOperand = $node->leftOperand; - if ($leftOperand instanceof UnaryExpression) { - $leftOperand = $leftOperand->operand; - $left = $resolver->resolveNode($frame, $leftOperand); - } - - if (!$leftOperand instanceof Node) { - return $context->withIssue(sprintf('Left operand was not a node, got "%s"', get_class($leftOperand))); - } - - if (!$node->rightOperand instanceof Node) { - return $context->withIssue(sprintf('Right operand was not a node, got "%s"', get_class($node->rightOperand))); - } - - // apply any type assertions (e.g. ===, instanceof, etc) - $context = $this->applyTypeAssertions( - $context, - $left, - $right, - $leftOperand, - $node->rightOperand, - $operator - ); - - if (!$node->leftOperand instanceof Node) { - return $context->withIssue(sprintf('Left operand was not a node, got "%s"', get_class($leftOperand))); - } - - // negate if there is a boolean comparison against an expression - $context = $this->negate( - $context, - $node->leftOperand, - $node->rightOperand, - $operator - ); - - $this->addVariable($operator, $frame, $leftOperand, $context); - - $frame->applyTypeAssertions($context->typeAssertions(), $node->getStartPosition()); - - return $context; - } - - private function walkBinaryExpression( - Type $left, - Type $right, - int $operator - ): Type { - if ($operator === TokenKind::QuestionQuestionToken) { - return $this->nullCoalesce($left, $right); - } - if ($left instanceof Concatable) { - switch ($operator) { - case TokenKind::DotToken: - case TokenKind::DotEqualsToken: - return $left->concat($right); - } - } - if ($left instanceof Comparable) { - $value = null; - $value = match ($operator) { - TokenKind::EqualsEqualsEqualsToken => $left->identical($right), - TokenKind::EqualsEqualsToken => $left->equal($right), - TokenKind::GreaterThanToken => $left->greaterThan($right), - TokenKind::GreaterThanEqualsToken => $left->greaterThanEqual($right), - TokenKind::LessThanToken => $left->lessThan($right), - TokenKind::LessThanEqualsToken => $left->lessThanEqual($right), - TokenKind::ExclamationEqualsToken => $left->notEqual($right), - TokenKind::ExclamationEqualsEqualsToken => $left->notIdentical($right), - default => null, - }; - if ($value !== null) { - return $value; - } - } - - if ($left instanceof ArrayType) { - switch ($operator) { - case TokenKind::PlusToken: - return $left->mergeType($right); - } - } - - $value = match ($operator) { - TokenKind::OrKeyword, TokenKind::BarBarToken => TypeUtil::toBool($left)->or(TypeUtil::toBool($right)), - TokenKind::AndKeyword, TokenKind::AmpersandAmpersandToken => TypeUtil::toBool($left)->and(TypeUtil::toBool($right)), - TokenKind::XorKeyword => TypeUtil::toBool($left)->xor(TypeUtil::toBool($right)), - TokenKind::PlusToken => TypeUtil::toNumber($left)->plus(TypeUtil::toNumber($right)), - TokenKind::MinusToken => TypeUtil::toNumber($left)->minus(TypeUtil::toNumber($right)), - TokenKind::AsteriskToken => TypeUtil::toNumber($left)->multiply(TypeUtil::toNumber($right)), - TokenKind::SlashToken => TypeUtil::toNumber($left)->divide(TypeUtil::toNumber($right)), - TokenKind::PercentToken => TypeUtil::toNumber($left)->modulo(TypeUtil::toNumber($right)), - TokenKind::AsteriskAsteriskToken => TypeUtil::toNumber($left)->exp(TypeUtil::toNumber($right)), - TokenKind::PipeToken => (function (Type $left, Type $right) { - if ($right instanceof ClosureType) { - return $right->returnType(); - } - - return $right; - })($left, $right), - default => null, - }; - if ($value !== null) { - return $value; - } - - if ($left instanceof BitwiseOperable) { - switch ($operator) { - case TokenKind::AmpersandToken: - return $left->bitwiseAnd($right); - case TokenKind::BarToken: - return $left->bitwiseOr($right); - case TokenKind::CaretToken: - return $left->bitwiseXor($right); - case TokenKind::LessThanLessThanToken: - return $left->shiftLeft($right); - case TokenKind::GreaterThanGreaterThanToken: - return $left->shiftRight($right); - } - } - - if ($left instanceof ClassType) { - switch ($operator) { - case TokenKind::InstanceOfKeyword: - return TypeFactory::boolLiteral(true); - } - } - - return new MissingType(); - } - - private function applyTypeAssertions( - NodeContext $context, - NodeContext $leftContext, - NodeContext $rightContext, - Node $leftOperand, - Node $rightOperand, - int $operator - ): NodeContext { - switch ($operator) { - case TokenKind::OrKeyword: - case TokenKind::BarBarToken: - return $context->withTypeAssertions( - $leftContext->typeAssertions()->or($rightContext->typeAssertions()) - ); - case TokenKind::AndKeyword: - case TokenKind::AmpersandAmpersandToken: - return $context->withTypeAssertions( - $leftContext->typeAssertions()->and($rightContext->typeAssertions()) - ); - } - - if (!NodeUtil::canAcceptTypeAssertion($leftOperand, $rightOperand)) { - return $context; - } - - [$reciever, $recieverContext ] = NodeUtil::canAcceptTypeAssertion( - $leftOperand - ) ? [$leftOperand, $leftContext] : [$rightOperand, $rightContext]; - [$transmitter, $transmittingContext ] = NodeUtil::canAcceptTypeAssertion( - $rightOperand - ) ? [$leftOperand, $leftContext] : [$rightOperand, $rightContext]; - - if (!NodeUtil::canAcceptTypeAssertion($reciever)) { - return $context; - } - return match ($operator) { - TokenKind::EqualsEqualsEqualsToken => $context->withTypeAssertion(TypeAssertion::forContext( - $recieverContext, - fn (Type $type) => $transmittingContext->type(), - - // ??? - fn (Type $type) => TypeCombinator::subtract($transmittingContext->type(), $type), - )), - TokenKind::InstanceOfKeyword => $context->withTypeAssertion(TypeAssertion::forContext( - $recieverContext, - function (Type $type) use ($transmittingContext) { - $type = TypeCombinator::acceptedByType($type, TypeFactory::object()); - $type = TypeCombinator::narrowTo($type, $transmittingContext->type()); - return $type; - }, - function (Type $type) use ($transmittingContext) { - $subtracted = TypeCombinator::subtract($transmittingContext->type(), $type); - return $subtracted; - } - )), - default => $context, - }; - } - - private function negate( - NodeContext $context, - Node $leftOperand, - Node $rightOperand, - int $operator - ): NodeContext { - $boolean = $leftOperand instanceof ReservedWord ? $leftOperand : $rightOperand; - - if (!$boolean instanceof ReservedWord) { - return $context; - } - - $text = $boolean->getText(); - - // if this is an OR then we don't negate the type - if (in_array($operator, [TokenKind::OrKeyword, TokenKind::BarBarToken])) { - return $context; - } - - if ($text === 'false') { - $context->typeAssertions()->negate(); - return $context; - } - - return $context; - } - - private function nullCoalesce(Type $left, Type $right): Type - { - if ($left instanceof MissingType) { - return $right; - } - - if ($left->isNullable()) { - return TypeFactory::union($left->stripNullable(), $right); - } - - if (!$left->isNull()) { - return $left; - } - - return $right; - } - - private function addVariable( - int $operator, - Frame $frame, - Node $leftOperand, - NodeContext $context - ): void { - if (!$leftOperand instanceof Variable) { - return; - } - - if (!in_array($operator, [ - TokenKind::DotEqualsToken, - ])) { - return; - } - - $name = NodeUtil::nameFromTokenOrNode($leftOperand, $leftOperand->name); - $context = NodeContextFactory::create( - $name, - $leftOperand->getStartPosition(), - $leftOperand->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => $context->type(), - ] - ); - - $frame->locals()->set(PhpactorVariable::fromSymbolContext($context)); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/CallExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/CallExpressionResolver.php deleted file mode 100644 index 5187182a0a..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/CallExpressionResolver.php +++ /dev/null @@ -1,170 +0,0 @@ -callableExpression; - - $context = $resolver->resolveNode($frame, $resolvableNode); - $returnType = $context->type(); - $containerType = $context->containerType(); - - // if this is a closure type then this _was_ a first-class callable - // and the "retutn type" is just the Closure type - if ($returnType instanceof ClosureType) { - if (NodeUtil::isFirstClassCallable($node)) { - return $context; - } - } - - if ( - $context instanceof CallContext && $context->arguments() - ) { - $this->applyAssertions($context, $frame, $node); - } - - if ($returnType instanceof ConditionalType) { - $context = $this->processConditionalType($returnType, $containerType, $context, $resolver, $frame, $node); - } - - if ($resolvableNode instanceof ParenthesizedExpression && $returnType instanceof ReflectedClassType && $returnType->isInvokable()) { - return NodeContextFactory::forNode($node) - ->withType($returnType->invokeType()); - } - - if ($returnType instanceof InvokeableType) { - return NodeContextFactory::forNode($node) - ->withType($returnType->returnType()); - } - - if (!$resolvableNode instanceof Variable) { - return $context; - } - - if ($returnType instanceof ReflectedClassType && $returnType->isInvokable()) { - return NodeContextFactory::forNode($node) - ->withType($returnType->invokeType()); - } - - if (!$returnType instanceof InvokeableType) { - return NodeContext::none(); - } - - return NodeContextFactory::create( - NodeUtil::nameFromTokenOrNode($resolvableNode, $resolvableNode->name), - $resolvableNode->getStartPosition(), - $resolvableNode->getEndPosition(), - [ - 'type' => $returnType->returnType(), - ] - ); - } - - private function processConditionalType( - ConditionalType $type, - Type $containerType, - NodeContext $context, - NodeContextResolver $resolver, - Frame $frame, - CallExpression $node - ): NodeContext { - if ($containerType instanceof ReflectedClassType) { - $reflection = $containerType->reflectionOrNull(); - if (!$reflection) { - return $context; - } - $method = $reflection->methods()->get($context->symbol()->name()); - return $context->withType($type->evaluate( - $method, - FunctionArguments::fromList($resolver, $frame, $node->argumentExpressionList) - )); - } - - if ($context->symbol()->symbolType() === Symbol::FUNCTION) { - $function = $resolver->reflector()->reflectFunction($context->symbol()->name()); - $arguments = FunctionArguments::fromList($resolver, $frame, $node->argumentExpressionList); - return (new FunctionCallContext( - $context->symbol(), - ByteOffsetRange::fromInts( - $node->getStartPosition(), - $node->getEndPosition() - ), - $function, - $arguments, - ))->withType($type->evaluate( - $function, - $arguments - )); - } - - return $context; - } - - private function applyAssertions( - CallContext $context, - Frame $frame, - CallExpression $node, - ): void { - $arguments = $context->arguments(); - $member = $context->callable(); - - if (null === $arguments) { - return; - } - - $parameters = $member->parameters(); - if (count($member->docblock()->assertions()) === 0) { - return; - } - $map = $this->resolver->mergeParameters($member->docblock()->templateMap(), $parameters, $arguments); - foreach ($member->docblock()->assertions() as $assertion) { - - if (!$parameters->has($assertion->variableName)) { - continue; - } - $param = $parameters->get($assertion->variableName); - $arg = $arguments->at($param->index()); - $type = $assertion->type; - if ($assertion->negated) { - $type = TypeCombinator::subtract($assertion->type, $arg->type()); - } - - $frame->locals()->set(new PhpactorVariable( - $arg->symbol()->name(), - $node->getStartPosition(), - $map->getOrGiven($type), - )); - } - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/CastExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/CastExpressionResolver.php deleted file mode 100644 index 1944df6dea..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/CastExpressionResolver.php +++ /dev/null @@ -1,50 +0,0 @@ -castType); - $type = rtrim(ltrim($type, '('), ')'); - $type = TypeFactory::fromStringWithReflector($type, $resolver->reflector()); - - $context = NodeContextFactory::create( - 'cast', - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'type' => $type, - ] - ); - - if (!in_array($type->__toString(), [ - 'string', - 'bool', - 'float', - 'string', - 'array', - 'object', - 'integer', - 'boolean', - 'double' - ])) { - $context = $context->withIssue(sprintf('Unsupported cast "%s"', $type->__toString())); - } - - return $context; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/CatchClauseResolver.php b/lib/WorseReflection/Core/Inference/Resolver/CatchClauseResolver.php deleted file mode 100644 index c38c5ddc17..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/CatchClauseResolver.php +++ /dev/null @@ -1,50 +0,0 @@ -getStartPosition(), $node->getEndPosition()); - assert($node instanceof CatchClause); - - /** @phpstan-ignore-next-line Lies */ - if (!$node->qualifiedNameList instanceof QualifiedNameList) { - return $context; - } - - /** @phpstan-ignore-next-line Lies */ - $type = $resolver->resolveNode($frame, $node->qualifiedNameList)->type(); - $variableName = $node->variableName; - - if (null === $variableName) { - return $context; - } - - $context = NodeContextFactory::create( - (string)$variableName->getText($node->getFileContents()), - $variableName->getStartPosition(), - $variableName->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => $type, - ] - ); - - $frame->locals()->set(Variable::fromSymbolContext($context)->asDefinition()); - - return $context; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ClassLikeResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ClassLikeResolver.php deleted file mode 100644 index e246571523..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ClassLikeResolver.php +++ /dev/null @@ -1,42 +0,0 @@ -name->getText((string)$node->getFileContents()), - $node->name->getStartPosition(), - $node->name->getEndPosition(), - [ - 'symbol_type' => Symbol::CLASS_, - 'type' => TypeFactory::fromStringWithReflector( - $node->getNamespacedName(), - $resolver->reflector(), - ) - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/CloneExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/CloneExpressionResolver.php deleted file mode 100644 index dcfedd36ad..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/CloneExpressionResolver.php +++ /dev/null @@ -1,19 +0,0 @@ -resolveNode($frame, $node->expression); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/CompoundStatementResolver.php b/lib/WorseReflection/Core/Inference/Resolver/CompoundStatementResolver.php deleted file mode 100644 index 7b8ee90ff2..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/CompoundStatementResolver.php +++ /dev/null @@ -1,24 +0,0 @@ -statements as $statement) { - $resolver->resolveNode($frame, $statement); - } - - return NodeContextFactory::forNode($node); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ConstElementResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ConstElementResolver.php deleted file mode 100644 index 0918c87bbc..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ConstElementResolver.php +++ /dev/null @@ -1,30 +0,0 @@ -getName(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::CONSTANT, - 'container_type' => NodeUtil::nodeContainerClassLikeType($resolver->reflector(), $node) - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/EnumCaseDeclarationResolver.php b/lib/WorseReflection/Core/Inference/Resolver/EnumCaseDeclarationResolver.php deleted file mode 100644 index cfda777922..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/EnumCaseDeclarationResolver.php +++ /dev/null @@ -1,30 +0,0 @@ -name), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::CASE, - 'container_type' => NodeUtil::nodeContainerClassLikeType($resolver->reflector(), $node) - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ExpressionStatementResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ExpressionStatementResolver.php deleted file mode 100644 index 37f5ccce56..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ExpressionStatementResolver.php +++ /dev/null @@ -1,21 +0,0 @@ -resolveNode($frame, $node->expression); - return NodeContextFactory::forNode($node); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ForeachStatementResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ForeachStatementResolver.php deleted file mode 100644 index 988feb5262..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ForeachStatementResolver.php +++ /dev/null @@ -1,224 +0,0 @@ -resolveNode($frame, $node->forEachCollectionName); - $this->processKey($resolver, $node, $frame, $nodeContext->type()); - $this->processValue($resolver, $node, $frame, $nodeContext); - - $this->addAssignedVarsInCompoundStatement($node, $resolver, $frame); - - return $context; - } - - private function processValue(NodeContextResolver $resolver, ForeachStatement $node, Frame $frame, NodeContext $nodeContext): void - { - $itemName = $node->foreachValue; - - if (!$itemName instanceof ForeachValue) { - return; - } - - $expression = $itemName->expression; - if ($expression instanceof Variable) { - $this->valueFromVariable($expression, $node, $nodeContext, $frame); - return; - } - - if ($expression instanceof ArrayCreationExpression) { - $this->valueFromArrayCreation($resolver, $expression, $node, $nodeContext, $frame); - } - } - - private function processKey(NodeContextResolver $resolver, ForeachStatement $node, Frame $frame, Type $type): void - { - $itemName = $node->foreachKey; - - if (!$itemName instanceof ForeachKey) { - return; - } - - $expression = $itemName->expression; - if (!$expression instanceof Variable) { - return; - } - - $itemName = $expression->name->getText($node->getFileContents()); - - if (!is_string($itemName)) { - return; - } - - $context = NodeContextFactory::create( - $itemName, - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - ] - ); - if ($type instanceof IterableType) { - $context = $context->withType($this->resolveKeyType($type)); - } - - $frame->locals()->set(WorseVariable::fromSymbolContext($context)->asDefinition()); - } - - private function valueFromVariable(Variable $expression, ForeachStatement $node, NodeContext $nodeContext, Frame $frame): void - { - $itemName = $expression->getText(); - - if (!is_string($itemName)) { - return; - } - - $type = $nodeContext->type(); - - $context = NodeContextFactory::create( - $itemName, - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - ] - ); - - if ($type instanceof ReflectedClassType) { - $context = $context->withType($type->iterableValueType()); - } - if ($type instanceof IterableType) { - $context = $context->withType($this->resolveValueType($type)); - } - - $frame->locals()->set(WorseVariable::fromSymbolContext($context)->asDefinition()); - } - - private function valueFromArrayCreation( - NodeContextResolver $resolver, - ArrayCreationExpression $expression, - ForeachStatement $node, - NodeContext $nodeContext, - Frame $frame - ): void { - $elements = $expression->arrayElements; - if (!$elements instanceof ArrayElementList) { - return; - } - - $arrayType = $nodeContext->type(); - - if (!$arrayType instanceof IterableType) { - return; - } - - $index = 0; - - foreach ($elements->children as $item) { - if (!$item instanceof ArrayElement) { - continue; - } - - $context = $resolver->resolveNode($frame, $item->elementValue); - $context = $context->withType($this->resolveArrayCreationType($arrayType, $index)); - - $frame->locals()->set(WorseVariable::fromSymbolContext($context)->asAssignment()); - $index++; - } - } - - private function resolveArrayCreationType(IterableType $arrayType, int $index): Type - { - if ($arrayType instanceof ArrayLiteral) { - $possibleTypes = []; - foreach ($arrayType->iterableValueTypes() as $type) { - if ($type instanceof ArrayLiteral) { - $possibleTypes[] = $type->typeAtOffset($index); - } - } - - return (new UnionType(...$possibleTypes))->reduce(); - } - - if ($arrayType instanceof ArrayType) { - $value = $arrayType->iterableValueType(); - if ($value instanceof IterableType) { - return $value->iterableValueType(); - } - } - - return new MixedType(); - } - - private function resolveValueType(IterableType $type): Type - { - if ($type instanceof ArrayLiteral) { - return (new UnionType(...$type->iterableValueTypes())); - } - - return $type->iterableValueType(); - } - - private function resolveKeyType(IterableType $type): Type - { - if ($type instanceof ArrayLiteral) { - return (new UnionType(...$type->iterableKeyTypes())); - } - - return $type->iterableKeyType(); - } - - private function addAssignedVarsInCompoundStatement(ForeachStatement $node, NodeContextResolver $resolver, Frame $frame): void - { - $compoundStatement = $node->statements; - if ($compoundStatement instanceof CompoundStatementNode) { - foreach ($compoundStatement->statements as $statement) { - $resolver->resolveNode($frame, $statement); - } - foreach ($frame->locals()->greaterThan( - $compoundStatement->openBrace->getStartPosition() - )->lessThan( - $compoundStatement->closeBrace->getStartPosition() - ) as $local) { - if (!$local->wasAssigned()) { - continue; - } - if ($previous = $frame->locals()->byName($local->name())->lessThan($local->offset())->lastOrNull()) { - $type = $previous->type()->addType($local->type())->reduce(); - $frame->locals()->set( - $previous->withType($type)->withOffset($compoundStatement->closeBrace->getEndPosition())->asDefinition() - ); - } - } - } - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/FunctionDeclarationResolver.php b/lib/WorseReflection/Core/Inference/Resolver/FunctionDeclarationResolver.php deleted file mode 100644 index 7d4eca471e..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/FunctionDeclarationResolver.php +++ /dev/null @@ -1,28 +0,0 @@ -name->getText((string)$node->getFileContents()), - $node->name->getStartPosition(), - $node->name->getEndPosition(), - [ - 'symbol_type' => Symbol::FUNCTION, - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/GlobalDeclarationResolver.php b/lib/WorseReflection/Core/Inference/Resolver/GlobalDeclarationResolver.php deleted file mode 100644 index c0b15c5708..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/GlobalDeclarationResolver.php +++ /dev/null @@ -1,45 +0,0 @@ -variableNameList->getChildNodes() as $child) { - if (!$child instanceof Variable) { - continue; - } - $name = $child->getName(); - if (!$name) { - continue; - } - - $context = NodeContextFactory::create( - $name, - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => TypeFactory::mixed(), - ] - ); - $frame->locals()->set(PhpactorVariable::fromSymbolContext($context)->asAssignment()); - } - - return NodeContextFactory::forNode($node); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/IfStatementResolver.php b/lib/WorseReflection/Core/Inference/Resolver/IfStatementResolver.php deleted file mode 100644 index 8ea310385f..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/IfStatementResolver.php +++ /dev/null @@ -1,252 +0,0 @@ -expression) { - return $context; - } - - - if (!$node->expression instanceof Expression) { - return $context; - } - - // apply type assertions only within the if block - $context = $this->ifBranch( - $resolver, - $frame, - $node, - $node->getStartPosition(), - $this->resolveInitialEndPosition($node) - ); - - // apply type assertions only within the if block elseif clauses - foreach ($node->elseIfClauses as $clause) { - $this->ifBranch( - $resolver, - $frame, - $clause, - $clause->getStartPosition(), - $clause->getEndPosition(), - ); - } - - // evaluate the nodes in the else clause - if ($node->elseClause) { - foreach ($node->elseClause->getChildNodes() as $child) { - $resolver->resolveNode($frame, $child); - } - } - - $terminates = $this->branchTerminates($resolver, $frame, $node); - - if ($terminates) { - $this->unsetAssignedVariables($frame, $node); - } else { - // if the branch DOES terminate, then we retain the negated types - // else we do this and restore state frame to what it was before - // the if statement - foreach ($frame->locals()->lessThan($node->getStartPosition())->mostRecent() as $assignment) { - $frame->locals()->set($assignment->withOffset($node->getEndPosition())); - } - } - - // handle termination, negate the types if the branch terminates and - // add any new assignments as a union - $this->ifBranchTermination( - $resolver, - $frame, - $node, - $node->getStartPosition(), - $node->getEndPosition(), - $context, - ); - - // handle terminateion for elseif clauses - foreach ($node->elseIfClauses as $clause) { - $this->ifBranchTermination( - $resolver, - $frame, - $clause, - $node->getStartPosition(), - $clause->getEndPosition(), - ); - } - - // add any new assignments as unions to existing vars - if ($node->elseClause) { - $this->combineVariableAssignments($frame, $node->elseClause, $node->getEndPosition()); - } - - return $context; - } - - /** - * @param IfStatementNode|ElseIfClauseNode $node - */ - private function ifBranch( - NodeContextResolver $resolver, - Frame $frame, - $node, - int $start, - int $end - ): NodeContext { - $context = $resolver->resolveNode($frame, $node->expression); - - // apply type assertions from this - $frame->applyTypeAssertions($context->typeAssertions(), $start); - - foreach ($node->getChildNodes() as $child) { - $resolver->resolveNode($frame, $child); - } - - // but negate them after the block finishes (this applies for `else` - // conditions). - $frame->applyTypeAssertions( - $context->typeAssertions()->negate(), - $start, - createAtOffset: $end - ); - - return $context; - } - - /** - * @param IfStatementNode|ElseIfClauseNode $node - */ - private function ifBranchTermination( - NodeContextResolver $resolver, - Frame $frame, - $node, - int $start, - int $end, - ?NodeContext $context = null, - ): void { - $context = $context ?? $resolver->resolveNode($frame, $node->expression); - $terminates = $this->branchTerminates($resolver, $frame, $node); - - if ($node instanceof ElseIfClauseNode && $terminates) { - $frame->applyTypeAssertions($context->typeAssertions(), $start, $end); - return; - } - - if ($terminates) { - return; - } - - $this->combineVariableAssignments($frame, $node, $end); - } - - private function combineVariableAssignments(Frame $frame, Node $node, int $end): void - { - foreach ($frame->locals()->greaterThan($node->getStartPosition())->lessThan( - $node->getEndPosition() - )->mostRecent()->assignmentsOnly() as $assignment) { - $frame->locals()->add($assignment->withOffset($end), $node->getStartPosition()); - } - } - - /** - * @param IfStatementNode|ElseIfClauseNode $node - */ - private function branchTerminates(NodeContextResolver $resolver, Frame $frame, $node): bool - { - /** @phpstan-ignore-next-line lies */ - foreach ($node->statements as $list) { - /** @phpstan-ignore-next-line lies */ - if (null === $list) { - continue; - } - /** @phpstan-ignore-next-line lies */ - foreach ($list as $statement) { - if (!is_object($statement)) { - continue; - } - if ($statement instanceof ReturnStatement) { - return true; - } - - if ($statement instanceof ExpressionStatement) { - if ($statement->expression instanceof ThrowExpression) { - return true; - } - - if ($callExpression = $statement->getFirstDescendantNode(CallExpression::class)) { - $context = $resolver->resolveNode($frame, $callExpression); - - if ($context->type() instanceof NeverType) { - return true; - } - } - } - - if ($statement instanceof ThrowExpression) { - return true; - } - - if ($statement instanceof CompoundStatementNode) { - foreach ($statement->statements as $statement) { - if ($statement instanceof BreakOrContinueStatement) { - return true; - } - - if ($statement instanceof ExpressionStatement) { - if ($statement->expression instanceof ExitIntrinsicExpression) { - return true; - } - } - } - } - } - } - - return false; - } - - private function resolveInitialEndPosition(IfStatementNode $node): int - { - foreach ($node->elseIfClauses as $clause) { - return $clause->getStartPosition(); - } - - if ($node->elseClause) { - return $node->elseClause->getStartPosition(); - } - - return $node->getEndPosition(); - } - - private function unsetAssignedVariables(Frame $frame, IfStatementNode $node): void - { - foreach ($frame->locals()->greaterThanOrEqualTo($node->getStartPosition())->lessThan($node->getEndPosition())->assignmentsOnly()->mostRecent() as $innerVariable) { - foreach ($frame->locals()->byName($innerVariable->name())->lessThan($node->getStartPosition())->mostRecent() as $assignment) { - $frame->locals()->set($assignment->withOffset($node->getEndPosition())); - } - } - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/MemberAccess/MemberContextResolver.php b/lib/WorseReflection/Core/Inference/Resolver/MemberAccess/MemberContextResolver.php deleted file mode 100644 index 01510f49f7..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/MemberAccess/MemberContextResolver.php +++ /dev/null @@ -1,13 +0,0 @@ -memberName); - $memberTypeName = $node->getParent() instanceof CallExpression ? Symbol::METHOD : Symbol::PROPERTY; - - // support trait method-alias clauses, e.g. use use A, B {A::foobar insteadof B; B::bigTalk insteadof A;} - if ( - $memberTypeName === Symbol::PROPERTY && - $node->parent?->parent && - $node->parent->parent instanceof TraitSelectOrAliasClauseList - ) { - $memberTypeName = Symbol::METHOD; - } - - if ($node->memberName instanceof Node) { - $memberNameType = $resolver->resolveNode($frame, $node->memberName)->type(); - if ($memberNameType instanceof StringLiteralType) { - $memberName = $memberNameType->value; - } - } - - if ( - Symbol::PROPERTY === $memberTypeName - && $node instanceof ScopedPropertyAccessExpression - && is_string($memberName) - && !str_starts_with($memberName, '$') - ) { - $memberTypeName = Symbol::CONSTANT; - } - - $context = NodeContextFactory::create( - (string)$memberName, - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => $memberTypeName, - ] - ); - - if (Symbol::CONSTANT === $memberTypeName) { - if ($memberName === 'class') { - if (!$classType instanceof ClassType) { - return $context; - } - return $context->withType(TypeFactory::classString($classType->name()->full())); - } - - $constantAssignment = $node->getParent(); - // If you're trying to assign a constant to itself like "const T = self::T;" - if ($constantAssignment instanceof ConstElement && $constantAssignment->getName() === $memberName) { - return $context; - } - } - - [ $containerType, $memberType, $member, $arguments ] = $this->resolveContainerMemberType( - $resolver, - $frame, - $node, - $classType, - $memberTypeName, - $memberName - ); - - if (!$containerType->isDefined()) { - $containerType = $classType; - } - - if ($member instanceof ReflectionMember) { - if ($member instanceof ReflectionMethod) { - $methodCallContext = new MethodCallContext( - $context->symbol(), - $memberType->reduce(), - $containerType, - ByteOffsetRange::fromInts($node->memberName->getStartPosition(), $node->memberName->getEndPosition()), - $member, - $arguments, - ); - - if (NodeUtil::isFirstClassCallable($node->parent)) { - return $methodCallContext->withType(new ClosureType( - $resolver->reflector(), - $member->parameters()->types()->toArray(), - $member->type(), - )); - } - - return $methodCallContext; - } - return new MemberAccessContext( - $context->symbol(), - $memberType->reduce(), - $containerType, - ByteOffsetRange::fromInts($node->memberName->getStartPosition(), $node->memberName->getEndPosition()), - $member, - $arguments, - ); - } - - return $context->withContainerType( - $containerType - )->withType($memberType->reduce()); - } - - /** - * @return array{Type,Type,?ReflectionMember,?FunctionArguments} - */ - private function resolveContainerMemberType( - NodeContextResolver $resolver, - Frame $frame, - Node $node, - Type $classType, - string $memberTypeName, - string $memberName - ): array { - $types = []; - $memberType = TypeFactory::undefined(); - $member = null; - - $arguments = $this->resolveArguments($resolver, $frame, $node->parent); - // this could be a union or a nullable - foreach ($classType->expandTypes()->classLike() as $subType) { - // upcast to ClassType to reflected type - if (get_class($subType) === ClassType::class) { - /** @phpstan-ignore-next-line */ - $subType = $subType->asReflectedClasssType($resolver->reflector()); - } - - try { - $reflection = $resolver->reflector()->reflectClassLike($subType->name()); - } catch (NotFound) { - continue; - } - - $types[] = $subType; - - if ($reflection instanceof ReflectionEnum && $memberTypeName === ReflectionMember::TYPE_CONSTANT) { - foreach ($subType->members()->byMemberType(ReflectionMember::TYPE_CASE)->byName($memberName) as $member) { - // if multiple classes declare a member, always take the "top" one - $memberType = $this->resolveMemberType($resolver, $frame, $member, $arguments, $node, $subType); - break; - } - } - if ($reflection instanceof ReflectionEnum && $memberName === 'cases') { - $memberType = TypeFactory::array(TypeFactory::reflectedClass($resolver->reflector(), $reflection->name())); - break; - } - - foreach ($subType->members()->byMemberType($memberTypeName)->byName($memberName) as $member) { - // if multiple classes declare a member, always take the "top" one - $memberType = $this->resolveMemberType($resolver, $frame, $member, $arguments, $node, $subType); - break; - } - } - - if ($member instanceof ReflectionMethod && $arguments) { - $byReference = $member->parameters()->passedByReference(); - - if ($byReference->count()) { - foreach ($byReference as $parameter) { - $argument = $arguments->at($parameter->index()); - $frame->locals()->set(new Variable( - name: $argument->symbol()->name(), - offset: $argument->symbol()->position()->start()->toInt(), - type: $parameter->type(), - wasAssigned: false /** $wasAssigned bool */, - wasDefined: true /** $wasDefined bool */ - )); - } - } - } - - $containerType = UnionType::fromTypes(...$types)->reduce(); - return [$containerType, $memberType, $member, $arguments]; - } - - private function resolveMemberType(NodeContextResolver $resolver, Frame $frame, ReflectionMember $member, ?FunctionArguments $arguments, Node $node, Type $subType): Type - { - $inferredType = $member->inferredType(); - $declaringClass = self::declaringClass($member); - - if ($member instanceof ReflectionProperty) { - $propertyType = self::getFrameTypesForPropertyAtPosition( - $frame, - $member->name(), - $subType, - $node->getEndPosition(), - ); - if ($propertyType) { - $inferredType = $propertyType; - } - } - - if ($arguments && $member instanceof ReflectionMethod) { - try { - $declaringMember = $declaringClass->members()->byMemberType($member->memberType())->byName($member->name())->first(); - if ($declaringMember instanceof ReflectionMethod) { - $templateMap = $declaringMember->docblock()->templateMap(); - if (count($templateMap)) { - $inferredType = $this->combineMethodTemplateVars($arguments, $templateMap, $declaringMember, $inferredType); - } - } - } catch (NotFound) { - } - } - - if (count($declaringClass->docblock()->templateMap())) { - $templateMap = $this->resolver->resolveClassTemplateMap($subType, $declaringClass->name(), $subType instanceof GenericClassType ? $subType->arguments() : []); - $inferredType = $inferredType->map(function (Type $type) use ($templateMap): Type { - if ($templateMap && $templateMap->has($type->short())) { - return $templateMap->get($type->short()); - } - return $type; - }); - } - - // unwrap static and self types (including $this which extends Static) and any nested globbed constant unions - $inferredType = $inferredType->map(function (Type $type) { - if ($type instanceof StaticType || $type instanceof SelfType) { - return $type->type(); - } - if ($type instanceof GlobbedConstantUnionType) { - return $type->toUnion(); - } - return $type; - }); - - // expand globbed constants - if ($inferredType instanceof GlobbedConstantUnionType) { - $inferredType = $inferredType->toUnion(); - } - - foreach ($this->memberResolvers as $memberResolver) { - if (null !== $customType = $memberResolver->resolveMemberContext($resolver->reflector(), $member, $inferredType, $arguments)) { - $inferredType = $customType; - } - } - - return $inferredType; - } - - private static function getFrameTypesForPropertyAtPosition( - Frame $frame, - string $propertyName, - Type $classType, - int $position - ): ?Type { - if (!$classType instanceof ClassType) { - return null; - } - - $variable = $frame->properties() - ->byName($propertyName) - ->lessThanOrEqualTo($position) - ->lastOrNull(); - - if (null === $variable) { - return null; - } - - return $variable->type(); - } - - private static function declaringClass(ReflectionMember $member): ReflectionClassLike - { - $reflectionClass = $member->declaringClass(); - - if (!$reflectionClass instanceof ReflectionClass) { - return $reflectionClass; - } - - $interface = self::searchInterfaces($reflectionClass->interfaces(), $member->name()); - - if (!$interface) { - return $reflectionClass; - } - - return $interface; - } - - private static function searchInterfaces(ReflectionInterfaceCollection $collection, string $memberName): ?ReflectionInterface - { - foreach ($collection as $interface) { - if ($interface->methods()->has($memberName)) { - return $interface; - } - - if (null !== $interface = self::searchInterfaces($interface->parents(), $memberName)) { - return $interface; - } - } - - return null; - } - - private function resolveArguments(NodeContextResolver $resolver, Frame $frame, ?Node $node): ?FunctionArguments - { - if (!$node || !$node instanceof CallExpression) { - return null; - } - - return FunctionArguments::fromList($resolver, $frame, $node->argumentExpressionList); - } - - private function combineMethodTemplateVars(FunctionArguments $arguments, TemplateMap $templateMap, ReflectionMethod $member, Type $type): Type - { - $templateMap = $this->resolver->mergeParameters($templateMap, $member->parameters(), $arguments); - $type = $type->map(function (Type $type) use ($templateMap): Type { - return $templateMap->getOrGiven($type); - }); - - return $type; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/MemberAccessExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/MemberAccessExpressionResolver.php deleted file mode 100644 index 093dfaadec..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/MemberAccessExpressionResolver.php +++ /dev/null @@ -1,27 +0,0 @@ -resolveNode($frame, $node->dereferencableExpression); - - return $this->nodeContextFromMemberAccess->infoFromMemberAccess($resolver, $frame, $class->type(), $node); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/MethodDeclarationResolver.php b/lib/WorseReflection/Core/Inference/Resolver/MethodDeclarationResolver.php deleted file mode 100644 index 36dcf64c45..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/MethodDeclarationResolver.php +++ /dev/null @@ -1,40 +0,0 @@ -resolveNode($frame, $classNode); - - return new MemberDeclarationContext( - Symbol::fromTypeNameAndPosition( - Symbol::METHOD, - (string)$node->name->getText($node->getFileContents()), - ByteOffsetRange::fromInts( - $node->name->getStartPosition(), - $node->name->getEndPosition() - ) - ), - TypeFactory::unknown(), - $classSymbolContext->type() - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/NumericLiteralResolver.php b/lib/WorseReflection/Core/Inference/Resolver/NumericLiteralResolver.php deleted file mode 100644 index ee7e485e17..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/NumericLiteralResolver.php +++ /dev/null @@ -1,36 +0,0 @@ -getText()); - assert($type instanceof Literal); - return NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::NUMBER, - 'type' => $type, - 'container_type' => NodeUtil::nodeContainerClassLikeType($resolver->reflector(), $node), - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ObjectCreationExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ObjectCreationExpressionResolver.php deleted file mode 100644 index 8a998be51f..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ObjectCreationExpressionResolver.php +++ /dev/null @@ -1,77 +0,0 @@ -classTypeDesignator instanceof Node) { - throw new CouldNotResolveNode(sprintf('Could not create object from "%s"', get_class($node))); - } - - - $classContext = $resolver->resolveNode($frame, $node->classTypeDesignator); - $classType = $classContext->type(); - - if ($classType instanceof ClassStringType) { - if ($classType->className() === null) { - return $classContext->withType(TypeFactory::object()); - } - $classType = TypeFactory::class($classType->className()); - } - - if ($classType instanceof ClassType) { - return $classContext->withType($this->resolveClassType($resolver, $frame, $node, $classType)); - } - - - return $classContext; - } - - private function resolveClassType(NodeContextResolver $resolver, Frame $frame, ObjectCreationExpression $node, ClassType $classType): Type - { - try { - $reflection = $resolver->reflector()->reflectClass($classType->name()); - } catch (NotFound) { - return $classType; - } - if (!$reflection->methods()->has('__construct')) { - return $classType; - } - $templateMap = $reflection->docblock()->templateMap(); - - if (!count($templateMap)) { - return $classType; - } - - $arguments = FunctionArguments::fromList($resolver, $frame, $node->argumentExpressionList); - $templateMap = $this->resolver->mergeParameters( - $templateMap, - $reflection->methods()->get('__construct')->parameters(), - $arguments - ); - return new GenericClassType($resolver->reflector(), $classType->name(), $templateMap->toArguments()); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ParameterResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ParameterResolver.php deleted file mode 100644 index 16a3437b98..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ParameterResolver.php +++ /dev/null @@ -1,159 +0,0 @@ -getFirstAncestor( - ArrowFunctionCreationExpression::class, - AnonymousFunctionCreationExpression::class, - MethodDeclaration::class, - FunctionDeclaration::class - ); - - if ($method instanceof MethodDeclaration) { - return $this->resolveParameterFromMethodReflection($resolver->reflector(), $method, $node); - } - - if ($method instanceof FunctionDeclaration) { - return $this->resolveParameterFromFunctionReflection($resolver->reflector(), $method, $node); - } - - $typeDeclaration = $node->typeDeclarationList; - - $type = NodeUtil::typeFromQualfiedNameLike($resolver->reflector(), $node, $node->typeDeclarationList); - - if ($node->dotDotDotToken) { - $type = TypeFactory::array($type); - } - - if ($node->questionToken) { - $type = TypeFactory::nullable($type); - } - - return NodeContextFactory::create( - (string)$node->variableName->getText($node->getFileContents()), - $node->variableName->getStartPosition(), - $node->variableName->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => $type, - ] - ); - } - - private function resolveParameterFromFunctionReflection(Reflector $reflector, FunctionDeclaration $function, Parameter $node): NodeContext - { - $name = $function->getNamespacedName(); - - try { - $function = $reflector->reflectFunction($name->getFullyQualifiedNameText()); - } catch (NotFound $notFound) { - throw new CouldNotResolveNode(sprintf( - 'Function "%s" not found', - $name->getFullyQualifiedNameText() - ), 0, $notFound); - } - - try { - $parameter = $function->parameters()->get((string)$node->getName()); - } catch (NotFound $notFound) { - throw new CouldNotResolveNode(sprintf( - 'Parameter "%s" not found', - (string)$node->getName(), - ), 0, $notFound); - } - - return NodeContextFactory::create( - (string)$node->variableName->getText($node->getFileContents()), - $node->variableName->getStartPosition(), - $node->variableName->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => $parameter->inferredType(), - ] - ); - } - - private function resolveParameterFromMethodReflection(Reflector $reflector, MethodDeclaration $method, Parameter $node): NodeContext - { - $class = NodeUtil::nodeContainerClassLikeDeclaration($node); - - if (null === $class) { - throw new CouldNotResolveNode(sprintf( - 'Cannot find class context "%s" for parameter', - $node->getName() - )); - } - - try { - $reflectionClass = $reflector->reflectClassLike($class->getNamespacedName()->__toString()); - } catch (NotFound $notFound) { - throw new CouldNotResolveNode(sprintf( - 'Class "%s" not found', - $class->getNamespacedName()->__toString() - ), 0, $notFound); - } - - try { - $reflectionMethod = $reflectionClass->methods()->get($method->getName()); - } catch (ItemNotFound $notFound) { - throw new CouldNotResolveNode(sprintf( - 'Could not find method "%s" in class "%s"', - $method->getName(), - $reflectionClass->name()->__toString() - ), 0, $notFound); - } - - if (null === $node->getName()) { - throw new CouldNotResolveNode( - 'Node name for parameter resolved to NULL' - ); - } - - if (!$reflectionMethod->parameters()->has($node->getName())) { - throw new CouldNotResolveNode(sprintf( - 'Cannot find parameter "%s" for method "%s" in class "%s"', - $node->getName(), - $reflectionMethod->name(), - $reflectionClass->name() - )); - } - - $reflectionParameter = $reflectionMethod->parameters()->get($node->getName()); - - return NodeContextFactory::create( - (string)$node->variableName->getText($node->getFileContents()), - $node->variableName->getStartPosition(), - $node->variableName->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => $reflectionParameter->inferredType(), - 'container_type' => $reflectionClass->type(), - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ParenthesizedExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ParenthesizedExpressionResolver.php deleted file mode 100644 index a843810ef2..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ParenthesizedExpressionResolver.php +++ /dev/null @@ -1,19 +0,0 @@ -resolveNode($frame, $node->expression); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/PostfixUpdateExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/PostfixUpdateExpressionResolver.php deleted file mode 100644 index 5f35b111a9..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/PostfixUpdateExpressionResolver.php +++ /dev/null @@ -1,33 +0,0 @@ -resolveNode($frame, $node->operand); - $type = $variable->type(); - if ($type instanceof NumericType && $type instanceof Literal) { - $value = $type->value(); - if (TokenKind::PlusPlusToken === $node->incrementOrDecrementOperator->kind) { - return $variable->withType($type->withValue(++$value)); - } - if (TokenKind::MinusMinusToken === $node->incrementOrDecrementOperator->kind) { - return $variable->withType($type->withValue(--$value)); - } - } - return $variable; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/QualifiedNameListResolver.php b/lib/WorseReflection/Core/Inference/Resolver/QualifiedNameListResolver.php deleted file mode 100644 index 490c2e208f..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/QualifiedNameListResolver.php +++ /dev/null @@ -1,44 +0,0 @@ -getChildNodes() as $child) { - if (!$child instanceof QualifiedName) { - continue; - } - if (null === $firstType) { - $firstType = $child; - } - $types[] = $resolver->resolveNode($frame, $child)->type(); - } - - $type = new UnionType(...$types); - return NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'type' => $type->reduce(), - 'symbol_type' => Symbol::CLASS_, - ] - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/QualifiedNameResolver.php b/lib/WorseReflection/Core/Inference/Resolver/QualifiedNameResolver.php deleted file mode 100644 index 0bd42964ba..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/QualifiedNameResolver.php +++ /dev/null @@ -1,194 +0,0 @@ -parent; - if ($parent instanceof CallExpression) { - return $this->resolveContextFromCall($resolver, $frame, $parent, $node); - } - - - return $this->resolveContext($node); - } - - private function resolveContext(QualifiedName $node): NodeContext - { - $context = NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::CLASS_ - ] - ); - - $text = $node->getText(); - - // magic constants - if ($text === '__DIR__') { - // TODO: [TP] tolerant parser `getUri` returns NULL or string but only declares NULL - $uri = $node->getRoot()->uri; - if (!$uri) { - return $context->withType(TypeFactory::string()); - } - - return $context->withType(TypeFactory::stringLiteral(dirname($uri))); - } - - $type = $this->nodeTypeConverter->resolve($node); - - if ($type instanceof ReflectedClassType) { - try { - // fast but inaccurate check to see if class exists - $this->reflector->sourceCodeForClassLike($type->name()); - // accurate check to see if class exists - $class = $this->reflector->reflectClassLike($type->name()); - return new ClassLikeContext( - $context->symbol(), - ByteOffsetRange::fromInts($node->getStartPosition(), $node->getEndPosition()), - $class - ); - } catch (NotFound) { - // resolve the name of the potential constant - [$_, $_, $constImportTable] = $node->getImportTablesForCurrentScope(); - if ($resolved = NodeUtil::resolveNameFromImportTable($node, $constImportTable)) { - $name = $resolved->__toString(); - } else { - $name = $type->name()->full(); - } - try { - // fast but inaccurate check to see if constant exists - $sourceCode = $this->reflector->sourceCodeForConstant($name); - // accurate check to see if constant exists - $constant = $this->reflector->reflectConstant($name); - return $context - ->withSymbolName($constant->name()->full()) - ->withType($constant->type()) - ->withSymbolType(Symbol::DECLARED_CONSTANT); - } catch (NotFound) { - } - } - } - - - return $context->withType($type); - } - - private function resolveContextFromCall( - NodeContextResolver $resolver, - Frame $frame, - CallExpression $parent, - QualifiedName $node - ): NodeContext { - $name = $node->getResolvedName(); - - if (null === $name) { - $name = $node->getNamespacedName(); - } - - $name = Name::fromString((string) $name); - $range = ByteOffsetRange::fromInts( - $node->getStartPosition(), - $node->getEndPosition(), - ); - - try { - $function = $this->reflector->reflectFunction($name); - } catch (NotFound $exception) { - // create dummy function - $function = VirtualReflectionFunction::empty($name, $range); - } - - - if (NodeUtil::isFirstClassCallable($parent)) { - $context = NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - ['symbol_type' => Symbol::FUNCTION], - ); - - return $context->withType(new ClosureType( - $resolver->reflector(), - $function->parameters()->types()->toArray(), - $function->type() - )); - } - - $arguments = FunctionArguments::fromList($resolver, $frame, $parent->argumentExpressionList); - $context = FunctionCallContext::create($name, $range, $function, $arguments); - - $byReference = $function->parameters()->passedByReference(); - $arguments = null; - - if ($byReference->count()) { - $arguments = FunctionArguments::fromList( - $resolver, - $frame, - $parent->argumentExpressionList - ); - foreach ($byReference as $parameter) { - $argument = $arguments->at($parameter->index()); - $frame->locals()->set(new Variable( - name: $argument->symbol()->name(), - offset: $argument->symbol()->position()->start()->toInt(), - type: $parameter->type(), - wasAssigned: false /** $wasAssigned bool */, - wasDefined: true /** $wasDefined bool */ - )); - } - } - - $stub = $this->registry->get($name->short()); - if ($stub) { - $arguments = $arguments ?: FunctionArguments::fromList( - $resolver, - $frame, - $parent->argumentExpressionList - ); - return $stub->resolve($frame, $context, $arguments); - } - - // the function may have been resolved to a global, so create - // the context again with the potentially shorter name - return $context->withSymbolName($function->name()->__toString()); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ReservedWordResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ReservedWordResolver.php deleted file mode 100644 index 785192aec0..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ReservedWordResolver.php +++ /dev/null @@ -1,65 +0,0 @@ -getText()); - - if ('null' === $word) { - $type = TypeFactory::null(); - $symbolType = Symbol::UNKNOWN; - $containerType = NodeUtil::nodeContainerClassLikeType($resolver->reflector(), $node); - } - - if ('false' === $word) { - $value = false; - $type = TypeFactory::boolLiteral($value); - $symbolType = Symbol::BOOLEAN; - $containerType = NodeUtil::nodeContainerClassLikeType($resolver->reflector(), $node); - } - - if ('true' === $word) { - $value = true; - $type = TypeFactory::boolLiteral($value); - $symbolType = Symbol::BOOLEAN; - $containerType = NodeUtil::nodeContainerClassLikeType($resolver->reflector(), $node); - } - - $info = NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'type' => $type, - 'symbol_type' => $symbolType === null ? Symbol::UNKNOWN : $symbolType, - 'container_type' => $containerType, - ] - ); - - if (null === $symbolType) { - $info = $info->withIssue(sprintf('Could not resolve reserved word "%s"', $node->getText())); - } - - if (null === $type) { - $info = $info->withIssue(sprintf('Could not resolve reserved word "%s"', $node->getText())); - } - - return $info; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ReturnStatementResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ReturnStatementResolver.php deleted file mode 100644 index 5162ce0fdb..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ReturnStatementResolver.php +++ /dev/null @@ -1,36 +0,0 @@ -expression) { - return $context; - } - - $type = $resolver->resolveNode($frame, $node->expression)->type(); - $context = $context->withType($type); - - if ($frame->returnType()->isVoid()) { - $frame->setReturnType($type); - return $context; - } - - $frame->setReturnType($frame->returnType()->addType($type)); - - return $context; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/ScopedPropertyAccessResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ScopedPropertyAccessResolver.php deleted file mode 100644 index 8c6a0f9756..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/ScopedPropertyAccessResolver.php +++ /dev/null @@ -1,52 +0,0 @@ -scopeResolutionQualifier instanceof Variable) { - $context = $resolver->resolveNode( - $frame, - $node->scopeResolutionQualifier - ); - $type = $context->type(); - if ($type instanceof ClassType) { - $name = $type->name->__toString(); - } - } - - if (empty($name)) { - $name = $node->scopeResolutionQualifier->getText(); - } - - $classType = $resolver->resolveNode($frame, $node->scopeResolutionQualifier)->type(); - - if ($classType instanceof ClassStringType && $classType->className()) { - $classType = TypeFactory::reflectedClass($resolver->reflector(), $classType->className()); - } - - return $this->nodeContextFromMemberAccess->infoFromMemberAccess($resolver, $frame, $classType, $node); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/SourceFileNodeResolver.php b/lib/WorseReflection/Core/Inference/Resolver/SourceFileNodeResolver.php deleted file mode 100644 index b87aa9a48b..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/SourceFileNodeResolver.php +++ /dev/null @@ -1,17 +0,0 @@ -variableName); - - if (!$name) { - return NodeContextFactory::forNode($node); - } - - $context = NodeContextFactory::create( - $name, - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => TypeFactory::mixed(), - ] - ); - $frame->locals()->set(PhpactorVariable::fromSymbolContext($context)->asAssignment()); - - return NodeContextFactory::forNode($node); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/StringLiteralResolver.php b/lib/WorseReflection/Core/Inference/Resolver/StringLiteralResolver.php deleted file mode 100644 index 0299c31f6c..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/StringLiteralResolver.php +++ /dev/null @@ -1,57 +0,0 @@ -getStringContentsText($node); - return NodeContextFactory::create( - 'string', - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::STRING, - 'type' => TypeFactory::stringLiteral($value), - 'container_type' => NodeUtil::nodeContainerClassLikeType($resolver->reflector(), $node), - ] - ); - } - - private function getStringContentsText(StringLiteral $node): string - { - $children = $node->children; - if (is_array($children) && array_key_exists(0, $children)) { - $children = $children[0]; - } - - if ($children instanceof Token) { - $value = (string)$children->getText($node->getFileContents()); - $startQuote = substr($node, 0, 1); - - return match ($startQuote) { - '\'' => rtrim(substr($value, 1), '\''), - '"' => rtrim(substr($value, 1), '"'), - '<' => trim($value), - default => '' - }; - } - - return ''; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/SubscriptExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/SubscriptExpressionResolver.php deleted file mode 100644 index 1ed3a3732b..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/SubscriptExpressionResolver.php +++ /dev/null @@ -1,71 +0,0 @@ -resolveNode($frame, $node->postfixExpression); - - if (null === $node->accessExpression) { - $info = $info->withIssue(sprintf( - 'Subscript expression "%s" is incomplete', - (string) $node->getText() - )); - return $info; - } - - $node = $node->accessExpression; - $type = $info->type(); - - if (!$type instanceof ArrayType) { - $info = $info->withIssue(sprintf( - 'Not resolving subscript expression of type "%s"', - (string) $info->type() - )); - return $info; - } - - $arrayLiteralType = $info->type(); - $info = $info->withType($type->iterableValueType()); - - if (!$arrayLiteralType instanceof ArrayAccessType) { - $info = $info->withIssue(sprintf( - 'Array value for symbol "%s" is not an array, is a "%s"', - (string) $info->symbol(), - $arrayLiteralType->__toString() - )); - - return $info; - } - - if ($node instanceof StringLiteral) { - $string = $resolver->resolveNode($frame, $node); - - $type = $arrayLiteralType->typeAtOffset(TypeUtil::valueOrNull($string->type())); - if (($type->isDefined())) { - return $string->withType($type); - } - } - - $info = $info->withIssue(sprintf( - 'Did not resolve access expression for node type "%s"', - get_class($node) - )); - - return $info; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/TernaryExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/TernaryExpressionResolver.php deleted file mode 100644 index 6dd0da26b3..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/TernaryExpressionResolver.php +++ /dev/null @@ -1,54 +0,0 @@ -resolveNode($frame, $node->condition); - $context = NodeContextFactory::create('trinary', $node->getStartPosition(), $node->getEndPosition()); - $left = NodeContext::none(); - $right = NodeContext::none(); - - - /** @phpstan-ignore-next-line */ - if ($node->ifExpression) { - $frame->applyTypeAssertions($condition->typeAssertions(), $node->ifExpression->getStartPosition()); - $left = $resolver->resolveNode($frame, $node->ifExpression); - } - - /** @phpstan-ignore-next-line */ - if (!$node->ifExpression) { - $left = $condition; - } - - /** @phpstan-ignore-next-line */ - if ($node->elseExpression) { - $frame->applyTypeAssertions($condition->typeAssertions()->negate(), $node->elseExpression->getStartPosition()); - $right = $resolver->resolveNode($frame, $node->elseExpression); - } - - $empty = $condition->type()->isEmpty(); - - if ($empty->isFalse()) { - return $context->withType($left->type()); - } - - if ($empty->isTrue()) { - return $context->withType($right->type()); - } - - return $context->withType($left->type()->addType($right->type())); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/UnaryOpExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/UnaryOpExpressionResolver.php deleted file mode 100644 index bf47be4ad0..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/UnaryOpExpressionResolver.php +++ /dev/null @@ -1,81 +0,0 @@ -resolveNode($frame, $node->operand); - - // see sister hack in BinaryExpressionResolver - // https://github.com/Microsoft/tolerant-php-parser/issues/19 - $doubleNegate = $this->shouldDoubleNegate($node); - - $context = NodeContextFactory::create( - $node->getText(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - ] - )->withTypeAssertions( - $operand->typeAssertions() - )->withType($operand->type()); - $operatorKind = NodeUtil::operatorKindForUnaryExpression($node); - - return $this->resolveType($context, $operatorKind, $operand->type(), $doubleNegate); - } - - private function resolveType(NodeContext $context, int $operatorKind, Type $type, bool $doubleNegate): NodeContext - { - switch ($operatorKind) { - case TokenKind::ExclamationToken: - $context = $context->withType( - TypeUtil::toBool($context->type())->negate() - ); - if ($doubleNegate) { - return $context; - } - return $context->negateTypeAssertions(); - case TokenKind::PlusToken: - return $context->withType(TypeUtil::toNumber($type)->identity()); - case TokenKind::MinusToken: - return $context->withType(TypeUtil::toNumber($type)->negative()); - case TokenKind::TildeToken: - if ($type instanceof BitwiseOperable) { - return $context->withType($type->bitwiseNot()); - } - } - - return $context; - } - - private function shouldDoubleNegate(UnaryExpression $node): bool - { - if (!$node->operand instanceof BinaryExpression) { - return false; - } - - if (!$node->operand->leftOperand instanceof UnaryExpression) { - return false; - } - - $operatorKind = NodeUtil::operatorKindForUnaryExpression($node->operand->leftOperand); - return $operatorKind === TokenKind::ExclamationToken; - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/UseVariableNameResolver.php b/lib/WorseReflection/Core/Inference/Resolver/UseVariableNameResolver.php deleted file mode 100644 index f7b864d18f..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/UseVariableNameResolver.php +++ /dev/null @@ -1,27 +0,0 @@ -getName(); - - return NodeContextFactory::forVariableAt( - $frame, - $node->getStartPosition(), - $node->getEndPosition(), - $name - ); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/VariableResolver.php b/lib/WorseReflection/Core/Inference/Resolver/VariableResolver.php deleted file mode 100644 index 207081c391..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/VariableResolver.php +++ /dev/null @@ -1,153 +0,0 @@ -getFirstAncestor(PropertyHook::class, PropertyDeclaration::class)) { - if ($ancestor instanceof PropertyDeclaration) { - return $this->resolvePropertyVariable($resolver, $node); - } - } - - if ($node->name instanceof BracedExpression) { - return $resolver->resolveNode($frame, $node->name->expression); - } - - $parent = $node->parent; - - // given `$foo::$bar` we check that we are resolving `$bar` and not - // `$foo` which both have scoped-property-access as a parent, avoiding - // an infinite loop. - if ( - $parent instanceof ScopedPropertyAccessExpression && - $parent->memberName === $node - ) { - $containerType = $resolver->resolveNode($frame, $parent->scopeResolutionQualifier); - $access = $this->resolveStaticPropertyAccess($resolver, $containerType->type(), $node); - return $access; - } - - $variableName = $node->getText(); - $variables = $frame->locals()->byName($variableName); - - // special handling for assignments - if ($assignment = $node->getFirstAncestor(AssignmentExpression::class)) { - assert($assignment instanceof AssignmentExpression); - // if we are dealing with the right hand side of the assignment - if ($assignment->leftOperand !== $node) { - // do not consider the variable being assigned to - $variables = $variables->not($assignment->getStartPosition()); - } - } - - $frameVariable = $variables->lessThanOrEqualTo($node->getStartPosition())->lastOrNull(); - - $type = new MissingType(); - if ($frameVariable) { - $type = $frameVariable->type(); - } - - $context = NodeContextFactory::forVariableAt( - $frame, - $node->getStartPosition(), - $node->getEndPosition(), - $variableName - )->withTypeAssertion(TypeAssertion::variable( - $variableName, - $node->getStartPosition(), - function (Type $type) { - return TypeCombinator::subtract(TypeFactory::unionEmpty(), $type); - }, - fn (Type $type) => TypeCombinator::intersection(TypeFactory::unionEmpty(), $type), - ))->withType($type); - - $varDocType = $frame->varDocBuffer()->yank($variableName); - - if (null !== $varDocType) { - $context = $context->withType($varDocType); - $this->applyVarDoc($context, $frame, $varDocType); - } - - return $context; - } - - private function resolvePropertyVariable(NodeContextResolver $resolver, Variable $node): NodeContext - { - if (null === $node->getName()) { - return NodeContext::none(); - } - - $context = NodeContextFactory::create( - $node->getName(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::PROPERTY, - ] - ); - - $context = (new MemberTypeResolver($resolver->reflector()))->propertyType( - NodeUtil::nodeContainerClassLikeType($resolver->reflector(), $node), - $context, - $context->symbol()->name() - ); - - return new MemberDeclarationContext($context->symbol(), $context->type(), $context->containerType()); - } - - private function resolveStaticPropertyAccess(NodeContextResolver $resolver, Type $containerType, Variable $node): NodeContext - { - $info = NodeContextFactory::create( - (string)$node->getName(), - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'symbol_type' => Symbol::PROPERTY, - ] - ); - - return (new MemberTypeResolver($resolver->reflector()))->propertyType( - $containerType, - $info, - $info->symbol()->name() - ); - } - - private function applyVarDoc(NodeContext $context, Frame $frame, Type $varDocType): void - { - foreach ($frame->locals()->byName($context->symbol()->name())->equalTo($context->symbol()->position()->start()->toInt()) as $existing) { - assert($existing instanceof PhpactorVariable); - $frame->locals()->replace($existing, $existing->withType($context->type())->asDefinition()); - return; - } - $frame->locals()->set(PhpactorVariable::fromSymbolContext($context)->asDefinition()); - } -} diff --git a/lib/WorseReflection/Core/Inference/Resolver/YieldExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/YieldExpressionResolver.php deleted file mode 100644 index bc99f144e3..0000000000 --- a/lib/WorseReflection/Core/Inference/Resolver/YieldExpressionResolver.php +++ /dev/null @@ -1,77 +0,0 @@ -arrayElement; - /** @var Token */ - $from = $node->yieldOrYieldFromKeyword; - $yieldFrom = $from->kind === TokenKind::YieldFromKeyword; - $returnType = $frame->returnType(); - - if (!$arrayElement) { - return $context; - } - - $key = new MissingType(); - if ($arrayElement->elementKey) { - $key = $resolver->resolveNode($frame, $arrayElement->elementKey)->type(); - } - $value = new MissingType(); - /** @phpstan-ignore-next-line No trust */ - if ($arrayElement->elementValue) { - $value = $resolver->resolveNode($frame, $arrayElement->elementValue)->type(); - - if ($yieldFrom) { - $frame->setReturnType($value); - return $context; - } - - // treat yield values as a seies of array shapes - if ($value instanceof ArrayLiteral) { - $value = $value->toShape(); - } - } - - if ($returnType->isDefined() && $returnType instanceof GeneratorType) { - if ($value->isDefined()) { - $returnType = $returnType->withValue($returnType->valueType()->addType($value)); - } - if ($key->isDefined()) { - $returnType = $returnType->withKey($returnType->keyType()->addType($key)); - } - - $frame->setReturnType($returnType); - return $context; - } - - $frame->setReturnType( - TypeFactory::generator( - $resolver->reflector(), - $key, - $value, - ) - ); - return $context; - } -} diff --git a/lib/WorseReflection/Core/Inference/SuperGlobals.php b/lib/WorseReflection/Core/Inference/SuperGlobals.php deleted file mode 100644 index bf3b05efbf..0000000000 --- a/lib/WorseReflection/Core/Inference/SuperGlobals.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ - public static function list(): array - { - return [ - 'GLOBALS' => TypeFactory::array(), - '_SERVER' => TypeFactory::array(), - '_GET' => TypeFactory::array(), - '_POST' => TypeFactory::array(), - '_FILES' => TypeFactory::array(), - '_COOKIE' => TypeFactory::array(), - '_SESSION' => TypeFactory::array(), - '_REQUEST' => TypeFactory::array(), - '_ENV' => TypeFactory::array(), - - 'argc' => TypeFactory::int(), - 'argv' => TypeFactory::array(TypeFactory::string()), - ]; - } -} diff --git a/lib/WorseReflection/Core/Inference/Symbol.php b/lib/WorseReflection/Core/Inference/Symbol.php deleted file mode 100644 index 9a3024d41a..0000000000 --- a/lib/WorseReflection/Core/Inference/Symbol.php +++ /dev/null @@ -1,126 +0,0 @@ -'; - - private string $name; - - /** - * @param Symbol::* $symbolType - */ - private function __construct( - private string $symbolType, - string $name, - private ByteOffsetRange $position - ) { - $this->name = ltrim($name, '$'); - } - - public function __toString() - { - return sprintf('%s:%s [%s] %s', $this->position->start()->toInt(), $this->position->end()->toInt(), $this->symbolType, $this->name); - } - - public static function unknown(): Symbol - { - return new self(self::UNKNOWN, self::UNKNOWN, ByteOffsetRange::fromInts(0, 0)); - } - - public function isKnown(): bool - { - return $this->symbolType !== self::UNKNOWN; - } - - /** - * @return self::* - */ - public static function castSymbolType(string $symbolType): string - { - if (false === in_array($symbolType, self::validSymbols())) { - throw new InvalidArgumentException(sprintf( - 'Invalid symbol type "%s", valid symbol names: "%s"', - $symbolType, - implode('", "', self::validSymbols()) - )); - } - - /** @phpstan-ignore-next-line */ - return $symbolType; - } - - public static function fromTypeNameAndPosition(string $symbolType, string $name, ByteOffsetRange $position): Symbol - { - $symbolType = self::castSymbolType($symbolType); - return new self($symbolType, $name, $position); - } - - /** - * @return Symbol::* - */ - public function symbolType(): string - { - return $this->symbolType; - } - - public function name(): string - { - return $this->name; - } - - public function position(): ByteOffsetRange - { - return $this->position; - } - - /** - * @param self::* $symbolType - */ - public function withSymbolType(string $symbolType): self - { - return new self($symbolType, $this->name, $this->position); - } - - public function withSymbolName(string $symbolName): self - { - return new self($this->symbolType, $symbolName, $this->position); - } - - /** - * @return array - */ - private static function validSymbols(): array - { - return [ - self::CLASS_, - self::VARIABLE, - self::UNKNOWN, - self::PROPERTY, - self::CONSTANT, - self::FUNCTION, - self::METHOD, - self::STRING, - self::NUMBER, - self::BOOLEAN, - self::ARRAY, - self::CASE, - ]; - } -} diff --git a/lib/WorseReflection/Core/Inference/TypeAssertion.php b/lib/WorseReflection/Core/Inference/TypeAssertion.php deleted file mode 100644 index 7cfcdd0656..0000000000 --- a/lib/WorseReflection/Core/Inference/TypeAssertion.php +++ /dev/null @@ -1,123 +0,0 @@ -name = ltrim($name, '$'); - $this->true = $true; - $this->false = $false; - } - - public function __toString() - { - return sprintf( - '%s: %s#%s %s', - $this->variableType(), - $this->name(), - $this->offset(), - $this->polarity() ? 'positive' : 'negative', - ); - } - - public static function variable(string $name, int $offset, Closure $true, Closure $false): self - { - return new self(self::VARIABLE_TYPE_VARIABLE, $name, $offset, $true, $false, null); - } - - public static function property(string $name, int $offset, Closure $true, Closure $false, Type $classType): self - { - return new self(self::VARIABLE_TYPE_PROPERTY, $name, $offset, $true, $false, $classType); - } - - public static function forContext(NodeContext $context, Closure $true, Closure $false): self - { - if ($context->symbol()->symbolType() === Symbol::PROPERTY) { - return TypeAssertion::property( - $context->symbol()->name(), - $context->symbol()->position()->start()->toInt(), - $true, - $false, - $context->containerType(), - ); - } - - if ($context->symbol()->symbolType() === Symbol::VARIABLE) { - return TypeAssertion::variable($context->symbol()->name(), $context->symbol()->position()->start()->toInt(), $true, $false); - } - - throw new RuntimeException(sprintf( - 'Do not know how to create type assertion for symbol type: "%s"', - $context->type()->__toString() - )); - } - - public function name(): string - { - return $this->name; - } - - public function apply(Type $type): Type - { - if ($this->polarity === true) { - $true = $this->true; - return $true($type); - } - - $false = $this->false; - return $false($type); - } - - public function variableType(): string - { - return $this->variableType; - } - - public function classType(): Type - { - return $this->classType ?: new MissingType(); - } - - public function negate(): TypeAssertion - { - $this->polarity = !$this->polarity; - return $this; - } - - public function offset(): int - { - return $this->offset; - } - - public function polarity(): bool - { - return $this->polarity; - } -} diff --git a/lib/WorseReflection/Core/Inference/TypeAssertions.php b/lib/WorseReflection/Core/Inference/TypeAssertions.php deleted file mode 100644 index 8701fe7af2..0000000000 --- a/lib/WorseReflection/Core/Inference/TypeAssertions.php +++ /dev/null @@ -1,175 +0,0 @@ - - */ -final class TypeAssertions implements IteratorAggregate -{ - /** - * @var TypeAssertion[] - */ - private array $typeAssertions = []; - - /** - * @param TypeAssertion[] $typeAssertions - */ - public function __construct(array $typeAssertions) - { - foreach ($typeAssertions as $assertion) { - $key = $this->key($assertion); - $this->typeAssertions[$key] = $assertion; - } - } - - public function __toString(): string - { - return implode("\n", array_map(function (TypeAssertion $typeAssertion) { - return $typeAssertion->__toString(); - }, $this->typeAssertions)); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->typeAssertions); - } - - public function add(TypeAssertion $typeAssertion): self - { - $assertions = $this->typeAssertions; - $assertions[] = $typeAssertion; - return new self($assertions); - } - - public function variables(): self - { - return new self(array_filter($this->typeAssertions, function (TypeAssertion $typeAssertion) { - return $typeAssertion->variableType() === TypeAssertion::VARIABLE_TYPE_VARIABLE; - })); - } - - public function properties(): self - { - return new self(array_filter($this->typeAssertions, function (TypeAssertion $typeAssertion) { - return $typeAssertion->variableType() === TypeAssertion::VARIABLE_TYPE_PROPERTY; - })); - } - - public function negate(): self - { - return $this->map(function (TypeAssertion $assertion) { - $assertion->negate(); - return $assertion; - }); - } - - public function map(Closure $closure): self - { - return new self(array_map($closure, $this->typeAssertions)); - } - - public function merge(TypeAssertions $typeAssertions): self - { - $assertions = $this->typeAssertions; - foreach ($typeAssertions as $key => $assertion) { - $assertions[$key] = $assertion; - } - - return new self($assertions); - } - - /** - * Combine incoming type assertions with logical OR (union) - * - * is_string($foobar) => string - * || - * is_bool($foobar) => string|bool - */ - public function or(TypeAssertions $typeAssertions): self - { - return $this->aggregate( - $typeAssertions, - function (Type $type, TypeAssertion $left, TypeAssertion $right) { - return UnionType::fromTypes($left->apply($type), $right->apply($type)); - }, - function (Type $type, TypeAssertion $left, TypeAssertion $right) { - return UnionType::fromTypes($left->negate()->apply($type), $right->negate()->apply($type)); - } - ); - } - - /** - * Combine incoming type assertions with logical AND (intersection) - * - * $foobar instanceof A => A - * && - * $foobar instanceof B => A&B - */ - public function and(TypeAssertions $typeAssertions): self - { - return $this->aggregate( - $typeAssertions, - function (Type $type, TypeAssertion $left, TypeAssertion $right) { - return $right->apply($left->apply($type)); - }, - function (Type $type, TypeAssertion $left, TypeAssertion $right) { - return UnionType::fromTypes($left->negate()->apply($type), $right->negate()->apply($type)); - } - ); - } - - public function firstForName(string $name): TypeAssertion - { - foreach ($this->typeAssertions as $assertion) { - if ($assertion->name() === $name) { - return $assertion; - } - } - - throw new RuntimeException(sprintf( - 'Type assertion collection has no assertion for name "%s"', - $name - )); - } - - private function aggregate(TypeAssertions $typeAssertions, Closure $true, Closure $false): self - { - $resolved = []; - foreach ($this->typeAssertions as $typeAssertion) { - $resolved[$typeAssertion->name()] = $typeAssertion; - } - - foreach ($typeAssertions as $typeAssertion) { - if (!isset($resolved[$typeAssertion->name()])) { - $resolved[$typeAssertion->name()] = $typeAssertion; - continue; - } - - $left = $resolved[$typeAssertion->name()]; - $right = $typeAssertion; - $resolved[$typeAssertion->name()] = TypeAssertion::variable( - $typeAssertion->name(), - $typeAssertion->offset(), - fn (Type $type) => $true($type, $left, $right), - fn (Type $type) => $false($type, $left, $right), - ); - } - - return new self($resolved); - } - - private function key(TypeAssertion $assertion): string - { - $key = $assertion->variableType().$assertion->name().$assertion->offset(); - return $key; - } -} diff --git a/lib/WorseReflection/Core/Inference/TypeCombinator.php b/lib/WorseReflection/Core/Inference/TypeCombinator.php deleted file mode 100644 index 907b059edc..0000000000 --- a/lib/WorseReflection/Core/Inference/TypeCombinator.php +++ /dev/null @@ -1,97 +0,0 @@ -remove($type); - } - - public static function narrowTo(Type $type, Type $narrowTo): Type - { - $type = $type->reduce(); - $narrowTo = $narrowTo->reduce(); - - if ($type instanceof IntersectionType) { - return $type->add($narrowTo); - } - - $resolved = []; - $types = UnionType::toUnion($type); - $asIntersection = !$types->contains($narrowTo) && $narrowTo instanceof ClassType; - - foreach ($types->types as $type) { - if ($type->accepts($narrowTo)->isTrue()) { - $resolved[] = $narrowTo; - continue; - } - - if ($asIntersection) { - $resolved[] = TypeFactory::intersection($type, $narrowTo)->clean(); - } - } - - $t= TypeFactory::union(...$resolved)->reduce(); - - return $t; - } - - - public static function subtract(Type $type, Type $from): Type - { - $from = TypeFactory::toAggregateOrUnion($from); - $type = TypeFactory::toAggregateOrUnion($type); - - $f = $from->withTypes(...array_filter($from->types, function (Type $t) use ($type) { - foreach ($type->types as $subtract) { - if ($t->__toString() === $subtract->__toString()) { - return false; - } - } - return true; - }))->reduce(); - return $f; - } - - /** - * Return only those types in type2 that are in type1 - */ - public static function intersection(Type $type1, Type $type2): Type - { - $type1 = UnionType::toUnion($type1); - $type2 = UnionType::toUnion($type2); - - return TypeFactory::union(...array_filter($type2->types, function (Type $t) use ($type1) { - foreach ($type1->types as $subtract) { - if ($t->__toString() === $subtract->__toString()) { - return true; - } - } - return false; - }))->reduce(); - } - - public static function acceptedByType(Type $type, Type $acceptingType): Type - { - $type = UnionType::toUnion($type); - $types = []; - foreach ($type->clean()->types as $type) { - if (!$acceptingType->accepts($type)->isTrue()) { - continue; - } - $types[] = $type; - } - - return (new UnionType(...$types))->reduce(); - } -} diff --git a/lib/WorseReflection/Core/Inference/TypeMap.php b/lib/WorseReflection/Core/Inference/TypeMap.php deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/WorseReflection/Core/Inference/VarDocBuffer.php b/lib/WorseReflection/Core/Inference/VarDocBuffer.php deleted file mode 100644 index c923f936c4..0000000000 --- a/lib/WorseReflection/Core/Inference/VarDocBuffer.php +++ /dev/null @@ -1,38 +0,0 @@ - - */ - private array $buffer = []; - - private int $version = 0; - - public function set(string $name, Type $type): void - { - $this->version++; - $this->buffer[$name] = $type; - } - - public function yank(string $name): ?Type - { - if (!isset($this->buffer[$name])) { - return null; - } - $type = $this->buffer[$name]; - - unset($this->buffer[$name]); - - return $type; - } - - public function version(): int - { - return $this->version; - } -} diff --git a/lib/WorseReflection/Core/Inference/Variable.php b/lib/WorseReflection/Core/Inference/Variable.php deleted file mode 100644 index 651be57250..0000000000 --- a/lib/WorseReflection/Core/Inference/Variable.php +++ /dev/null @@ -1,111 +0,0 @@ -name = ltrim($name, '$'); - } - - public function __toString(): string - { - return sprintf( - '%s#%s %s %s%s', - $this->name, - $this->offset, - $this->type, - $this->classType ? $this->classType->__toString() : '', - $this->wasDefined ? ' (definition)' : '', - ); - } - - public static function fromSymbolContext(NodeContext $nodeContext): Variable - { - return new self( - $nodeContext->symbol()->name(), - $nodeContext->symbol()->position()->start()->toInt(), - $nodeContext->type(), - $nodeContext->symbol()->symbolType() === Symbol::PROPERTY ? $nodeContext->containerType() : null - ); - } - - public function name(): string - { - return $this->name; - } - - public function isNamed(string $name): bool - { - $name = ltrim($name, '$'); - - return $this->name == $name; - } - - public function withType(Type $type): self - { - return new self($this->name, $this->offset, $type, $this->classType, $this->wasAssigned, $this->wasDefined); - } - - public function withOffset(int $offset): self - { - return new self($this->name, $offset, $this->type, $this->classType, $this->wasAssigned, $this->wasDefined); - } - - public function asAssignment(): self - { - return new self($this->name, $this->offset, $this->type, $this->classType, true, true); - } - - public function asDefinition(): self - { - return new self($this->name, $this->offset, $this->type, $this->classType, false, true); - } - - public function type(): Type - { - return $this->type; - } - - public function isProperty(): bool - { - return null !== $this->classType; - } - - public function classType(): Type - { - return $this->classType ?: new MissingType(); - } - - public function offset(): int - { - return $this->offset; - } - - public function wasAssigned(): bool - { - return $this->wasAssigned; - } - - public function wasDefinition(): bool - { - return $this->wasDefined; - } - - public function key(): string - { - return sprintf('%s-%s', $this->name(), $this->offset()); - } -} diff --git a/lib/WorseReflection/Core/Inference/Walker.php b/lib/WorseReflection/Core/Inference/Walker.php deleted file mode 100644 index fa2bf0a4f5..0000000000 --- a/lib/WorseReflection/Core/Inference/Walker.php +++ /dev/null @@ -1,25 +0,0 @@ -resolver(); - foreach ($this->providers as $provider) { - foreach ($provider->enter($resolver, $frame, $node) as $diagnostic) { - $this->diagnostics[] = $diagnostic; - } - } - - return $frame; - } - - /** - * @return Diagnostics - */ - public function diagnostics(): Diagnostics - { - return new Diagnostics($this->diagnostics); - } - - public function exit(FrameResolver $resolver, Frame $frame, Node $node): Frame - { - $resolver = $resolver->resolver(); - foreach ($this->providers as $provider) { - foreach ($provider->exit($resolver, $frame, $node) as $diagnostic) { - $this->diagnostics[] = $diagnostic; - } - } - - return $frame; - } -} diff --git a/lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php b/lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php deleted file mode 100644 index 1e5512d3ab..0000000000 --- a/lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php +++ /dev/null @@ -1,211 +0,0 @@ -new(); - } - - $this->walkFunctionLike($resolver, $frame, $node); - - return $frame; - } - - public function exit(FrameResolver $resolver, Frame $frame, Node $node): Frame - { - return $frame; - } - - /** - * @param PropertyHook|MethodDeclaration|FunctionDeclaration|AnonymousFunctionCreationExpression|ArrowFunctionCreationExpression $node - */ - private function walkFunctionLike(FrameResolver $resolver, Frame $frame, FunctionLike $node): void - { - $namespace = $node->getNamespaceDefinition(); - do { - // If we are here we found a normal ObjectCreationExpression like: new A(); and this is not useful and we continue traversing - $classNode = ($classNode ?? $node)->getFirstAncestor( - ClassDeclaration::class, - InterfaceDeclaration::class, - TraitDeclaration::class, - EnumDeclaration::class, - ObjectCreationExpression::class, // For Inline classes - ); - } while ($classNode instanceof ObjectCreationExpression && $classNode->classTypeDesignator instanceof Node); - - if ($node instanceof AnonymousFunctionCreationExpression) { - $this->addAnonymousImports($frame, $node); - - // if this is a static anonymous function, set classNode to NULL - // so that we don't add the class context - if ($node->staticModifier && $node->staticModifier->kind === TokenKind::StaticKeyword) { - $classNode = null; - } - } - - // works for both closure and class method (we currently ignore binding) - if ($classNode !== null) { - $classType = $resolver->resolveNode($frame, $classNode)->type(); - $this->addClassContext($node, $classType, $frame); - } - - // todo: PropertyHook name for parameters is inconsistent with other function-likes - if ($node instanceof PropertyHook) { - $parameters = $node->parameterList; - } else { - $parameters = $node->parameters; - } - - if (null === $parameters) { - return; - } - - /** @var Parameter $parameterNode */ - foreach ($parameters->getElements() as $parameterNode) { - $parameterName = $parameterNode->variableName->getText($node->getFileContents()); - - $nodeContext = $resolver->resolveNode($frame, $parameterNode); - - $context = NodeContextFactory::create( - (string)$parameterName, - $parameterNode->getStartPosition(), - $parameterNode->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - 'type' => $nodeContext->type(), - ] - ); - - $frame->locals()->set(Variable::fromSymbolContext($context)->asDefinition()); - } - } - - private function addAnonymousImports(Frame $frame, AnonymousFunctionCreationExpression $node): void - { - $useClause = $node->anonymousFunctionUseClause; - - if (null === $useClause) { - return; - } - - $parentFrame = $frame->parent(); - if (null === $parentFrame) { - return; - } - $parentVars = $parentFrame->locals()->lessThanOrEqualTo($node->getStartPosition()); - - if (null === $useClause->useVariableNameList) { - return; - } - - if ($useClause->useVariableNameList instanceof MissingToken) { - return; - } - - foreach ($useClause->useVariableNameList->getElements() as $element) { - $varName = $element->variableName->getText($node->getFileContents()); - - $variableContext = NodeContextFactory::create( - $varName, - $element->getStartPosition(), - $element->getEndPosition(), - [ - 'symbol_type' => Symbol::VARIABLE, - ] - ); - $varName = $variableContext->symbol()->name(); - - // if not in parent scope, then we know nothing about it - // add it with above context and continue - // TODO: Do we infer the type hint?? - if (0 === $parentVars->byName($varName)->count()) { - $frame->locals()->set(Variable::fromSymbolContext($variableContext)); - continue; - } - - $variable = $parentVars->byName($varName)->last(); - - $variableContext = $variableContext - ->withType($variable->type()); - - $frame->locals()->set(Variable::fromSymbolContext($variableContext)->asDefinition()); - } - } - - private function addClassContext(Node $node, Type $classType, Frame $frame): void - { - $context = NodeContextFactory::create( - 'this', - $node->getStartPosition(), - $node->getEndPosition(), - [ - 'type' => $classType, - 'symbol_type' => Symbol::VARIABLE, - ] - ); - - // add this and self - $frame->locals()->set(Variable::fromSymbolContext($context)->asDefinition()); - - if (!$classType instanceof ReflectedClassType) { - return; - } - $reflection = $classType->reflectionOrNull(); - if (null === $reflection) { - return; - } - foreach ($reflection->members()->byMemberType(ReflectionMember::TYPE_PROPERTY) as $property) { - assert($property instanceof ReflectionProperty); - $frame->properties()->set(new Variable($property->name(), $property->position()->start()->toInt(), $property->inferredType(), $classType)); - } - } -} diff --git a/lib/WorseReflection/Core/Inference/Walker/IncludeWalker.php b/lib/WorseReflection/Core/Inference/Walker/IncludeWalker.php deleted file mode 100644 index ff3091a4fc..0000000000 --- a/lib/WorseReflection/Core/Inference/Walker/IncludeWalker.php +++ /dev/null @@ -1,121 +0,0 @@ -resolveNode($frame, $node->expression); - $includeUri = TypeUtil::valueOrNull($context->type()); - - if (!is_string($includeUri)) { - return $frame; - } - - $sourceNode = $node->getFirstAncestor(SourceFileNode::class); - - if (!$sourceNode instanceof SourceFileNode) { - return $frame; - } - - $uri = $sourceNode->uri; - - if (!$uri) { - $this->logger->warning('source code has no path associated with it, cannot process include'); - return $frame; - } - - if (Path::isRelative($includeUri)) { - $includeUri = Path::join(dirname($uri), $includeUri); - } - - if (!file_exists($includeUri)) { - $this->logger->warning('require/include "%s" does not exist'); - return $frame; - } - - $sourceNode = $this->parser->get(TextDocumentBuilder::fromUri($uri)->build()); - $includedFrame = $this->resolver->build($sourceNode); - $frame->locals()->merge($includedFrame->locals()); - - $parentNode = $node->parent; - - if ($parentNode instanceof AssignmentExpression) { - return $this->processAssignment($sourceNode, $resolver, $frame, $parentNode, $node); - } - - $frame->locals()->merge($includedFrame->locals()); - - return $frame; - } - - public function exit(FrameResolver $resolver, Frame $frame, Node $node): Frame - { - return $frame; - } - - private function processAssignment(SourceFileNode $sourceNode, FrameResolver $resolver, Frame $frame, AssignmentExpression $parentNode, ScriptInclusionExpression $node): Frame - { - $return = $sourceNode->getFirstDescendantNode(ReturnStatement::class); - assert($return instanceof ReturnStatement); - $returnValueContext = $resolver->resolveNode($frame->new(), $return->expression); - - if (!$parentNode->leftOperand instanceof Variable) { - return $frame; - } - - $name = $parentNode->leftOperand->name; - - if (!$name instanceof Token) { - return $frame; - } - - $name = $name->getText($node->getFileContents()); - - foreach ($frame->locals()->byName((string)$name) as $variable) { - $frame->locals()->replace( - $variable, - $variable->withType($returnValueContext->type()) - ); - return $frame; - } - - return $frame; - } -} diff --git a/lib/WorseReflection/Core/Inference/Walker/PassThroughWalker.php b/lib/WorseReflection/Core/Inference/Walker/PassThroughWalker.php deleted file mode 100644 index e4198cd231..0000000000 --- a/lib/WorseReflection/Core/Inference/Walker/PassThroughWalker.php +++ /dev/null @@ -1,55 +0,0 @@ -resolveNode($frame, $node); - - return $frame; - } - - public function exit(FrameResolver $resolver, Frame $frame, Node $node): Frame - { - return $frame; - } -} diff --git a/lib/WorseReflection/Core/Inference/Walker/TestAssertWalker.php b/lib/WorseReflection/Core/Inference/Walker/TestAssertWalker.php deleted file mode 100644 index 5ef1f72a50..0000000000 --- a/lib/WorseReflection/Core/Inference/Walker/TestAssertWalker.php +++ /dev/null @@ -1,214 +0,0 @@ -callableExpression->getText(); - - if ($name === 'wrFrame') { - /** @phpstan-ignore-next-line Allow dump() here */ - dump($frame->__toString()); - return $frame; - } - if ($node->argumentExpressionList === null) { - return $frame; - } - if ($name === 'wrAssertType') { - $this->assertType($resolver, $frame, $node); - return $frame; - } - if ($name === 'wrAssertOffset') { - $this->assertOffset($resolver, $frame, $node); - return $frame; - } - if ($name === 'wrReturnType') { - $this->assertReturnType($resolver, $frame, $node); - return $frame; - } - if ($name === 'wrAssertEval') { - $this->assertEval($resolver, $frame, $node); - return $frame; - } - if ($name === 'wrAssertSymbolName') { - $this->assertSymbolName($resolver, $frame, $node); - return $frame; - } - - return $frame; - } - - public function exit(FrameResolver $resolver, Frame $frame, Node $node): Frame - { - return $frame; - } - - private function assertType(FrameResolver $resolver, Frame $frame, CallExpression $node): void - { - $list = $node->argumentExpressionList->getElements(); - $args = []; - $exprs = []; - foreach ($list as $expression) { - if (!$expression instanceof ArgumentExpression) { - continue; - } - - $args[] = $resolver->resolveNode($frame, $expression); - $exprs[] = $expression; - } - - // get string to compare against - $expectedType = $args[0]->type(); - $actualType = $args[1]->type(); - $this->assertTypeIs($node, $actualType, $expectedType, $args[2]??null); - } - - private function assertEval(FrameResolver $resolver, Frame $frame, CallExpression $node): void - { - $list = $node->argumentExpressionList->getElements(); - $args = []; - $toEval = null; - $resolvedType = new MissingType(); - foreach ($list as $expression) { - if (!$expression instanceof ArgumentExpression) { - continue; - } - - $toEval = $expression->getText(); - $resolvedType = $resolver->resolveNode($frame, $expression)->type(); - break; - } - - if ($toEval === null) { - return; - } - - $evaled = eval('return ' . $toEval . ';'); - $this->testCase->assertEquals( - TypeFactory::fromValue($evaled)->__toString(), - $resolvedType->__toString() - ); - } - - private function assertSymbolName(FrameResolver $resolver, Frame $frame, CallExpression $node): void - { - $argList = $node->argumentExpressionList; - $args = $this->resolveArgs($argList, $resolver, $frame); - - $actual = $args[1]->symbol()->name(); - $expected = $args[0]->type(); - if (!$expected instanceof StringLiteralType) { - throw new RuntimeException(sprintf('Expected symbol type must be a string got "%s"', $expected->__toString())); - } - $message = isset($args[2]) ? TypeUtil::valueOrNull($args[2]->type()) : null; - - if ($expected->value() !== $actual) { - $this->testCase->fail(sprintf( - "%s:\n %s\nis not\n %s", - $node->getText(), - $expected, - $actual - )); - } - $this->testCase->addToAssertionCount(1); - } - - private function assertReturnType(FrameResolver $resolver, Frame $frame, CallExpression $node): void - { - $returnType = $frame->returnType(); - $args = $this->resolveArgs($node->argumentExpressionList, $resolver, $frame); - if (!isset($args[0])) { - throw new RuntimeException( - 'wrAssertReturnType requires an expected type argument' - ); - } - $expected = $args[0]->type(); - if (!$expected instanceof StringLiteralType) { - throw new RuntimeException(sprintf('Expected symbol type must be a string got "%s"', $expected->__toString())); - } - - - $this->assertTypeIs($node, $frame->returnType(), $expected); - } - - private function assertOffset(FrameResolver $resolver, Frame $frame, CallExpression $node): void - { - $args = $this->resolveArgs($node->argumentExpressionList, $resolver, $frame); - $expectedType = $args[0]->type(); - $type = $args[1]->type(); - if (!$type instanceof IntLiteralType) { - throw new RuntimeException( - 'Expected int literal' - ); - } - $offset = $resolver->reflector()->reflectOffset(NodeToTextDocumentConverter::convert($node), $type->value()); - $this->assertTypeIs($node, $offset->nodeContext()->type(), $expectedType); - } - - /** - * @return array - */ - private function resolveArgs(?ArgumentExpressionList $argList, FrameResolver $resolver, Frame $frame): array - { - $list = $argList->getElements(); - $args = []; - foreach ($list as $expression) { - if (!$expression instanceof ArgumentExpression) { - continue; - } - - $args[] = $resolver->resolveNode($frame, $expression); - } - return $args; - } - - private function assertTypeIs(Node $node, Type $actualType, Type $expectedType, ?NodeContext $message = null): void - { - $message = isset($message) ? TypeUtil::valueOrNull($message->type()) : null; - $position = PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $node->getFileContents()); - if ($actualType->__toString() === TypeUtil::valueOrNull($expectedType)) { - $this->testCase->addToAssertionCount(1); - return; - } - $this->testCase->fail(sprintf( - "%s: \n\n %s\n\nis:\n\n %s\n\non offset %s line %s char %s", - $message ?: 'Failed asserting that:', - $actualType->__toString(), - trim($expectedType->__toString(), '"'), - $node->getStartPosition(), - $position->line + 1, - $position->character + 1, - )); - } -} diff --git a/lib/WorseReflection/Core/Inference/Walker/VariableWalker.php b/lib/WorseReflection/Core/Inference/Walker/VariableWalker.php deleted file mode 100644 index 137e1179a0..0000000000 --- a/lib/WorseReflection/Core/Inference/Walker/VariableWalker.php +++ /dev/null @@ -1,93 +0,0 @@ -reflector(), $node); - $docblockType = $this->injectVariablesFromComment($scope, $frame, $node); - - if (null === $docblockType) { - return $frame; - } - - if (!$node instanceof Variable) { - return $frame; - } - - $token = $node->name; - if (false === $token instanceof Token) { - return $frame; - } - - $name = (string)$token->getText($node->getFileContents()); - $frame->varDocBuffer()->set($name, $docblockType); - - return $frame; - } - - public function exit(FrameResolver $resolver, Frame $frame, Node $node): Frame - { - return $frame; - } - - private function injectVariablesFromComment(PhpactorReflectionScope $scope, Frame $frame, Node $node): ?Type - { - $comment = $node->getLeadingCommentAndWhitespaceText(); - $docblock = $this->docblockFactory->create($comment, $scope); - - if (false === $docblock->isDefined()) { - return null; - } - - $vars = $docblock->vars(); - $resolvedTypes = []; - - /** @var DocBlockVar $var */ - foreach ($docblock->vars() as $var) { - $type = $var->type(); - - if (empty($var->name())) { - return $type; - } - - // there's a chance this will be redefined later, but define it now - // to ensure that type assertions can find a previous variable - $frame->locals()->add(new PhpactorVariable( - name: $var->name(), - offset: $node->getStartPosition(), - type: $type, - wasAssigned: false /** $wasAssigned bool */, - wasDefined: true /** $wasDefined bool */ - ), $node->getStartPosition()); - $frame->varDocBuffer()->set('$' . $var->name(), $type); - } - - return null; - } -} diff --git a/lib/WorseReflection/Core/MemberTypeContextualiser.php b/lib/WorseReflection/Core/MemberTypeContextualiser.php deleted file mode 100644 index e365114752..0000000000 --- a/lib/WorseReflection/Core/MemberTypeContextualiser.php +++ /dev/null @@ -1,28 +0,0 @@ -map(function (Type $type) use ($class, $declaringClass) { - if ($type instanceof ThisType) { - return new ThisType($class->type()); - } - if ($type instanceof StaticType) { - return new StaticType($class->type()); - } - if ($type instanceof SelfType) { - return new SelfType($declaringClass->type()); - } - - return $type; - }); - } -} diff --git a/lib/WorseReflection/Core/Name.php b/lib/WorseReflection/Core/Name.php deleted file mode 100644 index 0ae2bf2976..0000000000 --- a/lib/WorseReflection/Core/Name.php +++ /dev/null @@ -1,138 +0,0 @@ - $parts */ - final public function __construct( - protected array $parts, - private bool $wasFullyQualified - ) { - } - - public function __toString(): string - { - return implode('\\', $this->parts); - } - - public static function fromParts(array $parts): static - { - return new static($parts, false); - } - - public static function fromString(string $string): Name - { - $fullyQualified = str_starts_with($string, '\\'); - $parts = explode('\\', trim($string, '\\')); - - return new static($parts, $fullyQualified); - } - - /** - * @param Name|string $value - * @return static|Name - */ - public static function fromUnknown($value): Name - { - if ($value instanceof Name) { - return $value; - } - - if (is_string($value)) { - return static::fromString($value); - } - - /** @phpstan-ignore-next-line */ - throw new InvalidArgumentException(sprintf( - 'Do not know how to create class from type "%s"', - get_debug_type($value) - )); - } - - /** - * Return with only last segment of the name of name - */ - public function head(): self - { - return new self([ reset($this->parts) ?: '' ], false); - } - - /** - * Return without the last segment of the name - */ - public function tail(): self - { - $parts = $this->parts; - array_shift($parts); - return new self($parts, $this->wasFullyQualified); - } - - /** - * Return with only the first segment of the name - */ - public function base(): self - { - $parts = $this->parts; - $first = array_shift($parts); - return new self([$first], $this->wasFullyQualified); - } - - public function namespace(): string - { - if (count($this->parts) === 1) { - return ''; - } - - return implode('\\', array_slice($this->parts, 0, count($this->parts) - 1)); - } - - public function full(): string - { - return $this->__toString(); - } - - public function short(): string - { - return (string) end($this->parts); - } - - public function wasFullyQualified(): bool - { - return $this->wasFullyQualified; - } - - public function prepend($name): static - { - $name = Name::fromUnknown($name); - return self::fromString(join('\\', [(string) $name, $this->__toString()])); - } - - public function isAncestorOrSame(Name $name): bool - { - $segment = array_slice($name->parts, 0, count($this->parts)); - return $segment === $this->parts; - } - - public function substitute(Name $name, $alias): Name - { - $suffix = array_slice($this->parts, count($name->parts)); - return Name::fromParts(array_merge( - [$alias], - $suffix - )); - } - - public function count(): int - { - return count($this->parts); - } -} diff --git a/lib/WorseReflection/Core/NameImports.php b/lib/WorseReflection/Core/NameImports.php deleted file mode 100644 index 731a6a3faf..0000000000 --- a/lib/WorseReflection/Core/NameImports.php +++ /dev/null @@ -1,67 +0,0 @@ - $item) { - $this->add($short, $item); - } - } - - public static function fromNames(array $nameImports): NameImports - { - return new self($nameImports); - } - - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->nameImports); - } - - public function getByAlias(string $alias) - { - if (!isset($this->nameImports[$alias])) { - throw new RuntimeException(sprintf( - 'Unknown alias "%s", known aliases: "%s"', - $alias, - implode('", "', array_keys($this->nameImports)) - )); - } - - return $this->nameImports[$alias]; - } - - public function resolveLocalName(Name $name): Name - { - foreach ($this->nameImports as $alias => $importedName) { - assert($importedName instanceof Name); - - if (!$importedName->isAncestorOrSame($name)) { - continue; - } - - return $name->substitute($importedName, $alias); - } - - return Name::fromString($name->short()); - } - - public function hasAlias(string $alias) - { - return isset($this->nameImports[$alias]); - } - - private function add(string $short, Name $item): void - { - $this->nameImports[$short] = $item; - } -} diff --git a/lib/WorseReflection/Core/NavigatorElementCollection.php b/lib/WorseReflection/Core/NavigatorElementCollection.php deleted file mode 100644 index d3c9b6658c..0000000000 --- a/lib/WorseReflection/Core/NavigatorElementCollection.php +++ /dev/null @@ -1,58 +0,0 @@ - - */ -class NavigatorElementCollection implements IteratorAggregate -{ - /** - * @param array $elements - */ - public function __construct(private array $elements) - { - } - - /** - * @return T - */ - public function first() - { - foreach ($this->elements as $element) { - return $element; - } - throw new RuntimeException( - 'Collection is empty, cannot get first' - ); - } - - /** - * @param Closure(T): bool $predicate - * @return T - */ - public function firstBy(Closure $predicate) - { - foreach ($this->elements as $element) { - if ($predicate($element)) { - return $element; - } - } - - throw new RuntimeException( - 'No elements matched the given predicate' - ); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->elements); - } -} diff --git a/lib/WorseReflection/Core/NodeText.php b/lib/WorseReflection/Core/NodeText.php deleted file mode 100644 index c64fd1124a..0000000000 --- a/lib/WorseReflection/Core/NodeText.php +++ /dev/null @@ -1,20 +0,0 @@ -nodeText; - } - - public static function fromString(string $nodeText): NodeText - { - return new self($nodeText); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/AbstractReflectionCollection.php b/lib/WorseReflection/Core/Reflection/Collection/AbstractReflectionCollection.php deleted file mode 100644 index 62159992c8..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/AbstractReflectionCollection.php +++ /dev/null @@ -1,150 +0,0 @@ - - */ -abstract class AbstractReflectionCollection implements ReflectionCollection -{ - /** - * @param array $items - */ - final protected function __construct(protected array $items) - { - } - - public function count(): int - { - return count($this->items); - } - - /** - * @return array-key[] - */ - public function keys(): array - { - return array_keys($this->items); - } - - /** - * @return static - * @param T[] $reflections - */ - public static function fromReflections(array $reflections): self - { - return new static($reflections); - } - - /** - * @return static - */ - public static function empty(): self - { - return new static([]); - } - - /** - * @return static - * @param AbstractReflectionCollection $collection - */ - public function merge(ReflectionCollection $collection): self - { - $items = $this->items; - - foreach ($collection as $key => $value) { - $items[$key] = $value; - } - - return new static($items); - } - - /** - * @return T - */ - public function get(string $name) - { - if (!isset($this->items[$name])) { - throw new ItemNotFound(sprintf( - 'Unknown item "%s", known items: "%s"', - $name, - implode('", "', array_keys($this->items)) - )); - } - - return $this->items[$name]; - } - - /** - * @return T - */ - public function first() - { - if ($this->items === []) { - throw new ItemNotFound( - 'Collection is empty, cannot get the first item' - ); - } - - return reset($this->items); - } - - /** - * @return T|null - */ - public function firstOrNull() - { - return reset($this->items) ?: null; - } - - /** - * @return T - */ - public function last() - { - if (empty($this->items)) { - throw new ItemNotFound( - 'Collection is empty, cannot get the last item' - ); - } - - return end($this->items); - } - - /** - * @return T|null - */ - public function lastOrNull() - { - if (empty($this->items)) { - return null; - } - - return end($this->items); - } - - public function has(string $name): bool - { - return isset($this->items[$name]); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } - - /** - * @return static - */ - public function byMemberClass(string $fqn): ReflectionCollection - { - return new static(array_filter($this->items, function ($member) use ($fqn) { - return $member instanceof $fqn; - })); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ChainReflectionMemberCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ChainReflectionMemberCollection.php deleted file mode 100644 index 70c0cf1e69..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ChainReflectionMemberCollection.php +++ /dev/null @@ -1,273 +0,0 @@ - - */ -final class ChainReflectionMemberCollection implements ReflectionMemberCollection -{ - /** - * @var array> - */ - private array $collections = []; - - /** - * @param array $collections - */ - final private function __construct(array $collections) - { - foreach ($collections as $collection) { - $this->add($collection); - } - } - - /** - * @param array $collections - * @return self - */ - public static function fromCollections(array $collections): self - { - return new static($collections); - } - - /** - * @return AppendIterator - */ - public function getIterator(): Traversable - { - $iterator = new AppendIterator(); - foreach ($this->collections as $collection) { - /** @phpstan-ignore-next-line */ - $iterator->append($collection->getIterator()); - } - - return $iterator; - } - - public function count(): int - { - return array_reduce($this->collections, function ($acc, ReflectionMemberCollection $collection) { - $acc += count($collection); - return $acc; - }, 0); - } - - public function keys(): array - { - return array_reduce($this->collections, function ($acc, ReflectionMemberCollection $collection) { - $acc = array_merge($acc, $collection->keys()); - return $acc; - }, []); - } - - /** - * @param ReflectionMemberCollection $collection - * @phpstan-ignore-next-line - */ - public function merge(ReflectionCollection $collection): self - { - $new = new static($this->collections); - $new->add($collection); - return $new; - } - - public function get(string $name) - { - $known = []; - foreach ($this->collections as $collection) { - $known = array_merge($known, $collection->keys()); - if ($collection->has($name)) { - return $collection->get($name); - } - } - - throw new ItemNotFound(sprintf( - 'Unknown item "%s", known items: "%s"', - $name, - implode('", "', $known) - )); - } - - public function first() - { - foreach ($this->collections as $collection) { - if ($collection->count()) { - return $collection->first(); - } - } - - throw new ItemNotFound( - 'None of the collections have items' - ); - } - - public function last() - { - $last = null; - - foreach ($this->collections as $collection) { - $last = $collection->last(); - } - - if ($last) { - return $last; - } - - throw new ItemNotFound( - 'None of the collections have items' - ); - } - - public function has(string $name): bool - { - foreach ($this->collections as $collection) { - if ($collection->has($name)) { - return true; - } - } - - return false; - } - - /** - * @return ReflectionMemberCollection - * @param array $visibilities - */ - public function byVisibilities(array $visibilities): ReflectionMemberCollection - { - $collections = []; - foreach ($this->collections as $collection) { - $collections[] = $collection->byVisibilities($visibilities); - } - - return new static($collections); - } - - public function belongingTo(ClassName $class): ReflectionMemberCollection - { - $collections = []; - foreach ($this->collections as $collection) { - $collections[] = $collection->belongingTo($class); - } - - return new static($collections); - } - - public function atOffset(int $offset): ReflectionMemberCollection - { - $collections = []; - foreach ($this->collections as $collection) { - $collections[] = $collection->atOffset($offset); - } - - return new static($collections); - } - - public function byName(string $name): ReflectionMemberCollection - { - $collections = []; - foreach ($this->collections as $collection) { - $collections[] = $collection->byName($name); - } - - return new static($collections); - } - - public function virtual(): ReflectionMemberCollection - { - $collections = []; - foreach ($this->collections as $collection) { - $collections[] = $collection->virtual(); - } - - return new static($collections); - } - - public function real(): ReflectionMemberCollection - { - $collections = []; - foreach ($this->collections as $collection) { - $collections[] = $collection->real(); - } - - return new static($collections); - } - - public function methods(): ReflectionMethodCollection - { - return ReflectionMethodCollection::fromReflections(iterator_to_array($this->byMemberClass(ReflectionMethod::class))); - } - - public function properties(): ReflectionPropertyCollection - { - return ReflectionPropertyCollection::fromReflections(iterator_to_array($this->byMemberClass(ReflectionProperty::class))); - } - - public function constants(): ReflectionConstantCollection - { - return ReflectionConstantCollection::fromReflections(iterator_to_array($this->byMemberClass(ReflectionConstant::class))); - } - - public function enumCases(): ReflectionEnumCaseCollection - { - return ReflectionEnumCaseCollection::fromReflections(iterator_to_array($this->byMemberClass(ReflectionEnumCase::class))); - } - - public function byMemberClass(string $fqn): ReflectionCollection - { - $items = []; - foreach ($this->collections as $collection) { - foreach ($collection->byMemberClass($fqn) as $key => $reflection) { - $items[$key] = $reflection; - } - } - - /** @phpstan-ignore-next-line It's _fine_ */ - return HomogeneousReflectionMemberCollection::fromReflections($items); - } - - /** - * @param ReflectionMember::TYPE_* $type - */ - public function byMemberType(string $type): ReflectionMemberCollection - { - $collections = []; - foreach ($this->collections as $collection) { - $collections[] = $collection->byMemberType($type); - } - - return new static($collections); - } - - public function map(Closure $mapper) - { - $collections = []; - foreach ($this->collections as $collection) { - $collections[] = $collection->map($mapper); - } - - return new static($collections); - } - - /** - * @param ReflectionMemberCollection $collection - */ - private function add(ReflectionMemberCollection $collection): void - { - $this->collections[] = $collection; - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ClassLikeReflectionMemberCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ClassLikeReflectionMemberCollection.php deleted file mode 100644 index 7355619cb1..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ClassLikeReflectionMemberCollection.php +++ /dev/null @@ -1,330 +0,0 @@ - - * @implements ReflectionMemberCollection - */ -final class ClassLikeReflectionMemberCollection extends AbstractReflectionCollection implements ReflectionMemberCollection -{ - private const MEMBER_TYPES = [ - 'constants', - 'properties', - 'methods', - 'enumCases', - ]; - - /** - * @var PhpactorReflectionConstant[] - */ - private array $constants = []; - - /** - * @var PhpactorReflectionProperty[] - */ - private array $properties = []; - - /** - * @var PhpactorReflectionMethod[] - */ - private array $methods = []; - - /** - * @var PhpactorReflectionEnumCase[] - */ - private array $enumCases = []; - - public static function fromClassMemberDeclarations( - ServiceLocator $serviceLocator, - ClassDeclaration $class, - ReflectionClass $reflectionClass - ): self { - return self::fromDeclarations( - $serviceLocator, - $reflectionClass, - $class->classMembers->classMemberDeclarations, - ); - } - - public static function fromTraitMemberDeclarations(ServiceLocator $serviceLocator, TraitDeclaration $traitDeclaration, ReflectionTrait $reflectionTrait): self - { - return self::fromDeclarations( - $serviceLocator, - $reflectionTrait, - $traitDeclaration->traitMembers->traitMemberDeclarations, - ); - } - - public static function fromInterfaceMemberDeclarations(ServiceLocator $serviceLocator, InterfaceDeclaration $interfaceDeclaration, ReflectionInterface $reflectionInterface): self - { - return self::fromDeclarations( - $serviceLocator, - $reflectionInterface, - $interfaceDeclaration->interfaceMembers->interfaceMemberDeclarations, - ); - } - - public static function fromEnumMemberDeclarations(ServiceLocator $serviceLocator, EnumDeclaration $enumDeclaration, ReflectionEnum $reflectionEnum): self - { - return self::fromDeclarations( - $serviceLocator, - $reflectionEnum, - $enumDeclaration->enumMembers->enumMemberDeclarations - ); - } - - public function getIterator(): Traversable - { - foreach (self::MEMBER_TYPES as $collection) { - yield from $this->$collection; - } - } - - public function merge(ReflectionCollection $collection): AbstractReflectionCollection - { - $new = clone $this; - foreach ($collection as $member) { - $new->items[$member->name()] = $member; - if ($member instanceof ReflectionConstant) { - $new->constants[$member->name()] = $member; - continue; - } - if ($member instanceof PhpactorReflectionProperty) { - $new->properties[$member->name()] = $member; - continue; - } - if ($member instanceof PhpactorReflectionMethod) { - $new->methods[$member->name()] = $member; - continue; - } - if ($member instanceof PhpactorReflectionEnumCase) { - $new->enumCases[$member->name()] = $member; - continue; - } - } - - return $new; - } - - /** - * @return static - */ - public function byMemberClass(string $fqn): ReflectionCollection - { - return $this->filter(function (ReflectionMember $member) use ($fqn) { - return $member instanceof $fqn; - }); - } - - /** - * @return self - */ - public function byMemberType(string $type): ReflectionCollection - { - return $this->filter(function (ReflectionMember $member) use ($type) { - return $member->memberType() === $type; - }); - } - - public function byVisibilities(array $visibilities): ReflectionMemberCollection - { - return $this->filter(function (ReflectionMember $member) use ($visibilities) { - foreach ($visibilities as $visiblity) { - if ($visiblity == $member->visibility()) { - return true; - } - } - - return false; - }); - } - - public function belongingTo(ClassName $class): ReflectionMemberCollection - { - return $this->filter(function (ReflectionMember $member) use ($class) { - return $member->declaringClass()->name() == $class; - }); - } - - public function atOffset(int $offset): ReflectionMemberCollection - { - return $this->filter(function (ReflectionMember $member) use ($offset) { - return $member->position()->start()->toInt() <= $offset && $member->position()->end()->toInt() >= $offset; - }); - } - - public function byName(string $name): ReflectionMemberCollection - { - return $this->filter(function (ReflectionMember $member) use ($name) { - return $member->name() === $name; - }); - } - - public function virtual(): ReflectionMemberCollection - { - return $this->filter(fn (ReflectionMember $member) => $member->isVirtual()); - } - - public function real(): ReflectionMemberCollection - { - return $this->filter(fn (ReflectionMember $member) => !$member->isVirtual()); - } - - public function methods(): ReflectionMethodCollection - { - return new ReflectionMethodCollection($this->methods); - } - - public function properties(): ReflectionPropertyCollection - { - return new ReflectionPropertyCollection($this->properties); - } - - public function constants(): ReflectionConstantCollection - { - return new ReflectionConstantCollection($this->constants); - } - - public function enumCases(): ReflectionEnumCaseCollection - { - return new ReflectionEnumCaseCollection($this->enumCases); - } - - - public function map(Closure $closure) - { - $new = new self([]); - foreach (self::MEMBER_TYPES as $collection) { - /** @phpstan-ignore-next-line */ - $new->$collection = array_map($closure, $this->$collection); - } - $new->items = array_merge(...array_map(fn (string $type) => $this->$type, self::MEMBER_TYPES)); - - return $new; - } - - /** - * @param array $nodes - */ - private static function fromDeclarations(ServiceLocator $serviceLocator, ReflectionClassLike $classLike, array $nodes): self - { - $new = new self([]); - foreach ($nodes as $member) { - if ($member instanceof ClassConstDeclaration) { - /** @phpstan-ignore-next-line TP: lie */ - if (!$member->constElements) { - continue; - } - - foreach ($member->constElements->getElements() as $constElement) { - $constant = new ReflectionConstant($serviceLocator, $classLike, $member, $constElement); - $new->constants[$constant->name()] = $constant; - $new->items[$constant->name()] = $constant; - } - continue; - } - - // Phan's fork parses properties in interfaces, whereas the upstream one seems not to. - if ($member instanceof PropertyDeclaration && !$classLike instanceof PhpactorReflectionInterface) { - foreach ($member->propertyElements->getChildNodes() as $propertyElement) { - assert($propertyElement instanceof PropertyElement); - $variable = $propertyElement->variable; - if (false === $variable instanceof Variable) { - continue; - } - $property = new ReflectionProperty($serviceLocator, $classLike, $member, $variable); - $new->properties[$property->name()] = $property; - $new->items[$property->name()] = $property; - } - continue; - } - if ($member instanceof MethodDeclaration) { - $method = new ReflectionMethod($serviceLocator, $classLike, $member); - $new->items[$method->name()] = $method; - $new->methods[$method->name()] = $method; - - // promoted properties - if ($method->name() === '__construct') { - $parameters = $member->parameters; - /** @phpstan-ignore-next-line */ - if (!$parameters) { - continue; - } - $children = $parameters->children; - if (!$children) { - continue; - } - foreach (array_filter($children, function ($member) { - if (!$member instanceof Parameter) { - return false; - } - return $member->visibilityToken !== null; - }) as $promotedParameter) { - if (!$promotedParameter instanceof Parameter) { - continue; - } - $property = new ReflectionPromotedProperty($serviceLocator, $classLike, $promotedParameter); - $new->items[$property->name()] = $property; - $new->properties[$property->name()] = $property; - } - } - continue; - } - - if ($member instanceof EnumCaseDeclaration && $classLike instanceof ReflectionEnum) { - $enumCase = new PhpactorReflectionEnumCase($serviceLocator, $classLike, $member); - $new->items[$enumCase->name()] = $enumCase; - $new->enumCases[$enumCase->name()] = $enumCase; - continue; - } - } - - - return $new; - } - - private function filter(Closure $closure): self - { - $new = new self([]); - foreach (self::MEMBER_TYPES as $collection) { - $new->$collection = array_filter($this->$collection, $closure); - } - - $new->items = array_merge(...array_map(fn (string $type) => $new->$type, self::MEMBER_TYPES)); - - return $new; - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/HomogeneousReflectionMemberCollection.php b/lib/WorseReflection/Core/Reflection/Collection/HomogeneousReflectionMemberCollection.php deleted file mode 100644 index 7fbc56f2fc..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/HomogeneousReflectionMemberCollection.php +++ /dev/null @@ -1,144 +0,0 @@ - - * @implements ReflectionMemberCollection - */ -class HomogeneousReflectionMemberCollection extends AbstractReflectionCollection implements ReflectionMemberCollection -{ - /** - * @return static - * @param ReflectionMember[] $members - */ - public static function fromMembers(array $members): HomogeneousReflectionMemberCollection - { - return new static($members); - } - - /** - * @return static - * @param Visibility[] $visibilities - */ - public function byVisibilities(array $visibilities): HomogeneousReflectionMemberCollection - { - $items = []; - foreach ($this as $key => $item) { - foreach ($visibilities as $visibility) { - if ($item->visibility() != $visibility) { - continue; - } - - $items[$key] = $item; - } - } - - return new static($items); - } - - /** - * @return static - */ - public function belongingTo(ClassName $class): HomogeneousReflectionMemberCollection - { - return new static(array_filter($this->items, function (ReflectionMember $item) use ($class) { - return $item->declaringClass()->name() == $class; - })); - } - - /** - * @return static - */ - public function atOffset(int $offset): HomogeneousReflectionMemberCollection - { - return new static(array_filter($this->items, function (ReflectionMember $item) use ($offset) { - return $item->position()->start()->toInt() <= $offset && $item->position()->end()->toInt() >= $offset; - })); - } - - /** - * @return static - */ - public function byName(string $name): HomogeneousReflectionMemberCollection - { - if ($this->has($name)) { - return new static([ $this->get($name) ]); - } - - return new static([]); - } - - /** - * @return static - */ - public function virtual(): HomogeneousReflectionMemberCollection - { - return new static(array_filter($this->items, fn (ReflectionMember $member) => $member->isVirtual())); - } - - /** - * @return static - */ - public function real(): HomogeneousReflectionMemberCollection - { - return new static(array_filter($this->items, fn (ReflectionMember $member) => !$member->isVirtual())); - } - - public function methods(): ReflectionMethodCollection - { - return new ReflectionMethodCollection(array_filter($this->items, function (ReflectionMember $member) { - return $member instanceof ReflectionMethod; - })); - } - - public function constants(): ReflectionConstantCollection - { - return new ReflectionConstantCollection(array_filter($this->items, function (ReflectionMember $member) { - return $member instanceof ReflectionConstant; - })); - } - - public function properties(): ReflectionPropertyCollection - { - return new ReflectionPropertyCollection(array_filter($this->items, function (ReflectionMember $member) { - return $member instanceof ReflectionProperty; - })); - } - - public function enumCases(): ReflectionEnumCaseCollection - { - return new ReflectionEnumCaseCollection(array_filter($this->items, function (ReflectionMember $member) { - return $member instanceof ReflectionEnumCase; - })); - } - - /** - * @return static - */ - public function byMemberType(string $type): HomogeneousReflectionMemberCollection - { - return new static(array_filter($this->items, function (ReflectionMember $member) use ($type) { - return $type === $member->memberType(); - })); - } - - public function map(Closure $mapper) - { - return new static(array_map($mapper, $this->items)); - } - - protected function collectionType(): string - { - return HomogeneousReflectionMemberCollection::class; - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionArgumentCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionArgumentCollection.php deleted file mode 100644 index e14fff8d7f..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionArgumentCollection.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ -class ReflectionArgumentCollection extends AbstractReflectionCollection -{ - public static function fromArgumentListAndFrame(ServiceLocator $locator, ArgumentExpressionList $list, Frame $frame): self - { - $arguments = []; - foreach ($list->getElements() as $element) { - if (!$element instanceof ArgumentExpression) { - continue; - } - if ($element->name) { - $key = $element->name->getText($element->getFileContents()); - $arguments[$key] = new ReflectionArgument($locator, $frame, $element); - continue; - } - $arguments[] = new ReflectionArgument($locator, $frame, $element); - } - - return new self($arguments); - } - - public function notPromoted(): self - { - return $this; - } - - public function promoted(): self - { - return new self([]); - } - - /** - * @return array - */ - public function named(): array - { - $arguments = []; - $counters = []; - foreach ($this as $argument) { - $name = $argument->guessName(); - - if (isset($arguments[$name])) { - if (!isset($counters[$name])) { - $counters[$name] = 1; - } - $counters[$name]++; - $name = $argument->guessName() . $counters[$name]; - } - - $arguments[$name] = $argument; - } - - return $arguments; - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionClassCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionClassCollection.php deleted file mode 100644 index e64114dc63..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionClassCollection.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ -final class ReflectionClassCollection extends AbstractReflectionCollection -{ - public function concrete(): self - { - return new static(array_filter($this->items, function ($item) { - return $item->isConcrete(); - })); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionClassLikeCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionClassLikeCollection.php deleted file mode 100644 index d1bef46d16..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionClassLikeCollection.php +++ /dev/null @@ -1,76 +0,0 @@ - - */ -final class ReflectionClassLikeCollection extends AbstractReflectionCollection -{ - /** - * @param array $visited - */ - public static function fromNode(ServiceLocator $serviceLocator, TextDocument $source, Node $node, array $visited = []): self - { - $items = []; - - $nodeCollection = $node->getDescendantNodes(function (Node $node) { - return false === $node instanceof ClassLike; - }); - - foreach ($nodeCollection as $child) { - if (false === $child instanceof ClassLike) { - continue; - } - - if ($child instanceof TraitDeclaration) { - $items[(string) $child->getNamespacedName()] = new ReflectionTrait($serviceLocator, $source, $child, $visited); - continue; - } - - if ($child instanceof EnumDeclaration) { - $items[(string) $child->getNamespacedName()] = new ReflectionEnum($serviceLocator, $source, $child); - continue; - } - - if ($child instanceof InterfaceDeclaration) { - $items[(string) $child->getNamespacedName()] = new ReflectionInterface($serviceLocator, $source, $child, $visited); - continue; - } - - if ($child instanceof ClassDeclaration) { - $items[(string) $child->getNamespacedName()] = new ReflectionClass($serviceLocator, $source, $child, $visited); - } - } - - return new static($items); - } - - public function classes(): ReflectionClassCollection - { - /** @phpstan-ignore-next-line */ - return new ReflectionClassCollection(iterator_to_array($this->byMemberClass(PhpactorReflectionClass::class))); - } - - public function concrete(): self - { - return new static(array_filter($this->items, function ($item) { - return $item->isConcrete(); - })); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionCollection.php deleted file mode 100644 index dee9f05405..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionCollection.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ -interface ReflectionCollection extends IteratorAggregate, Countable -{ - public function count(): int; - - /** - * @return array-key[] - */ - public function keys(): array; - - /** - * @return static - * @param ReflectionCollection $collection - */ - public function merge(ReflectionCollection $collection): self; - - /** - * @return T - */ - public function get(string $name); - - /** - * @return T - */ - public function first(); - - /** - * @return T - */ - public function last(); - - public function has(string $name): bool; - - /** - * @template M of T - * @param class-string $fqn - * @return ReflectionCollection - */ - public function byMemberClass(string $fqn): ReflectionCollection; -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionConstantCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionConstantCollection.php deleted file mode 100644 index 1103541287..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionConstantCollection.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -class ReflectionConstantCollection extends HomogeneousReflectionMemberCollection -{ - /** - * @param CoreReflectionConstant[] $constants - */ - public static function fromReflectionConstants(array $constants): self - { - return new self($constants); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionDeclaredConstantCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionDeclaredConstantCollection.php deleted file mode 100644 index c5e0e41093..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionDeclaredConstantCollection.php +++ /dev/null @@ -1,55 +0,0 @@ - - */ -class ReflectionDeclaredConstantCollection extends AbstractReflectionCollection -{ - /** - * @param ReflectionDeclaredConstant[] $constants - */ - public static function fromReflectionConstants(array $constants): self - { - return new self($constants); - } - - public static function fromNode(ServiceLocator $serviceLocator, TextDocument $sourceCode, SourceFileNode $node): ReflectionDeclaredConstantCollection - { - $items = []; - foreach ($node->getDescendantNodes() as $descendentNode) { - if (!$descendentNode instanceof CallExpression) { - continue; - } - - $callable = $descendentNode->callableExpression; - - if (!$callable instanceof QualifiedName) { - continue; - } - - if ('define' !== NodeUtil::shortName($callable)) { - continue; - } - - $constant = new PhpactorReflectionDeclaredConstant( - $serviceLocator, - $sourceCode, - $descendentNode - ); - $items[$constant->name()->__toString()] = $constant; - } - - return new self($items); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionEnumCaseCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionEnumCaseCollection.php deleted file mode 100644 index 0260ab1d86..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionEnumCaseCollection.php +++ /dev/null @@ -1,12 +0,0 @@ - - */ -final class ReflectionEnumCaseCollection extends HomogeneousReflectionMemberCollection -{ -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionFunctionCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionFunctionCollection.php deleted file mode 100644 index cddf1f76cd..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionFunctionCollection.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -class ReflectionFunctionCollection extends AbstractReflectionCollection -{ - public static function fromNode(ServiceLocator $serviceLocator, TextDocument $sourceCode, SourceFileNode $node): self - { - $items = []; - foreach ($node->getDescendantNodes() as $descendentNode) { - if (!$descendentNode instanceof FunctionDeclaration) { - continue; - } - - $items[(string) $descendentNode->getNamespacedName()] = new ReflectionFunction($sourceCode, $serviceLocator, $descendentNode); - } - - return new self($items); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionInterfaceCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionInterfaceCollection.php deleted file mode 100644 index fc66139b4f..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionInterfaceCollection.php +++ /dev/null @@ -1,73 +0,0 @@ - - */ -class ReflectionInterfaceCollection extends AbstractReflectionCollection -{ - /** - * @param array $visited - */ - public static function fromInterfaceDeclaration(ServiceLocator $serviceLocator, InterfaceDeclaration $interface, array $visited = []): self - { - return self::fromBaseClause($serviceLocator, $interface->interfaceBaseClause, $visited); - } - - public static function fromClassDeclaration(ServiceLocator $serviceLocator, ClassDeclaration $class): self - { - return self::fromBaseClause($serviceLocator, $class->classInterfaceClause, []); - } - - /** - * @param mixed $baseClause - * @param array $visited - */ - private static function fromBaseClause(ServiceLocator $serviceLocator, $baseClause, array $visited): self - { - if (!$baseClause instanceof ClassInterfaceClause && !$baseClause instanceof InterfaceBaseClause) { - return new self([]); - } - - $items = []; - $interfaceNameList = $baseClause->interfaceNameList; - - if (null === $interfaceNameList) { - return new self([]); - } - - $children = $interfaceNameList->children; - - if (!$children) { - return new self([]); - } - - foreach ($children as $name) { - if (false === $name instanceof QualifiedName) { - continue; - } - - try { - $interface = $serviceLocator->reflector()->reflectInterface( - ClassName::fromString((string) $name->getResolvedName()), - $visited - ); - $items[$interface->name()->full()] = $interface; - } catch (NotFound) { - } - } - - return new self($items); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionMemberCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionMemberCollection.php deleted file mode 100644 index 481c667910..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionMemberCollection.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ -interface ReflectionMemberCollection extends ReflectionCollection -{ - /** - * @return static - * @param Visibility[] $visibilities - */ - public function byVisibilities(array $visibilities): ReflectionMemberCollection; - - /** - * @return static - */ - public function belongingTo(ClassName $class): ReflectionMemberCollection; - - /** - * @return static - */ - public function atOffset(int $offset): ReflectionMemberCollection; - - /** - * @return static - */ - public function byName(string $name): ReflectionMemberCollection; - - /** - * @return static - */ - public function virtual(): ReflectionMemberCollection; - - /** - * @return static - */ - public function real(): ReflectionMemberCollection; - - public function methods(): ReflectionMethodCollection; - - public function properties(): ReflectionPropertyCollection; - - public function constants(): ReflectionConstantCollection; - - public function enumCases(): ReflectionEnumCaseCollection; - - /** - * @return static - */ - public function byMemberType(string $type): ReflectionCollection; - - - /** - * @param Closure(T): ReflectionMember $mapper - * @return static - */ - public function map(Closure $mapper); -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionMethodCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionMethodCollection.php deleted file mode 100644 index aca0406217..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionMethodCollection.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ -class ReflectionMethodCollection extends HomogeneousReflectionMemberCollection -{ - /** - * @param CoreReflectionMethod[] $methods - */ - public static function fromReflectionMethods(array $methods): CoreReflectionMethodCollection - { - $items = []; - foreach ($methods as $method) { - $items[$method->name()] = $method; - } - return new self($items); - } - - public function abstract(): CoreReflectionMethodCollection - { - return new self(array_filter($this->items, function (CoreReflectionMethod $item) { - return $item->isAbstract(); - })); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.php deleted file mode 100644 index b3953485c0..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.php +++ /dev/null @@ -1,125 +0,0 @@ - - */ -final class ReflectionParameterCollection extends AbstractReflectionCollection -{ - /** - * @param ReflectionParameter[] $reflectionParameters - */ - public static function fromReflectionParameters(array $reflectionParameters): self - { - $parameters = []; - foreach ($reflectionParameters as $reflectionParameter) { - $parameters[$reflectionParameter->name()] = $reflectionParameter; - } - - return new self($parameters); - } - - public static function fromMethodDeclaration(ServiceLocator $serviceLocator, MethodDeclaration $method, ReflectionMethod $reflectionMethod): self - { - $items = []; - - /** @phpstan-ignore-next-line */ - if ($method->parameters) { - $index = 0; - foreach ($method->parameters->getElements() as $parameter) { - $items[$parameter->getName()] = new ReflectionParameter( - $serviceLocator, - $reflectionMethod, - $parameter, - $index++ - ); - } - } - - - return new static($items); - } - - public static function fromFunctionDeclaration(ServiceLocator $serviceLocator, FunctionDeclaration $functionDeclaration, ReflectionFunction $reflectionFunction): self - { - $items = []; - - /** - * @phpstan-ignore-next-line - */ - if ($functionDeclaration->parameters) { - $index = 0; - foreach ($functionDeclaration->parameters->getElements() as $parameter) { - $items[$parameter->getName()] = new ReflectionParameter( - $serviceLocator, - $reflectionFunction, - $parameter, - $index++ - ); - } - } - - - return new static($items); - } - - public function promoted(): PhpactorReflectionParameterCollection - { - return new self(array_filter($this->items, function (PhpactorReflectionParameter $parameter) { - return $parameter->isPromoted(); - })); - } - - public function notPromoted(): PhpactorReflectionParameterCollection - { - return new self(array_filter($this->items, function (PhpactorReflectionParameter $parameter) { - return !$parameter->isPromoted(); - })); - } - - public function add(PhpactorReflectionParameter $parameter): void - { - $this->items[$parameter->name()] = $parameter; - } - - public function at(int $index): ?PhpactorReflectionParameter - { - $offset = 0; - foreach ($this->items as $item) { - if ($offset++ === $index) { - return $item; - } - } - - return null; - } - /** - * @return Types - */ - public function types(): Types - { - return new Types(array_map( - static fn (PhpactorReflectionParameter $parameter) => $parameter->type(), - $this->items - )); - } - - public function passedByReference(): PhpactorReflectionParameterCollection - { - return new self(array_filter($this->items, function (PhpactorReflectionParameter $parameter) { - return $parameter->byReference(); - })); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionPropertyCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionPropertyCollection.php deleted file mode 100644 index ee3d23d890..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionPropertyCollection.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -final class ReflectionPropertyCollection extends HomogeneousReflectionMemberCollection -{ - /** - * @param PhpactorReflectionProperty[] $properties - */ - public static function fromReflectionProperties(array $properties): CoreReflectionPropertyCollection - { - $items = []; - foreach ($properties as $property) { - $items[$property->name()] = $property; - } - - return new self($items); - } -} diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php deleted file mode 100644 index 4f3265a28e..0000000000 --- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php +++ /dev/null @@ -1,87 +0,0 @@ - - */ -class ReflectionTraitCollection extends AbstractReflectionCollection -{ - public static function fromClassDeclaration(ServiceLocator $serviceLocator, ClassDeclaration $class): self - { - $items = []; - foreach ($class->classMembers->classMemberDeclarations as $memberDeclaration) { - if (false === $memberDeclaration instanceof TraitUseClause) { - continue; - } - - if ($memberDeclaration->traitNameList === null) { - continue; - } - - foreach ($memberDeclaration->traitNameList->getValues() as $traitName) { - $traitName = TolerantQualifiedNameResolver::getResolvedName($traitName); - try { - $items[(string) $traitName] = $serviceLocator->reflector()->reflectTrait(ClassName::fromString($traitName)); - } catch (NotFound) { - } - } - } - - return new self($items); - } - - /** - * @param array $visited - */ - public static function fromTraitDeclaration(ServiceLocator $serviceLocator, TraitDeclaration $traitDeclaration, array $visited = []): self - { - $items = []; - foreach ($traitDeclaration->traitMembers->traitMemberDeclarations as $memberDeclaration) { - if (false === $memberDeclaration instanceof TraitUseClause) { - continue; - } - - foreach ($memberDeclaration->traitNameList->getValues() as $traitName) { - $traitName = TolerantQualifiedNameResolver::getResolvedName($traitName); - try { - $items[(string) $traitName] = $serviceLocator->reflector()->reflectTrait(ClassName::fromString($traitName), $visited); - } catch (NotFound) { - } - } - } - - return new self($items); - } - - public static function fromEnumDeclaration(ServiceLocator $serviceLocator, EnumDeclaration $enumDeclaration): ReflectionTraitCollection - { - $items = []; - /** @phpstan-ignore-next-line not trusting TP */ - foreach ($enumDeclaration?->enumMembers?->enumMemberDeclarations ?? [] as $memberDeclaration) { - if (false === $memberDeclaration instanceof TraitUseClause) { - continue; - } - - foreach ($memberDeclaration->traitNameList->getValues() as $traitName) { - $traitName = TolerantQualifiedNameResolver::getResolvedName($traitName); - try { - $items[(string) $traitName] = $serviceLocator->reflector()->reflectTrait(ClassName::fromString($traitName)); - } catch (NotFound) { - } - } - } - - return new self($items); - } -} diff --git a/lib/WorseReflection/Core/Reflection/ReflectionArgument.php b/lib/WorseReflection/Core/Reflection/ReflectionArgument.php deleted file mode 100644 index 8cbc6e83cc..0000000000 --- a/lib/WorseReflection/Core/Reflection/ReflectionArgument.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function interfaces(): ReflectionInterfaceCollection; - - public function traits(): ReflectionTraitCollection; - - public function memberListPosition(): ByteOffsetRange; - - public function isFinal(): bool; -} diff --git a/lib/WorseReflection/Core/Reflection/ReflectionClassLike.php b/lib/WorseReflection/Core/Reflection/ReflectionClassLike.php deleted file mode 100644 index 6d1b406f71..0000000000 --- a/lib/WorseReflection/Core/Reflection/ReflectionClassLike.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ - public function members(): ReflectionMemberCollection; - - /** - * @return ReflectionMemberCollection - */ - public function ownMembers(): ReflectionMemberCollection; - - public function sourceCode(): TextDocument; - - public function isInstanceOf(ClassName $className): bool; - - public function isConcrete(): bool; - - public function docblock(): DocBlock; - - public function deprecation(): Deprecation; - - public function templateMap(): TemplateMap; - - public function type(): ReflectedClassType; - - public function classLikeType(): string; - - public function constants(): ReflectionConstantCollection; -} diff --git a/lib/WorseReflection/Core/Reflection/ReflectionConstant.php b/lib/WorseReflection/Core/Reflection/ReflectionConstant.php deleted file mode 100644 index 55560070f3..0000000000 --- a/lib/WorseReflection/Core/Reflection/ReflectionConstant.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ - public function parameters(): ReflectionParameterCollection; - - public function body(): NodeText; - - public function position(): ByteOffsetRange; - - public function frame(): Frame; - - public function docblock(): DocBlock; - - public function scope(): ReflectionScope; - - public function inferredType(): Type; - - public function type(): Type; -} diff --git a/lib/WorseReflection/Core/Reflection/ReflectionInterface.php b/lib/WorseReflection/Core/Reflection/ReflectionInterface.php deleted file mode 100644 index fc4f852241..0000000000 --- a/lib/WorseReflection/Core/Reflection/ReflectionInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -getDocblockTypeFromFunction($this->function), - $this->function->type() - ); - } - - private function getDocblockTypeFromFunction(ReflectionFunction $function): Type - { - return $function->docblock()->returnType(); - } -} diff --git a/lib/WorseReflection/Core/Reflection/TypeResolver/MethodTypeResolver.php b/lib/WorseReflection/Core/Reflection/TypeResolver/MethodTypeResolver.php deleted file mode 100644 index b0b00f977f..0000000000 --- a/lib/WorseReflection/Core/Reflection/TypeResolver/MethodTypeResolver.php +++ /dev/null @@ -1,87 +0,0 @@ -getDocblockTypesFromClassOrMethod($this->method); - - if (($resolvedType->isDefined())) { - return $resolvedType; - } - - $resolvedType = $this->getTypesFromParentClass($contextClass); - - if (($resolvedType->isDefined())) { - return $resolvedType; - } - - return $this->getTypesFromInterfaces($contextClass); - } - - private function getDocblockTypesFromClassOrMethod(ReflectionMethod $method): Type - { - $classLike = $method->class(); - $classMethodOverride = $classLike->docblock()->methodType($method->name()); - - if (($classMethodOverride->isDefined())) { - return $classMethodOverride; - } - $returnType = $method->docblock()->returnType(); - $aliased = $classLike->docblock()->typeAliases()->forType($returnType); - if ($aliased) { - return $aliased; - } - - // no static support here - return $returnType; - } - - private function getTypesFromParentClass(ReflectionClassLike $reflectionClassLike): Type - { - $methodClass = $this->method->declaringClass(); - - if (!$methodClass instanceof ReflectionClass) { - return TypeFactory::undefined(); - } - - if (null === $methodClass->parent()) { - return TypeFactory::undefined(); - } - - $parentClass = $methodClass->parent(); - - if (false === $parentClass->methods($reflectionClassLike)->has($this->method->name())) { - return TypeFactory::undefined(); - } - - return $parentClass->methods($reflectionClassLike)->get($this->method->name())->inferredType(); - } - - private function getTypesFromInterfaces(ReflectionClassLike $reflectionClassLike): Type - { - if (!$reflectionClassLike instanceof ReflectionClass) { - return TypeFactory::undefined(); - } - - foreach ($reflectionClassLike->interfaces() as $interface) { - if ($interface->methods()->has($this->method->name())) { - return $interface->methods()->get($this->method->name())->inferredType(); - } - } - - return TypeFactory::undefined(); - } -} diff --git a/lib/WorseReflection/Core/Reflection/TypeResolver/ParameterTypeResolver.php b/lib/WorseReflection/Core/Reflection/TypeResolver/ParameterTypeResolver.php deleted file mode 100644 index 22626c414b..0000000000 --- a/lib/WorseReflection/Core/Reflection/TypeResolver/ParameterTypeResolver.php +++ /dev/null @@ -1,96 +0,0 @@ -parameter->functionLike(); - - $type = $this->resolveType($functionLike, $this->parameter); - - return $type; - } - - public function resolveType(ReflectionFunctionLike $functionLike, ReflectionParameter $parameter): Type - { - if (!$functionLike instanceof ReflectionMethod) { - $docblock = $functionLike->docblock(); - $docblockType = $docblock->parameterType($parameter->name()); - return TypeUtil::firstDefined($docblockType, $parameter->type()); - } - - $hierarchy = (new ClassHierarchyResolver( - ClassHierarchyResolver::INCLUDE_PARENT | ClassHierarchyResolver::INCLUDE_INTERFACE - ))->resolve($functionLike->class()); - - foreach (array_reverse($hierarchy) as $classLike) { - // find declaring class - if (!$classLike->methods()->has($functionLike->name())) { - continue; - } - $docblock = $classLike->methods()->get($functionLike->name())->docblock(); - $type = $docblock->parameterType($parameter->name()); - - if (!$type->isDefined()) { - continue; - } - - $aliased = $classLike->docblock()->typeAliases()->forType($type); - - if ($aliased) { - return $aliased; - } - - - return $this->resolveGenericType($functionLike->class(), $classLike, $type); - } - - return $parameter->type(); - } - - private function resolveGenericType( - ReflectionClassLike $topClass, - ReflectionClassLike $bottomClass, - Type $type - ): Type { - // potential optimisation - if (false === TypeUtil::contains(ClassLikeType::class, $type)) { - return $type; - } - - $topTemplateMap = $topClass->docblock()->templateMap(); - if ($topTemplateMap->has($type->__toString())) { - return $type; - } - $map = $this->mapResolver->resolveClassTemplateMap($topClass->type(), $bottomClass->name(), []); - if (!$map) { - return $type; - } - if ($map->has($type->short())) { - $t = $map->get($type->short()); - if (!$t->isDefined()) { - return $type; - } - return $t; - } - return $type; - } -} diff --git a/lib/WorseReflection/Core/Reflection/TypeResolver/PropertyTypeResolver.php b/lib/WorseReflection/Core/Reflection/TypeResolver/PropertyTypeResolver.php deleted file mode 100644 index fe12f82e18..0000000000 --- a/lib/WorseReflection/Core/Reflection/TypeResolver/PropertyTypeResolver.php +++ /dev/null @@ -1,48 +0,0 @@ -getDocblockType(); - - if (false === ($docblockType->isDefined())) { - $docblockType = $this->getDocblockTypesFromClass(); - } - - if (($docblockType->isDefined())) { - return $docblockType; - } - - if ($this->property instanceof ReflectionPromotedProperty) { - $paramType = $this->property->class()->methods()->get('__construct')->docblock()->parameterType( - $this->property->name() - ); - if ($paramType->isDefined()) { - return $paramType; - } - } - - return $this->property->type(); - } - - private function getDocblockType(): Type - { - return $this->property->docblock()->vars()->type(); - } - - private function getDocblockTypesFromClass(): Type - { - return $this->property->class()->docblock()->propertyType($this->property->name()); - } -} diff --git a/lib/WorseReflection/Core/Reflector/ClassReflector.php b/lib/WorseReflection/Core/Reflector/ClassReflector.php deleted file mode 100644 index 6c55e73cdc..0000000000 --- a/lib/WorseReflection/Core/Reflector/ClassReflector.php +++ /dev/null @@ -1,53 +0,0 @@ - $visited - */ - public function reflectInterface($className, array $visited = []): ReflectionInterface; - - /** - * Reflect a trait - * @param Name|string $className - * @param array $visited - */ - public function reflectTrait($className, array $visited = []): ReflectionTrait; - - /** - * Reflect an enum - * - * @param Name|string $className - */ - public function reflectEnum($className): ReflectionEnum; - - /** - * Reflect a class, trait, enum or interface by its name. - * @param Name|string $className - * @param array $visited - */ - public function reflectClassLike($className, array $visited = []): ReflectionClassLike; - - /** - * @param string|Name $className - */ - public function sourceCodeForClassLike($className): TextDocument; -} diff --git a/lib/WorseReflection/Core/Reflector/ClassReflector/MemonizedReflector.php b/lib/WorseReflection/Core/Reflector/ClassReflector/MemonizedReflector.php deleted file mode 100644 index edd43c9b8d..0000000000 --- a/lib/WorseReflection/Core/Reflector/ClassReflector/MemonizedReflector.php +++ /dev/null @@ -1,133 +0,0 @@ -innerReflector = $classReflector; - } - - public function reflectClass($className): ReflectionClass - { - return $this->getOrSet(self::CLASS_PREFIX.$className, function () use ($className) { - return $this->classReflector->reflectClass($className); - }); - } - - public function reflectInterface($className, array $visited = []): ReflectionInterface - { - return $this->getOrSet(self::INTERFACE_PREFIX.$className, function () use ($className, $visited) { - return $this->classReflector->reflectInterface($className, $visited); - }); - } - - public function reflectTrait($className, array $visited = []): ReflectionTrait - { - return $this->getOrSet(self::TRAIT_PREFIX.$className, function () use ($className, $visited) { - return $this->classReflector->reflectTrait($className, $visited); - }); - } - - public function reflectEnum($className): ReflectionEnum - { - return $this->getOrSet(self::ENUM_PREFIX.$className, function () use ($className) { - return $this->classReflector->reflectEnum($className); - }); - } - - public function reflectClassLike($className, $visited = []): ReflectionClassLike - { - if (isset($visited[(string)$className])) { - throw new CycleDetected(sprintf( - 'Cycle detected while resolving class "%s"', - (string)$className - )); - } - return $this->getOrSet(self::CLASS_LIKE_PREFIX.(string)$className, function () use ($className, $visited) { - return $this->classReflector->reflectClassLike($className, $visited); - }); - } - - public function reflectFunction($name): ReflectionFunction - { - return $this->getOrSet(self::FUNC_PREFIX.$name, function () use ($name) { - return $this->functionReflector->reflectFunction($name); - }); - } - - public function sourceCodeForFunction($name): TextDocument - { - return $this->getOrSet(self::FUNC_PREFIX.'source_code'.$name, function () use ($name): TextDocument { - return $this->functionReflector->sourceCodeForFunction($name); - }); - } - - public function sourceCodeForClassLike($name): TextDocument - { - return $this->getOrSet(self::CLASS_LIKE_PREFIX.'source_code'.$name, function () use ($name) { - return $this->classReflector->sourceCodeForClassLike($name); - }); - } - - public function reflectConstant($name): ReflectionDeclaredConstant - { - return $this->constantReflector->reflectConstant($name); - } - - public function sourceCodeForConstant($name): TextDocument - { - return $this->constantReflector->sourceCodeForConstant($name); - } - - /** - * @template T - * @param Closure(): T $closure - * @return T - */ - private function getOrSet(string $key, Closure $closure) - { - $closure = function () use ($closure) { - try { - return $closure(); - } catch (NotFound $e) { - return $e; - } - }; - $result = $this->cache->getOrSet($key, $closure); - if ($result instanceof NotFound) { - throw $result; - } - return $result; - } -} diff --git a/lib/WorseReflection/Core/Reflector/CompositeReflector.php b/lib/WorseReflection/Core/Reflector/CompositeReflector.php deleted file mode 100644 index d72d859d0d..0000000000 --- a/lib/WorseReflection/Core/Reflector/CompositeReflector.php +++ /dev/null @@ -1,136 +0,0 @@ -classReflector->reflectClass($className); - } - - public function reflectInterface($className, array $visited = []): ReflectionInterface - { - return $this->classReflector->reflectInterface($className, $visited); - } - - public function reflectTrait($className, array $visited = []): ReflectionTrait - { - return $this->classReflector->reflectTrait($className, $visited); - } - - public function reflectEnum($className): ReflectionEnum - { - return $this->classReflector->reflectEnum($className); - } - - public function reflectClassLike($className, $visited = []): ReflectionClassLike - { - return $this->classReflector->reflectClassLike($className, $visited); - } - - public function reflectClassesIn(TextDocument $sourceCode, array $visited = []): ReflectionClassLikeCollection - { - return $this->sourceCodeReflector->reflectClassesIn($sourceCode, $visited); - } - - public function reflectOffset(TextDocument $sourceCode, $offset): ReflectionOffset - { - return $this->sourceCodeReflector->reflectOffset($sourceCode, $offset); - } - - public function reflectMethodCall(TextDocument $sourceCode, $offset): ReflectionMethodCall - { - return $this->sourceCodeReflector->reflectMethodCall($sourceCode, $offset); - } - - public function reflectFunctionsIn(TextDocument $sourceCode): ReflectionFunctionCollection - { - return $this->sourceCodeReflector->reflectFunctionsIn($sourceCode); - } - - public function navigate(TextDocument $sourceCode): ReflectionNavigation - { - return $this->sourceCodeReflector->navigate($sourceCode); - } - - public function reflectFunction($name): ReflectionFunction - { - return $this->functionReflector->reflectFunction($name); - } - - public function sourceCodeForClassLike($className): TextDocument - { - return $this->classReflector->sourceCodeForClassLike($className); - } - - public function sourceCodeForFunction($name): TextDocument - { - return $this->functionReflector->sourceCodeForFunction($name); - } - - public function diagnostics(TextDocument $sourceCode): Promise - { - return $this->sourceCodeReflector->diagnostics($sourceCode); - } - - public function reflectNodeContext(Node $node): NodeContext - { - return $this->sourceCodeReflector->reflectNodeContext($node); - } - - public function reflectNode(TextDocument $sourceCode, $offset): ReflectionNode - { - return $this->sourceCodeReflector->reflectNode($sourceCode, $offset); - } - - public function reflectConstantsIn(TextDocument $sourceCode): ReflectionDeclaredConstantCollection - { - return $this->sourceCodeReflector->reflectConstantsIn($sourceCode); - } - - public function reflectConstant($name): ReflectionDeclaredConstant - { - return $this->constantReflector->reflectConstant($name); - } - - public function sourceCodeForConstant($name): TextDocument - { - return $this->constantReflector->sourceCodeForConstant($name); - } - - public function walk(TextDocument $sourceCode, Walker $walker): Generator - { - return $this->sourceCodeReflector->walk($sourceCode, $walker); - } -} diff --git a/lib/WorseReflection/Core/Reflector/ConstantReflector.php b/lib/WorseReflection/Core/Reflector/ConstantReflector.php deleted file mode 100644 index 983a5231d8..0000000000 --- a/lib/WorseReflection/Core/Reflector/ConstantReflector.php +++ /dev/null @@ -1,20 +0,0 @@ -reflectClassLike($className); - - if (false === $class instanceof ReflectionClass) { - throw new ClassNotFound(sprintf( - '"%s" is not a class, it is a "%s"', - $className->full(), - get_class($class) - )); - } - - return $class; - } - - /** - * Reflect an interface. - * - * @param ClassName|string $className - * - * @throws ClassNotFound If the class was not found, or the found class - * was not a trait. - */ - public function reflectInterface($className, array $visited = []): ReflectionInterface - { - $className = ClassName::fromUnknown($className); - - $class = $this->reflectClassLike($className, $visited); - - if (false === $class instanceof ReflectionInterface) { - throw new ClassNotFound(sprintf( - '"%s" is not an interface, it is a "%s"', - $className->full(), - get_class($class) - )); - } - - return $class; - } - - /** - * Reflect a trait - * - * @param ClassName|string $className - * - * @throws ClassNotFound If the class was not found, or the found class - * was not a trait. - */ - public function reflectTrait($className, array $visited = []): ReflectionTrait - { - $className = ClassName::fromUnknown($className); - - $class = $this->reflectClassLike($className, $visited); - - if (false === $class instanceof ReflectionTrait) { - throw new ClassNotFound(sprintf( - '"%s" is not a trait, it is a "%s"', - $className->full(), - get_class($class) - )); - } - - return $class; - } - - public function reflectEnum($className): ReflectionEnum - { - $className = ClassName::fromUnknown($className); - - $class = $this->reflectClassLike($className); - - if (false === $class instanceof ReflectionEnum) { - throw new ClassNotFound(sprintf( - '"%s" is not an enum, it is a "%s"', - $className->full(), - get_class($class) - )); - } - - return $class; - } - - /** - * Reflect a class, trait or interface by its name. - * - * If the class it not found an exception will be thrown. - * - * @throws ClassNotFound - */ - public function reflectClassLike($className, array $visited = []): ReflectionClassLike - { - $className = ClassName::fromUnknown($className); - - if (isset($visited[$className->__toString()])) { - throw new CycleDetected(sprintf( - 'Cycle detected while resolving class "%s"', - $className->full() - )); - } - $visited[$className->__toString()] = true; - - $source = $this->sourceLocator->locate($className); - $classes = $this->reflectClassesIn($source, $visited); - - if (false === $classes->has((string) $className)) { - throw new ClassNotFound(sprintf( - 'Unable to locate class "%s"', - $className->full() - )); - } - - $class = $classes->get((string) $className); - - return $class; - } - - /** - * Reflect all classes (or class-likes) in the given source code. - */ - public function reflectClassesIn(TextDocument $sourceCode, array $visited = []): ReflectionClassLikeCollection - { - return $this->sourceReflector->reflectClassesIn($sourceCode, $visited); - } - - /** - * Return the information for the given offset in the given file, including the value - * and type of a variable and the frame information. - */ - public function reflectOffset(TextDocument $sourceCode, ByteOffset|int $offset): ReflectionOffset - { - return $this->sourceReflector->reflectOffset($sourceCode, $offset); - } - - public function reflectMethodCall(TextDocument $sourceCode, ByteOffset|int $offset): ReflectionMethodCall - { - return $this->sourceReflector->reflectMethodCall($sourceCode, $offset); - } - - public function reflectFunctionsIn(TextDocument $sourceCode): ReflectionFunctionCollection - { - return $this->sourceReflector->reflectFunctionsIn($sourceCode); - } - - public function reflectConstantsIn(TextDocument $source): ReflectionDeclaredConstantCollection - { - return $this->sourceReflector->reflectConstantsIn($source); - } - - public function navigate(TextDocument $sourceCode): ReflectionNavigation - { - return $this->sourceReflector->navigate($sourceCode); - } - - /** - * @param Name|string $name - */ - public function reflectFunction($name): ReflectionFunction - { - $name = Name::fromUnknown($name); - - // if the source is not found, fallback to the global - // function - try { - $source = $this->sourceLocator->locate($name); - } catch (NotFound) { - $name = Name::fromString($name->short()); - $source = $this->sourceLocator->locate($name); - } - - $functions = $this->reflectFunctionsIn($source); - - if (false === $functions->has((string) $name)) { - $name = Name::fromString($name->short()); - $source = $this->sourceLocator->locate($name); - $functions = $this->reflectFunctionsIn($source); - if (false === $functions->has($name)) { - throw new FunctionNotFound(sprintf( - 'Unable to locate function "%s"', - $name - )); - } - } - - $function = $functions->get((string) $name); - - return $function; - } - - public function sourceCodeForFunction($name): TextDocument - { - return $this->sourceLocator->locate(Name::fromUnknown($name)); - } - - public function sourceCodeForClassLike($name): TextDocument - { - return $this->sourceLocator->locate(Name::fromUnknown($name)); - } - - public function diagnostics(TextDocument $sourceCode): Promise - { - return $this->sourceReflector->diagnostics($sourceCode); - } - - public function reflectNodeContext(Node $node): NodeContext - { - return $this->sourceReflector->reflectNodeContext($node); - } - - public function reflectNode($sourceCode, $offset): ReflectionNode - { - return $this->sourceReflector->reflectNode($sourceCode, $offset); - } - - public function walk(TextDocument $sourceCode, Walker $walker): Generator - { - return $this->sourceReflector->walk($sourceCode, $walker); - } - - public function reflectConstant($name): ReflectionDeclaredConstant - { - $name = Name::fromUnknown($name); - - // if the source is not found, fallback to the global - // function - try { - $source = $this->sourceLocator->locate($name); - } catch (NotFound) { - $name = Name::fromString($name->short()); - $source = $this->sourceLocator->locate($name); - } - - $constants = $this->reflectConstantsIn($source); - - if (false === $constants->has((string) $name)) { - $name = Name::fromString($name->short()); - $source = $this->sourceLocator->locate($name); - $constants = $this->reflectConstantsIn($source); - if (false === $constants->has($name)) { - throw new ConstantNotFound(sprintf( - 'Unable to locate constant "%s"', - $name - )); - } - } - - return $constants->get((string) $name); - } - - public function sourceCodeForConstant($name): TextDocument - { - $name = Name::fromUnknown($name); - - // if the source is not found, fallback to the global - // function - try { - $source = $this->sourceLocator->locate($name); - } catch (NotFound) { - $name = Name::fromString($name->short()); - $source = $this->sourceLocator->locate($name); - } - - return $source; - } -} diff --git a/lib/WorseReflection/Core/Reflector/FunctionReflector.php b/lib/WorseReflection/Core/Reflector/FunctionReflector.php deleted file mode 100644 index 0790b8f8df..0000000000 --- a/lib/WorseReflection/Core/Reflector/FunctionReflector.php +++ /dev/null @@ -1,20 +0,0 @@ -locator->pushSourceCode(TextDocumentBuilder::fromUnknown($sourceCode)); - - $collection = $this->innerReflector->reflectClassesIn($sourceCode, $visited); - - return $collection; - } - - public function reflectOffset(TextDocument $sourceCode, $offset): ReflectionOffset - { - $this->locator->pushSourceCode($sourceCode); - - $offset = $this->innerReflector->reflectOffset($sourceCode, $offset); - - return $offset; - } - - public function reflectMethodCall(TextDocument $sourceCode, $offset): ReflectionMethodCall - { - $this->locator->pushSourceCode($sourceCode); - - $offset = $this->innerReflector->reflectMethodCall($sourceCode, $offset); - - return $offset; - } - - public function reflectFunctionsIn(TextDocument $sourceCode): ReflectionFunctionCollection - { - $this->locator->pushSourceCode($sourceCode); - - $offset = $this->innerReflector->reflectFunctionsIn($sourceCode); - - return $offset; - } - - public function navigate(TextDocument $sourceCode): ReflectionNavigation - { - return $this->innerReflector->navigate($sourceCode); - } - - public function diagnostics(TextDocument $sourceCode): Promise - { - $this->locator->pushSourceCode($sourceCode); - return $this->innerReflector->diagnostics($sourceCode); - } - - public function reflectNode(TextDocument $sourceCode, $offset): ReflectionNode - { - return $this->innerReflector->reflectNode($sourceCode, $offset); - } - - public function reflectConstantsIn(TextDocument $sourceCode): ReflectionDeclaredConstantCollection - { - return $this->innerReflector->reflectConstantsIn($sourceCode); - } - - public function walk(TextDocument $sourceCode, Walker $walker): Generator - { - return $this->innerReflector->walk($sourceCode, $walker); - } - - public function reflectNodeContext(Node $node): NodeContext - { - return $this->innerReflector->reflectNodeContext($node); - } -} diff --git a/lib/WorseReflection/Core/Reflector/SourceCodeReflector.php b/lib/WorseReflection/Core/Reflector/SourceCodeReflector.php deleted file mode 100644 index c7fc9d0206..0000000000 --- a/lib/WorseReflection/Core/Reflector/SourceCodeReflector.php +++ /dev/null @@ -1,81 +0,0 @@ - $visited - */ - public function reflectClassesIn( - TextDocument $sourceCode, - array $visited = [] - ): ReflectionClassLikeCollection; - - /** - * Reflect all functions in the given source code. - * - * @return ReflectionFunctionCollection - */ - public function reflectFunctionsIn(TextDocument $sourceCode): ReflectionFunctionCollection; - - /** - * Return the information for the given offset in the given file, including the value - * and type of a variable and the frame information. - */ - public function reflectOffset( - TextDocument $sourceCode, - ByteOffset|int $offset - ): ReflectionOffset; - - public function reflectMethodCall( - TextDocument $sourceCode, - ByteOffset|int $offset - ): ReflectionMethodCall; - - public function navigate(TextDocument $sourceCode): ReflectionNavigation; - - /** - * @return Promise> - */ - public function diagnostics(TextDocument $sourceCode): Promise; - - public function reflectNode( - TextDocument $sourceCode, - ByteOffset|int $offset - ): ReflectionNode; - - public function reflectNodeContext(Node $node): NodeContext; - - public function reflectConstantsIn( - TextDocument $sourceCode - ): ReflectionDeclaredConstantCollection; - - /** - * Walk the given source code's AST with the provided walker. - * The walker is able to resolve nodes and has access to the frame. - * @return Generator - */ - public function walk(TextDocument $sourceCode, Walker $walker): Generator; -} diff --git a/lib/WorseReflection/Core/Reflector/SourceCodeReflectorFactory.php b/lib/WorseReflection/Core/Reflector/SourceCodeReflectorFactory.php deleted file mode 100644 index 2c38af185a..0000000000 --- a/lib/WorseReflection/Core/Reflector/SourceCodeReflectorFactory.php +++ /dev/null @@ -1,10 +0,0 @@ -create($this); - - if ($enableContextualLocation) { - // functions are located as well as classes, otherwise functions - // declared in the contextual source code (e.g. the file being - // analysed) are reported as not-found. - $temporarySourceLocator = new TemporarySourceLocator($sourceReflector, locateFunctions: true); - $sourceLocator = new ChainSourceLocator([ - $temporarySourceLocator, - $sourceLocator, - ], $logger); - $sourceReflector = new ContextualSourceCodeReflector($sourceReflector, $temporarySourceLocator); - } - - $coreReflector = new CoreReflector($sourceReflector, $sourceLocator); - - if (!$cache instanceof NullCache) { - $coreReflector = new MemonizedReflector($coreReflector, $coreReflector, $coreReflector, $cache); - } - - $this->reflector = new CompositeReflector( - $coreReflector, - $sourceReflector, - $coreReflector, - $coreReflector - ); - - $this->sourceLocator = $sourceLocator; - $this->docblockFactory = new DocblockParserFactory($this->reflector); - if (!$cache instanceof NullCache) { - $this->docblockFactory = new CachedParserFactory($this->docblockFactory, $cache); - } - $this->logger = $logger; - - $this->nameResolver = new NodeToTypeConverter($this->reflector, $this->logger); - $this->cache = $cache; - } - - public function reflector(): Reflector - { - return $this->reflector; - } - - public function logger(): LoggerInterface - { - return $this->logger; - } - - public function sourceLocator(): SourceCodeLocator - { - return $this->sourceLocator; - } - - public function docblockFactory(): DocBlockFactory - { - return $this->docblockFactory; - } - - public function nodeContextResolver(): NodeContextResolver - { - return new NodeContextResolver( - $this->reflector, - $this->docblockFactory, - $this->logger, - // use a cache which is local to this resolver instance - // this avoids issues with stale cache data while also - // providing memoised caching for this resolver instance. - new StaticCache(), - (new DefaultResolverFactory( - $this->reflector, - $this->nameResolver, - new GenericMapResolver($this->reflector), - new NodeContextFromMemberAccess( - new GenericMapResolver($this->reflector), - $this->memberContextResolvers - ) - ))->createResolvers(), - ); - } - - public function frameBuilder(?NodeContextResolver $resolver = null): FrameResolver - { - return FrameResolver::create( - $resolver ?? $this->nodeContextResolver(), - array_merge([ - new FunctionLikeWalker(), - new PassThroughWalker(), - new VariableWalker($this->docblockFactory), - ], $this->frameWalkers), - $this->cacheForDocument, - ); - } - - public function methodProviders(): ReflectionMemberProvider - { - return new ChainReflectionMemberProvider(...$this->methodProviders); - } - - public function cache(): Cache - { - return $this->cache; - } - - public function cacheForDocument(): CacheForDocument - { - return $this->cacheForDocument; - } - - public function newDiagnosticsWalker(): DiagnosticsWalker - { - return new DiagnosticsWalker($this->diagnosticProviders); - } - - public function stubReflector(): Reflector - { - return ReflectorBuilder::create() - ->addLocator($this->sourceLocator) - ->addMemberProvider(new DocblockMemberProvider()) - ->build(); - } -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator.php b/lib/WorseReflection/Core/SourceCodeLocator.php deleted file mode 100644 index b2ace5a21a..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ - private ?array $map = null; - - public function __construct( - private Reflector $reflector, - private string $path - ) { - } - - public function locate(Name $name): TextDocument - { - $map = $this->map(); - - if (isset($map[(string) $name])) { - return TextDocumentBuilder::fromUri($map[(string) $name])->build(); - } - - throw new SourceNotFound(sprintf( - 'Could not find source for "%s" in stub directory "%s"', - (string) $name, - $this->path - )); - } - - /** - * @return array - */ - private function map(): array - { - if (null !== $this->map) { - return $this->map; - } - - $this->buildCache(); - return $this->map(); - } - - private function buildCache(): void - { - $map = []; - foreach ($this->fileIterator() as $file) { - if ($file->getExtension() !== 'php' || $file->isDir()) { - continue; - } - - $map = $this->buildClassMap($file, $map); - $map = $this->buildFunctionMap($file, $map); - } - - $this->map = $map; - } - - /** - * @return RecursiveIteratorIterator - */ - private function fileIterator(): RecursiveIteratorIterator - { - return new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($this->path, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::SELF_FIRST - ); - } - - /** - * @param array $map - * @return array - */ - private function buildClassMap(SplFileInfo $file, array $map): array - { - $functions = $this->reflector->reflectClassesIn( - TextDocumentBuilder::fromUri($file)->build() - ); - - foreach ($functions as $function) { - $map[(string) $function->name()] = (string) $file; - } - - return $map; - } - - /** - * @param array $map - * @return array - */ - private function buildFunctionMap(SplFileInfo $file, array $map): array - { - $functions = $this->reflector->reflectFunctionsIn( - TextDocumentBuilder::fromUri($file)->build() - ); - - foreach ($functions as $function) { - $map[(string) $function->name()] = (string) $file; - } - - return $map; - } -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator/ChainSourceLocator.php b/lib/WorseReflection/Core/SourceCodeLocator/ChainSourceLocator.php deleted file mode 100644 index 1051e8a0e3..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/ChainSourceLocator.php +++ /dev/null @@ -1,71 +0,0 @@ -add($sourceLocator); - } - } - - public function locate(Name $name): TextDocument - { - $exception = new SourceNotFound( - 'No source locators registered with chain loader '. - '(or source locator did not throw SourceNotFound exception' - ); - - foreach ($this->locators as $locator) { - $start = microtime(true); - try { - $source = $locator->locate($name); - $this->logger->debug(sprintf( - ' OK [%s] "%s" with locator "%s"', - number_format(microtime(true) - $start, 4), - $name, - get_class($locator) - )); - return $source; - } catch (SourceNotFound $e) { - $this->logger->debug(sprintf( - 'NOK [%s] "%s" with locator "%s" : %s', - number_format(microtime(true) - $start, 4), - $name, - get_class($locator), - $e->getMessage() - )); - $exception = new SourceNotFound(sprintf( - 'Could not find source with "%s"', - (string) $name - ), 0, $e); - } - } - - throw $exception; - } - - private function add(SourceCodeLocator $locator): void - { - $this->locators[] = $locator; - } -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator/InternalLocator.php b/lib/WorseReflection/Core/SourceCodeLocator/InternalLocator.php deleted file mode 100644 index 785dbfea02..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/InternalLocator.php +++ /dev/null @@ -1,53 +0,0 @@ - $map - */ - public function __construct(private array $map) - { - } - - public static function forInternalStubs(): self - { - return new self([ - 'iterable' => __DIR__ . '/InternalStubs/Iterator.php', - 'Traversable' => __DIR__ . '/InternalStubs/Iterator.php', - 'IteratorAggregate' => __DIR__ . '/InternalStubs/Iterator.php', - 'Iterator' => __DIR__ . '/InternalStubs/Iterator.php', - 'UnitEnumCase' => __DIR__ . '/InternalStubs/Enum.php', - 'UnitEnum' => __DIR__ . '/InternalStubs/Enum.php', - 'BackedEnumCase' => __DIR__ . '/InternalStubs/Enum.php', - 'BackedEnum' => __DIR__ . '/InternalStubs/Enum.php', - 'Generator' => __DIR__ . '/InternalStubs/GenericTypes.php', - 'ArrayAccess' => __DIR__ . '/InternalStubs/GenericTypes.php', - 'ArrayObject' => __DIR__ . '/InternalStubs/GenericTypes.php', - 'Serializable' => __DIR__ . '/InternalStubs/GenericTypes.php', - 'WeakReference' => __DIR__ . '/InternalStubs/GenericTypes.php', - 'WeakMap' => __DIR__ . '/InternalStubs/GenericTypes.php', - ]); - } - - public function locate(Name $name): TextDocument - { - if (isset($this->map[$name->__toString()])) { - return TextDocumentBuilder::fromUri($this->map[$name->__toString()])->build(); - } - throw new SourceNotFound(sprintf( - 'Could not find internal stub for "%s"', - (string) $name - )); - } -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator/InternalStubs/Enum.php b/lib/WorseReflection/Core/SourceCodeLocator/InternalStubs/Enum.php deleted file mode 100644 index 60b0cfa07e..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/InternalStubs/Enum.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ -class Generator implements Traversable { - /** - * @return ?TValue Can return any type. - */ - public function current() {} - - /** - * @return void Any returned value is ignored. - */ - public function next() {} - - /** - * @return TKey scalar on success, or null on failure. - */ - public function key() {} - - /** - * @return bool The return value will be casted to boolean and then evaluated. - */ - public function valid() {} - - /** - * @return void Any returned value is ignored. - */ - public function rewind() {} - - /** - * @return TReturn Can return any type. - */ - public function getReturn() {} - - /** - * @param TSend $value - * @return ?TValue Can return any type. - */ - public function send($value) {} - - /** - * @return ?TValue Can return any type. - */ - public function throw(Throwable $exception) {} -} - -/** - * Interface to provide accessing objects as arrays. - * @link http://php.net/manual/en/class.arrayaccess.php - * - * @template TKey - * @template TValue - */ -interface ArrayAccess { - - /** - * Whether a offset exists - * @link http://php.net/manual/en/arrayaccess.offsetexists.php - * - * @param TKey $offset An offset to check for. - * @return bool true on success or false on failure. - * The return value will be casted to boolean if non-boolean was returned. - * - * @since 5.0.0 - */ - public function offsetExists($offset); - - /** - * Offset to retrieve - * @link http://php.net/manual/en/arrayaccess.offsetget.php - * - * @param TKey $offset The offset to retrieve. - * @return TValue|null Can return all value types. - * - * @since 5.0.0 - */ - public function offsetGet($offset); - - /** - * Offset to set - * @link http://php.net/manual/en/arrayaccess.offsetset.php - * - * @param TKey|null $offset The offset to assign the value to. - * @param TValue $value The value to set. - * @return void - * - * @since 5.0.0 - */ - public function offsetSet($offset, $value); - - /** - * Offset to unset - * @link http://php.net/manual/en/arrayaccess.offsetunset.php - * - * @param TKey $offset The offset to unset. - * @return void - * - * @since 5.0.0 - */ - public function offsetUnset($offset); -} - -/** - * This class allows objects to work as arrays. - * @link http://php.net/manual/en/class.arrayobject.php - * - * @template TKey - * @template TValue - * @template-implements IteratorAggregate - * @template-implements ArrayAccess - */ -class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable { - /** - * Properties of the object have their normal functionality when accessed as list (var_dump, foreach, etc.). - */ - const STD_PROP_LIST = 1; - - /** - * Entries can be accessed as properties (read and write). - */ - const ARRAY_AS_PROPS = 2; - - /** - * Construct a new array object - * @link http://php.net/manual/en/arrayobject.construct.php - * - * @param array|object $input The input parameter accepts an array or an Object. - * @param int $flags Flags to control the behaviour of the ArrayObject object. - * @param string $iterator_class Specify the class that will be used for iteration of the ArrayObject object. ArrayIterator is the default class used. - * - * @since 5.0.0 - */ - public function __construct($input = null, $flags = 0, $iterator_class = "ArrayIterator") { } - - /** - * Returns whether the requested index exists - * @link http://php.net/manual/en/arrayobject.offsetexists.php - * - * @param TKey $index The index being checked. - * @return bool true if the requested index exists, otherwise false - * - * @since 5.0.0 - */ - public function offsetExists($index) { } - - /** - * Returns the value at the specified index - * @link http://php.net/manual/en/arrayobject.offsetget.php - * - * @param TKey $index The index with the value. - * @return TValue The value at the specified index or false. - * - * @since 5.0.0 - */ - public function offsetGet($index) { } - - /** - * Sets the value at the specified index to newval - * @link http://php.net/manual/en/arrayobject.offsetset.php - * - * @param TKey $index The index being set. - * @param TValue $newval The new value for the index. - * @return void - * - * @since 5.0.0 - */ - public function offsetSet($index, $newval) { } - - /** - * Unsets the value at the specified index - * @link http://php.net/manual/en/arrayobject.offsetunset.php - * - * @param TKey $index The index being unset. - * @return void - * - * @since 5.0.0 - */ - public function offsetUnset($index) { } - - /** - * Appends the value - * @link http://php.net/manual/en/arrayobject.append.php - * - * @param TValue $value The value being appended. - * @return void - * - * @since 5.0.0 - */ - public function append($value) { } - - /** - * Creates a copy of the ArrayObject. - * @link http://php.net/manual/en/arrayobject.getarraycopy.php - * - * @return array a copy of the array. When the ArrayObject refers to an object - * an array of the public properties of that object will be returned. - * - * @since 5.0.0 - */ - public function getArrayCopy() { } - - /** - * Get the number of public properties in the ArrayObject - * When the ArrayObject is constructed from an array all properties are public. - * @link http://php.net/manual/en/arrayobject.count.php - * - * @return int The number of public properties in the ArrayObject. - * - * @since 5.0.0 - */ - public function count() { } - - /** - * Gets the behavior flags. - * @link http://php.net/manual/en/arrayobject.getflags.php - * - * @return int the behavior flags of the ArrayObject. - * - * @since 5.1.0 - */ - public function getFlags() { } - - /** - * Sets the behavior flags. - * - * It takes on either a bitmask, or named constants. Using named - * constants is strongly encouraged to ensure compatibility for future - * versions. - * - * The available behavior flags are listed below. The actual - * meanings of these flags are described in the - * predefined constants. - * - * - * ArrayObject behavior flags - * - * - * - * - * - * - * - * - * - * - * - * - *
valueconstant
1 - * ArrayObject::STD_PROP_LIST - *
2 - * ArrayObject::ARRAY_AS_PROPS - *
- * - * @link http://php.net/manual/en/arrayobject.setflags.php - * - * @param int $flags The new ArrayObject behavior. - * @return void - * - * @since 5.1.0 - */ - public function setFlags($flags) { } - - /** - * Sort the entries by value - * @link http://php.net/manual/en/arrayobject.asort.php - * - * @return void - * - * @since 5.2.0 - */ - public function asort() { } - - /** - * Sort the entries by key - * @link http://php.net/manual/en/arrayobject.ksort.php - * - * @return void - * - * @since 5.2.0 - */ - public function ksort() { } - - /** - * Sort the entries with a user-defined comparison function and maintain key association - * @link http://php.net/manual/en/arrayobject.uasort.php - * - * Function cmp_function should accept two - * parameters which will be filled by pairs of entries. - * The comparison function must return an integer less than, equal - * to, or greater than zero if the first argument is considered to - * be respectively less than, equal to, or greater than the - * second. - * - * @param callable(TValue, TValue):int $cmp_function - * @return void - * - * @since 5.2.0 - */ - public function uasort($cmp_function) { } - - /** - * Sort the entries by keys using a user-defined comparison function - * @link http://php.net/manual/en/arrayobject.uksort.php - * - * Function cmp_function should accept two - * parameters which will be filled by pairs of entry keys. - * The comparison function must return an integer less than, equal - * to, or greater than zero if the first argument is considered to - * be respectively less than, equal to, or greater than the - * second. - * - * @param callable(TKey, TKey):int $cmp_function The callable comparison function. - * @return void - * - * @since 5.2.0 - */ - public function uksort($cmp_function) { } - - /** - * Sort entries using a "natural order" algorithm - * @link http://php.net/manual/en/arrayobject.natsort.php - * - * @return void - * - * @since 5.2.0 - */ - public function natsort() { } - - /** - * Sort an array using a case insensitive "natural order" algorithm - * @link http://php.net/manual/en/arrayobject.natcasesort.php - * - * @return void - * - * @since 5.2.0 - */ - public function natcasesort() { } - - /** - * Unserialize an ArrayObject - * @link http://php.net/manual/en/arrayobject.unserialize.php - * - * @param string $serialized The serialized ArrayObject - * @return void The unserialized ArrayObject - * - * @since 5.3.0 - */ - public function unserialize($serialized) { } - - /** - * Serialize an ArrayObject - * @link http://php.net/manual/en/arrayobject.serialize.php - * - * @return string The serialized representation of the ArrayObject. - * - * @since 5.3.0 - */ - public function serialize() { } - - /** - * Create a new iterator from an ArrayObject instance - * @link http://php.net/manual/en/arrayobject.getiterator.php - * - * @return ArrayIterator An iterator from an ArrayObject. - * - * @since 5.0.0 - */ - public function getIterator() { } - - /** - * Exchange the array for another one. - * @link http://php.net/manual/en/arrayobject.exchangearray.php - * - * @param mixed $input The new array or object to exchange with the current array. - * @return array the old array. - * - * @since 5.1.0 - */ - public function exchangeArray($input) { } - - /** - * Sets the iterator classname for the ArrayObject. - * @link http://php.net/manual/en/arrayobject.setiteratorclass.php - * - * @param string $iterator_class The classname of the array iterator to use when iterating over this object. - * @return void - * - * @since 5.1.0 - */ - public function setIteratorClass($iterator_class) { } - - /** - * Gets the iterator classname for the ArrayObject. - * @link http://php.net/manual/en/arrayobject.getiteratorclass.php - * - * @return string the iterator class name that is used to iterate over this object. - * - * @since 5.1.0 - */ - public function getIteratorClass() { } -} - -interface Serializable { - /** - * @return null|string - */ - public function serialize(); - - /** - * @param string $data - * @return void - */ - public function unserialize($data); -} - -/** - * @template-covariant T as object - */ -final class WeakReference -{ - // always fail - public function __construct() {} - - /** - * @template TIn as object - * @param TIn $referent - * @return WeakReference - */ - public static function create(object $referent): WeakReference {} - - /** @return ?T */ - public function get(): ?object {} -} - -/** - * @template TKey of object - * @template TVal of mixed - * @implements ArrayAccess - * @implements IteratorAggregate - * @implements Traversable - * - * @since 8.0.0 - */ -final class WeakMap implements ArrayAccess, Countable, IteratorAggregate, Traversable -{ - /** - * @param TKey $offset - * @return bool - */ - public function offsetExists($offset) {} - - /** - * @param TKey $offset - * @return TVal|null - */ - public function offsetGet($offset) {} - - /** - * @param TKey $offset - * @param TVal $value - * @return void - */ - public function offsetSet($offset, $value) {} - - /** - * @param TKey $offset - * @return void - */ - public function offsetUnset($offset) {} -} - - -#[Attribute(Attribute::TARGET_METHOD)] -final class ReturnTypeWillChange -{ - public function __construct() {} -} - -#[Attribute(Attribute::TARGET_PARAMETER)] -final class SensitiveParameter -{ - public function __construct() {} -} - -#[Attribute(Attribute::TARGET_CLASS)] -final class AllowDynamicProperties -{ - public function __construct() {} -} - diff --git a/lib/WorseReflection/Core/SourceCodeLocator/InternalStubs/Iterator.php b/lib/WorseReflection/Core/SourceCodeLocator/InternalStubs/Iterator.php deleted file mode 100644 index a97f0d5f01..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/InternalStubs/Iterator.php +++ /dev/null @@ -1,89 +0,0 @@ - - */ -interface Traversable extends iterable -{ -} - -/** - * Interface to create an external Iterator. - * @link https://php.net/manual/en/class.iteratoraggregate.php - * @template TKey - * @template TValue - * @template-implements Traversable - */ -interface IteratorAggregate extends Traversable -{ - /** - * Retrieve an external iterator - * @link https://php.net/manual/en/iteratoraggregate.getiterator.php - * @return Traversable|TValue[] An instance of an object implementing Iterator or - * Traversable - * @throws Exception on failure. - */ - public function getIterator(): Traversable; -} - -/** - * Interface for external iterators or objects that can be iterated - * themselves internally. - * @link https://php.net/manual/en/class.iterator.php - * @template TKey - * @template TValue - * @template-implements Traversable - */ -interface Iterator extends Traversable -{ - /** - * Return the current element - * @link https://php.net/manual/en/iterator.current.php - * @return TValue Can return any type. - */ - public function current(); - - /** - * Move forward to next element - * @link https://php.net/manual/en/iterator.next.php - * @return void Any returned value is ignored. - */ - public function next(): void; - - /** - * Return the key of the current element - * @link https://php.net/manual/en/iterator.key.php - * @return TKey|null TKey on success, or null on failure. - */ - public function key(); - - /** - * Checks if current position is valid - * @link https://php.net/manual/en/iterator.valid.php - * @return bool The return value will be casted to boolean and then evaluated. - * Returns true on success or false on failure. - */ - public function valid(): bool; - - /** - * Rewind the Iterator to the first element - * @link https://php.net/manual/en/iterator.rewind.php - * @return void Any returned value is ignored. - */ - public function rewind(): void; -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator/NativeReflectionFunctionSourceLocator.php b/lib/WorseReflection/Core/SourceCodeLocator/NativeReflectionFunctionSourceLocator.php deleted file mode 100644 index 1536208952..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/NativeReflectionFunctionSourceLocator.php +++ /dev/null @@ -1,46 +0,0 @@ -sourceFromFunctionName($name); - } - - throw new SourceNotFound(sprintf( - 'Could not locate function with Reflection: "%s"', - $name->__toString() - )); - } - - private function sourceFromFunctionName(Name $name): TextDocument - { - $functionName = (string) $name; - $function = new ReflectionFunction($functionName); - $fileName = $function->getFileName(); - - if ($function->isInternal()) { - throw new SourceNotFound(sprintf( - 'Function "%s" is an internal function, there is another locator for that', - $name->__toString() - )); - } - if ($fileName === false) { - throw new InvalidArgumentException(sprintf('Function "%s" has no file', $functionName)); - } - - return TextDocumentBuilder::fromUri($fileName)->build(); - } -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator/NullSourceLocator.php b/lib/WorseReflection/Core/SourceCodeLocator/NullSourceLocator.php deleted file mode 100644 index 27c31e681f..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/NullSourceLocator.php +++ /dev/null @@ -1,19 +0,0 @@ -__toString() - )); - } -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator/StringSourceLocator.php b/lib/WorseReflection/Core/SourceCodeLocator/StringSourceLocator.php deleted file mode 100644 index 7328d85cc2..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/StringSourceLocator.php +++ /dev/null @@ -1,19 +0,0 @@ -source; - } -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator/StubSourceLocator.php b/lib/WorseReflection/Core/SourceCodeLocator/StubSourceLocator.php deleted file mode 100644 index a96ce969e2..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/StubSourceLocator.php +++ /dev/null @@ -1,164 +0,0 @@ - - */ - private ?array $map = null; - - public function __construct( - private Reflector $reflector, - private string $stubPath, - private string $cacheDir - ) { - } - - public function locate(Name $name): TextDocument - { - $map = $this->map(); - - if (isset($map[(string) $name])) { - return TextDocumentBuilder::fromUri($map[(string) $name])->build(); - } - - throw new SourceNotFound(sprintf( - 'Could not find source for "%s" in stub directory "%s"', - (string) $name, - $this->stubPath - )); - } - - /** - * @return array - */ - private function map(): array - { - if ($this->map !== null) { - return $this->map; - } - - if (file_exists($this->serializedMapPath())) { - $map = unserialize((string)file_get_contents($this->serializedMapPath())); - - if (!is_array($map)) { - throw new RuntimeException(sprintf('Invalid serialized stub data, expected an array, got: %s', get_debug_type($map))); - } - - /** @var array $map */ - $this->map = $map; - - return $this->map; - } - - $this->buildCache(); - - return $this->map(); - } - - private function buildCache(): void - { - $map = []; - foreach ($this->fileIterator() as $file) { - /** @var SplFileInfo $file */ - if ($file->getExtension() !== 'php' || $file->isDir()) { - continue; - } - - $map = $this->buildClassMap($file, $map); - $map = $this->buildFunctionMap($file, $map); - $map = $this->buildConstantMap($file, $map); - } - - if (!file_exists($this->cacheDir)) { - if (!@mkdir($this->cacheDir, 0777, true)) { - throw new RuntimeException(sprintf( - 'Could not create cache dir "%s"', - $this->cacheDir - )); - } - } - - file_put_contents($this->serializedMapPath(), serialize($map)); - } - - private function serializedMapPath(): string - { - return $this->cacheDir . '/' . md5($this->stubPath) . '.map'; - } - - /** - * @return RecursiveIteratorIterator - */ - private function fileIterator(): RecursiveIteratorIterator - { - return new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($this->stubPath, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::SELF_FIRST - ); - } - - /** - * @return array - * @param array $map - */ - private function buildClassMap(SplFileInfo $file, array $map): array - { - $functions = $this->reflector->reflectClassesIn( - TextDocumentBuilder::fromUri($file)->build() - ); - - foreach ($functions as $function) { - $map[(string) $function->name()] = (string) $file; - } - - return $map; - } - - /** - * @param array $map - * @return array - */ - private function buildFunctionMap(SplFileInfo $file, array $map): array - { - $functions = $this->reflector->reflectFunctionsIn( - TextDocumentBuilder::fromUri($file)->build() - ); - - foreach ($functions as $function) { - $map[(string) $function->name()] = (string) $file; - } - - return $map; - } - - /** - * @param array $map - * @return array - */ - private function buildConstantMap(SplFileInfo $file, array $map): array - { - $constants = $this->reflector->reflectConstantsIn( - TextDocumentBuilder::fromUri($file)->build() - ); - - foreach ($constants as $constant) { - $map[(string) $constant->name()] = (string) $file; - } - - return $map; - } -} diff --git a/lib/WorseReflection/Core/SourceCodeLocator/TemporarySourceLocator.php b/lib/WorseReflection/Core/SourceCodeLocator/TemporarySourceLocator.php deleted file mode 100644 index b3b8fcc025..0000000000 --- a/lib/WorseReflection/Core/SourceCodeLocator/TemporarySourceLocator.php +++ /dev/null @@ -1,78 +0,0 @@ -sources) > $this->bufferSize) { - array_shift($this->sources); - } - - $this->sources[] = $source; - } - - public function locate(Name $name): TextDocument - { - foreach ($this->sources as $source) { - $classes = $this->reflector->reflectClassesIn($source); - - if ($classes->has((string) $name)) { - return $source; - } - - if ($this->locateFunctions) { - $functions = $this->reflector->reflectFunctionsIn($source); - - if ($functions->has((string) $name)) { - return $source; - } - } - } - - throw new SourceNotFound(sprintf( - 'Class "%s" not found', - (string) $name - )); - } -} diff --git a/lib/WorseReflection/Core/TemplateMap.php b/lib/WorseReflection/Core/TemplateMap.php deleted file mode 100644 index 6288356e2d..0000000000 --- a/lib/WorseReflection/Core/TemplateMap.php +++ /dev/null @@ -1,122 +0,0 @@ - $map - */ - public function __construct(private array $map) - { - } - - public function __toString(): string - { - return implode("\n", array_map(fn (string $name, Type $type) => sprintf('%s: %s', $name, $type->__toString()), array_keys($this->map), $this->map)); - } - - /** - * @return array - */ - public function toArray(): array - { - return $this->map; - } - - public function replace(string $key, Type $type): self - { - $this->map[$key] = $type; - - return $this; - } - - public function has(string $key): bool - { - return isset($this->map[$key]); - } - - /** - * @param Type[] $arguments - */ - public function get(string $key, array $arguments = []): Type - { - if (!isset($this->map[$key])) { - return new MissingType(); - } - - // if any of the arguments are template parameters replace them with - // any constraints (e.g. T of Foobar) - $arguments = array_map(function (Type $argument) { - return $this->map[$argument->short()] ?? $argument; - }, $arguments); - - if ($arguments) { - $offset = array_search($key, array_keys($this->map)); - - if (isset($arguments[$offset])) { - return $arguments[$offset]; - } - } - - return $this->map[$key]; - } - - public function merge(TemplateMap $map): TemplateMap - { - $new = $this->map; - foreach ($map->map as $key => $value) { - $new[$key] = $value; - } - - - return new TemplateMap($new); - } - - public function count(): int - { - return count($this->map); - } - - /** - * @param Type[] $arguments - */ - public function mapArguments(array $arguments): TemplateMap - { - $newMap = []; - foreach ($this->map as $key => $type) { - $argument = array_shift($arguments); - if (null === $argument) { - $newMap[$key] = $type; - continue; - } - $newMap[$key] = $argument; - } - - return new self($newMap); - } - - /** - * @return Type[] - */ - public function toArguments(): array - { - return array_values($this->map); - } - - public function getOrGiven(Type $type): Type - { - if (!$type instanceof ClassType) { - return $type; - } - $templateType = $this->map[$type->short()] ?? null; - if ($templateType) { - return $templateType; - } - return $type; - } -} diff --git a/lib/WorseReflection/Core/Trinary.php b/lib/WorseReflection/Core/Trinary.php deleted file mode 100644 index 21efcb64ab..0000000000 --- a/lib/WorseReflection/Core/Trinary.php +++ /dev/null @@ -1,59 +0,0 @@ -maybe = null === $true; - } - - public static function true(): self - { - return new self(true); - } - - public static function false(): self - { - return new self(false); - } - - public static function maybe(): self - { - return new self(null); - } - - public static function fromBoolean(bool $bool): self - { - if ($bool) { - return self::true(); - } - - return self::false(); - } - - public function isTrue(): bool - { - return $this->true === true; - } - - public function isFalse(): bool - { - return $this->true === false; - } - - public function isMaybe(): bool - { - return $this->maybe === true; - } - - public function isFalseOrMaybe(): bool - { - return $this->isFalse() || $this->isMaybe(); - } -} diff --git a/lib/WorseReflection/Core/Type.php b/lib/WorseReflection/Core/Type.php deleted file mode 100644 index 3eb2c5b490..0000000000 --- a/lib/WorseReflection/Core/Type.php +++ /dev/null @@ -1,260 +0,0 @@ - - */ - public function expandTypes(): Types - { - /** @phpstan-ignore-next-line */ - return new Types([$this]); - } - - /** - * Return ALL types referenced in this type. - * - * For example: - * - * - `MyGeneric`: Will Return `MyGeneric`, `One`, `string` and `int`. - * - `MyClass`: Will return `MyClass`. - * - `Closure(Foobar,int): float`: Will return `Closure` (as a "class" type), `Foobar`, `int` and `float` ` - * @return Types - */ - public function allTypes(): Types - { - /** @phpstan-ignore-next-line */ - return new Types([$this]); - } - - - public function isDefined(): bool - { - return !$this instanceof MissingType; - } - - public function isVoid(): bool - { - return $this instanceof VoidType; - } - - public function isClass(): bool - { - return $this instanceof ClassType; - } - - public function isClosure(): bool - { - return $this instanceof ClosureType; - } - - public function isArray(): bool - { - return $this instanceof ArrayType; - } - - public function isIterable(): bool - { - return $this instanceof PseudoIterableType; - } - - public function isNullable(): bool - { - return false; - } - - public function addType(Type $type): AggregateType - { - return new UnionType($this, $type); - } - - public function isPrimitive(): bool - { - return $this instanceof PrimitiveType; - } - - public function short(): string - { - $type = $this; - if ($type instanceof AggregateType) { - // generalize literal types in order to de-duplicate them - $type = $type->generalize()->reduce(); - } - - if ($type instanceof UnionType) { - return implode('|', array_map(fn (Type $t) => $t->short(), $type->types)); - } - - if ($type instanceof IntersectionType) { - return implode('&', array_map(fn (Type $t) => $t->short(), $type->types)); - } - - if ($type instanceof NullableType) { - return '?' . $type->type->short(); - } - - if ($type instanceof GenericClassType) { - return sprintf('%s<%s>', $type->name()->short(), implode(',', array_map(fn (Type $arg) => $arg->short(), $type->arguments()))); - } - - if ($type instanceof ClassType) { - return $type->name()->short(); - } - - return $type->toPhpString(); - } - - public function toLocalType(ReflectionScope $scope): self - { - // TODO: do not modify type by reference - return $this->map(fn (Type $type) => $scope->resolveLocalType(clone $type)); - } - - public static function fromTypes(Type ...$types): Type - { - if (count($types) === 0) { - return new MissingType(); - } - if (count($types) === 1) { - return $types[0]; - } - - return new UnionType(...$types); - } - - public function generalize(): Type - { - return $this->map(function (Type $type) { - return $type instanceof Generalizable ? $type->generalize() : $type; - }); - } - - public function equals(Type $type): bool - { - return $this->__toString() === $type->__toString(); - } - - public function instanceof(Type $type): Trinary - { - return Trinary::fromBoolean($type->equals($this)); - } - - public function isNull(): bool - { - return false; - } - - public function stripNullable(): Type - { - return $this; - } - - public function reduce(): Type - { - return $this; - } - - public function isTrue(): bool - { - return false; - } - - public function isEmpty(): Trinary - { - $empty = TypeFactory::unionEmpty()->accepts($this); - - if ($empty->isTrue() || $empty->isFalse()) { - return $empty; - } - - if ($this instanceof Literal) { - return Trinary::false(); - } - return Trinary::maybe(); - } - - public function isMixed(): bool - { - return $this instanceof MixedType; - } - public function mergeType(Type $type): Type - { - if ($this instanceof MissingType) { - return $type; - } - - if ($this instanceof AggregateType) { - return $this->add($type); - } - - return TypeFactory::intersection($this, $type); - } - - /** - * @param Closure(Type): Type $mapper - */ - public function map(Closure $mapper): Type - { - return $mapper($this); - } - - /** - * If this type can "consume" or replace the given type - */ - public function consumes(Type $type2): Trinary - { - return Trinary::maybe(); - } - - /** - * If the type has been augmented with more information - * than a standard PHP type (e.g. typed arrays, generics, closures, etc). - * - * For example augmented types should have a php doc. - */ - public function isAugmented(): bool - { - return $this->isDefined() && !$this->isPrimitive() && $this->__toString() !== $this->toPhpString(); - } -} diff --git a/lib/WorseReflection/Core/Type/AggregateType.php b/lib/WorseReflection/Core/Type/AggregateType.php deleted file mode 100644 index ca85799306..0000000000 --- a/lib/WorseReflection/Core/Type/AggregateType.php +++ /dev/null @@ -1,196 +0,0 @@ -types as $utype) { - $unique[$utype->__toString()] = $utype; - } - continue; - } - if ($type instanceof AggregateType && count($type->types) > 1) { - $type = TypeFactory::parenthesized($type); - } - if ($type instanceof NullableType) { - $type = $type->type; - $null = TypeFactory::null(); - $unique[$null->__toString()] = $null; - } - - $unique[$type->__toString()] = $type; - } - $this->types = array_values($unique); - } - - public function __toString(): string - { - return implode('|', array_map(fn (Type $type) => $type->__toString(), $this->types)); - } - - public function toPhpString(): string - { - return implode('|', array_map(fn (Type $type) => $type->toPhpString(), $this->types)); - } - - public function reduce(): Type - { - if (count($this->types) === 0) { - return new MissingType(); - } - - if (count($this->types) === 1) { - $type = $this->types[array_key_first($this->types)]; - - if ($type instanceof ParenthesizedType) { - return $type->type; - } - - return $type; - } - - if (count($this->types) === 2 && $this->isNullable()) { - return TypeFactory::nullable($this->stripNullable()); - } - - $remove = []; - foreach ($this->types as $type1) { - foreach ($this->types as $type2) { - if ($type1 === $type2) { - continue; - } - - if ($type1->consumes($type2)->isTrue()) { - $remove[] = $type2; - } - } - } - - $type = $this; - foreach ($remove as $removeType) { - $type = $this->remove($removeType); - } - - return $type; - } - - abstract public function withTypes(Type ...$types): AggregateType; - - public function clean(): AggregateType - { - $types = $this->types; - $unique = []; - - foreach ($types as $type) { - if ($type instanceof MissingType) { - continue; - } - if ($type instanceof AggregateType) { - $type = $type->reduce(); - } - $unique[$type->__toString()] = $type; - } - - return $this->withTypes(...array_values($unique)); - } - - public function remove(Type $remove): Type - { - $remove = UnionType::toUnion($remove); - $removeStrings = array_map(fn (Type $t) => $t->__toString(), $remove->types); - - return ($this->withTypes(...array_filter($this->types, function (Type $type) use ($removeStrings) { - return !in_array($type->__toString(), $removeStrings); - })))->reduce(); - } - - public function expandTypes(): Types - { - $types = new Types([]); - foreach ($this->types as $type) { - $types = $types->merge($type->expandTypes()); - } - return $types; - } - - public function allTypes(): Types - { - $types = new Types([]); - foreach ($this->expandTypes() as $type) { - $types = $types->merge($type->allTypes()); - } - - return $types; - } - - public function add(Type $type): AggregateType - { - return ($this->withTypes(...array_merge($this->types, [$type])))->clean(); - } - - public function isNull(): bool - { - $reduced = $this->reduce(); - return $reduced instanceof NullType; - } - - public function isNullable(): bool - { - foreach ($this->types as $type) { - if ($type->isNull()) { - return true; - } - } - - return false; - } - - public function stripNullable(): Type - { - return ($this->withTypes(...array_filter($this->types, function (Type $type) { - return !$type instanceof NullType; - })))->reduce(); - } - - public function map(Closure $mapper): Type - { - return $this->withTypes(...array_map(fn (Type $type) => $type->map($mapper), $this->types)); - } - - public function filter(Closure $closure): AggregateType - { - return $this->withTypes(...array_filter($this->types, $closure)); - } - public function count(): int - { - return count($this->types); - } - - public function contains(Type $narrowTo): bool - { - foreach ($this->types as $type) { - if ($type->equals($narrowTo)) { - return true; - } - } - - return false; - } -} diff --git a/lib/WorseReflection/Core/Type/ArrayAccessType.php b/lib/WorseReflection/Core/Type/ArrayAccessType.php deleted file mode 100644 index a1725afb91..0000000000 --- a/lib/WorseReflection/Core/Type/ArrayAccessType.php +++ /dev/null @@ -1,13 +0,0 @@ -isFalse()) { - return $parentAccepts; - } - - if ($type instanceof IntType) { - return Trinary::true(); - } - - if ($type instanceof FloatType) { - return Trinary::true(); - } - - if ($type instanceof StringType) { - return Trinary::true(); - } - - return Trinary::false(); - } -} diff --git a/lib/WorseReflection/Core/Type/ArrayLiteral.php b/lib/WorseReflection/Core/Type/ArrayLiteral.php deleted file mode 100644 index 601eea8033..0000000000 --- a/lib/WorseReflection/Core/Type/ArrayLiteral.php +++ /dev/null @@ -1,131 +0,0 @@ - $typeMap - */ - public function __construct(private array $typeMap) - { - $this->keyType = TypeUtil::generalTypeFromTypes($this->iterableKeyTypes()); - $this->valueType = TypeUtil::generalTypeFromTypes(array_values($typeMap)); - } - - public function __toString(): string - { - if ($this->isList()) { - return sprintf( - 'array{%s}', - implode(',', array_map( - fn (Type $type) => sprintf('%s', $type->__toString()), - array_values($this->typeMap), - )) - ); - } - - return sprintf( - 'array{%s}', - implode(',', array_map( - fn ($key, Type $type) => sprintf('%s:%s', $key, $type->__toString()), - array_keys($this->typeMap), - array_values($this->typeMap), - )) - ); - } - - /** - * @return Type[] - */ - public function iterableValueTypes(): array - { - return array_values($this->typeMap); - } - - /** - * @return Type[] - */ - public function iterableKeyTypes(): array - { - return TypeFactory::fromValues(array_keys($this->typeMap)); - } - - public function isList(): bool - { - return range(0, count($this->typeMap) - 1) === array_keys($this->typeMap); - } - - /** - * @return mixed[] - */ - public function value(): array - { - return array_map( - fn (Type $type) => TypeUtil::valueOrNull($type), - $this->typeMap - ); - } - - public function generalize(): Type - { - return new ArrayType($this->keyType, $this->valueType); - } - - /** - * @param array-key $offset $offset - */ - public function typeAtOffset($offset): Type - { - return $this->typeMap[$offset] ?? new MissingType(); - } - - public function withValue(mixed $value): self - { - return $this; - } - - /** - * @return array - */ - public function types(): array - { - return $this->typeMap; - } - - /** - * @param array-key $key - */ - public function set($key, Type $type): self - { - $map = $this->typeMap; - $map[$key] = $type; - return new self($map); - } - - public function add(Type $type): self - { - $map = $this->typeMap; - $map[] = $type; - return new self($map); - } - - public function toShape(): ArrayShapeType - { - return new ArrayShapeType($this->typeMap); - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof ArrayType) { - return Trinary::maybe(); - } - - return parent::accepts($type); - } -} diff --git a/lib/WorseReflection/Core/Type/ArrayShapeType.php b/lib/WorseReflection/Core/Type/ArrayShapeType.php deleted file mode 100644 index 07acdaad0f..0000000000 --- a/lib/WorseReflection/Core/Type/ArrayShapeType.php +++ /dev/null @@ -1,78 +0,0 @@ - $typeMap - */ - public function __construct(public array $typeMap) - { - $this->keyType = TypeUtil::generalTypeFromTypes(TypeFactory::fromValues(array_keys($typeMap))); - $this->valueType = TypeUtil::generalTypeFromTypes($typeMap); - } - - public function __toString(): string - { - if ($this->isList()) { - return sprintf( - 'array{%s}', - implode(',', array_map( - fn (Type $t) => $t->__toString(), - $this->typeMap, - )) - ); - } - - return sprintf( - 'array{%s}', - implode(',', array_map( - fn (string $key, Type $t) => sprintf('%s:%s', $key, $t->__toString()), - array_keys($this->typeMap), - $this->typeMap - )) - ); - } - - public function isList(): bool - { - return range(0, count($this->typeMap) - 1) === array_keys($this->typeMap); - } - - public function generalize(): Type - { - return new self(array_map(fn (Type $type) => $type->generalize(), $this->typeMap)); - } - - /** - * @param array-key $offset $offset - */ - public function typeAtOffset($offset): Type - { - return $this->typeMap[$offset] ?? new MissingType(); - } - - /** - * @return array-key[] - */ - public function keys(): array - { - return array_keys($this->typeMap); - } - - public function map(Closure $mapper): Type - { - return new self( - array_map(function (Type $type) use ($mapper) { - $type = $type->map($mapper); - return $mapper($type); - }, $this->typeMap) - ); - } -} diff --git a/lib/WorseReflection/Core/Type/ArrayType.php b/lib/WorseReflection/Core/Type/ArrayType.php deleted file mode 100644 index 4b909cfcbb..0000000000 --- a/lib/WorseReflection/Core/Type/ArrayType.php +++ /dev/null @@ -1,68 +0,0 @@ -valueType instanceof MissingType) { - return $this->toPhpString(); - } - if ($this->keyType === null) { - return sprintf('%s[]', $this->valueType->__toString()); - } - - return sprintf('array<%s,%s>', $this->keyType->__toString(), $this->valueType->__toString()); - } - - public function toPhpString(): string - { - return 'array'; - } - - public function map(Closure $mapper): Type - { - return new self( - $this->keyType ? $this->iterableKeyType()->map($mapper) : null, - $this->valueType ? $this->iterableValueType()->map($mapper) : null, - ); - } - - public function emptyType(): Type - { - return new ArrayLiteral([]); - } - - public function consumes(Type $type): Trinary - { - // if type is an empty array, replace with this one - if ($type instanceof ArrayLiteral && count($type->types()) === 0) { - return Trinary::true(); - } - - return Trinary::maybe(); - } - - public function allTypes(): Types - { - return (new Types([TypeFactory::array()]))->merge(parent::allTypes()); - } - - public function add(Type $type): self - { - if (null === $this->valueType) { - return new self($this->keyType, $type); - } - if (!$this->valueType->isDefined()) { - return new self($this->keyType, $type); - } - return new self($this->keyType, $this->valueType->addType($type)); - } -} diff --git a/lib/WorseReflection/Core/Type/BinLiteralType.php b/lib/WorseReflection/Core/Type/BinLiteralType.php deleted file mode 100644 index c24acfea7e..0000000000 --- a/lib/WorseReflection/Core/Type/BinLiteralType.php +++ /dev/null @@ -1,22 +0,0 @@ -value; - } - - public function value(): int|float - { - return bindec(substr($this->value, 2)); - } -} diff --git a/lib/WorseReflection/Core/Type/BitwiseOperable.php b/lib/WorseReflection/Core/Type/BitwiseOperable.php deleted file mode 100644 index c8c0572a5c..0000000000 --- a/lib/WorseReflection/Core/Type/BitwiseOperable.php +++ /dev/null @@ -1,15 +0,0 @@ -value ? 'true' : 'false'; - } - - public function value(): bool - { - return $this->value; - } - - public function generalize(): Type - { - return new BooleanType(); - } - - public function or(BooleanType $right): BooleanType - { - if ($right instanceof BooleanLiteralType) { - return new self($this->value || $right->value); - } - - return new BooleanType(); - } - - public function and(BooleanType $right): BooleanType - { - if ($right instanceof BooleanLiteralType) { - return new self($this->value && $right->value); - } - - return new BooleanType(); - } - - public function negate(): BooleanType - { - return new self(!$this->value); - } - - public function xor(BooleanType $booleanType): BooleanType - { - if ($booleanType instanceof BooleanLiteralType) { - return new self($this->value() xor $booleanType->value()); - } - - return new BooleanType(); - } - - public function toTrinary(): Trinary - { - return Trinary::fromBoolean($this->value); - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof BooleanLiteralType) { - return Trinary::fromBoolean($type->equals($this)); - } - if ($type instanceof BooleanType) { - return Trinary::maybe(); - } - return parent::accepts($type); - } -} diff --git a/lib/WorseReflection/Core/Type/BooleanType.php b/lib/WorseReflection/Core/Type/BooleanType.php deleted file mode 100644 index 0f103c8284..0000000000 --- a/lib/WorseReflection/Core/Type/BooleanType.php +++ /dev/null @@ -1,53 +0,0 @@ -value() === true; - } - - return false; - } - - public function emptyType(): Type - { - return new BooleanLiteralType(false); - } -} diff --git a/lib/WorseReflection/Core/Type/CallableType.php b/lib/WorseReflection/Core/Type/CallableType.php deleted file mode 100644 index b6f0d59831..0000000000 --- a/lib/WorseReflection/Core/Type/CallableType.php +++ /dev/null @@ -1,71 +0,0 @@ -returnType instanceof MissingType) { - return sprintf( - 'callable(%s)', - implode(',', array_map(fn (Type $type) => $type->__toString(), $this->args)) - ); - } - return sprintf( - 'callable(%s): %s', - implode(',', array_map(fn (Type $type) => $type->__toString(), $this->args)), - $this->returnType->__toString() - ); - } - - public function toPhpString(): string - { - return 'callable'; - } - - public function accepts(Type $type): Trinary - { - return Trinary::fromBoolean($type instanceof CallableType); - } - - public function map(Closure $mapper): Type - { - $new = clone $this; - $new->args = array_map(fn (Type $t) => $t->map($mapper), $this->args); - $new->returnType = $this->returnType->map($mapper); - return $new; - } - - public function arguments(): array - { - return $this->args; - } - - public function returnType(): Type - { - return $this->returnType; - } - - public function allTypes(): Types - { - return new Types([ - ...$this->args, - $this->returnType - ]); - } -} diff --git a/lib/WorseReflection/Core/Type/ClassLikeType.php b/lib/WorseReflection/Core/Type/ClassLikeType.php deleted file mode 100644 index c070979607..0000000000 --- a/lib/WorseReflection/Core/Type/ClassLikeType.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ - public function members(): ReflectionMemberCollection; -} diff --git a/lib/WorseReflection/Core/Type/ClassStringType.php b/lib/WorseReflection/Core/Type/ClassStringType.php deleted file mode 100644 index e712a17ea6..0000000000 --- a/lib/WorseReflection/Core/Type/ClassStringType.php +++ /dev/null @@ -1,48 +0,0 @@ -className) { - return sprintf('class-string<%s>', $this->className->__toString()); - } - return 'class-string'; - } - - public function toPhpString(): string - { - return 'string'; - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof ClassStringType) { - // this is not really true - we should not accept a class-string for class-string - // BUT also class-string should accept class-string as we - // can't (easily) resolve the template var early. - return Trinary::true(); - } - - if (!$type instanceof StringType) { - return Trinary::false(); - } - - return Trinary::maybe(); - } - - public function className(): ?ClassName - { - return $this->className; - } -} diff --git a/lib/WorseReflection/Core/Type/ClassType.php b/lib/WorseReflection/Core/Type/ClassType.php deleted file mode 100644 index b29f1c5e1e..0000000000 --- a/lib/WorseReflection/Core/Type/ClassType.php +++ /dev/null @@ -1,115 +0,0 @@ - - */ - public ReflectionMemberCollection $members; - - public function __construct(public ClassName $name) - { - $this->members = ClassLikeReflectionMemberCollection::empty(); - } - - public function __toString(): string - { - return $this->name->full(); - } - - public function toPhpString(): string - { - return $this->__toString(); - } - - public function name(): ClassName - { - return $this->name; - } - - /** - * @return ReflectionMemberCollection - */ - public function members(): ReflectionMemberCollection - { - return $this->members; - } - - /** - * @param ReflectionMemberCollection $collection - */ - public function mergeMembers(ReflectionMemberCollection $collection): self - { - $new = clone $this; - $new->members = $this->members->merge($collection); - return $new; - } - - - public function is(Type $type): Trinary - { - if ($type instanceof MissingType) { - return Trinary::maybe(); - } - - if (!$type instanceof ClassType) { - return Trinary::false(); - } - - return Trinary::fromBoolean($type->name() == $this->name()); - } - - public function accepts(Type $type): Trinary - { - if ($this->is($type)->isTrue()) { - return Trinary::true(); - } - - if ($type instanceof ClassType) { - return Trinary::maybe(); - } - - return Trinary::false(); - } - - public function instanceof(Type $right): Trinary - { - if ($right->equals($this)) { - return Trinary::true(); - } - return Trinary::maybe(); - } - - public function isInterface(): Trinary - { - return Trinary::maybe(); - } - - public function isUnknown(): Trinary - { - return Trinary::true(); - } - - public function emptyType(): Type - { - return $this; - } - - public function asReflectedClasssType(Reflector $reflector): ReflectedClassType - { - if ($this instanceof ReflectedClassType) { - return $this; - } - return new ReflectedClassType($reflector, $this->name()); - } -} diff --git a/lib/WorseReflection/Core/Type/ClosureType.php b/lib/WorseReflection/Core/Type/ClosureType.php deleted file mode 100644 index 946de77976..0000000000 --- a/lib/WorseReflection/Core/Type/ClosureType.php +++ /dev/null @@ -1,70 +0,0 @@ - $type->__toString(), $this->args)), - $this->returnType->__toString() - ); - } - - public function toPhpString(): string - { - return 'Closure'; - } - - public function name(): ClassName - { - return ClassName::fromString('Closure'); - } - - public function arguments(): array - { - return $this->args; - } - - public function returnType(): Type - { - return $this->returnType; - } - - public function map(Closure $mapper): Type - { - $new = clone $this; - $new->args = array_map(fn (Type $t) => $t->map($mapper), $this->args); - $new->returnType = $this->returnType->map($mapper); - return $new; - } - - public function allTypes(): Types - { - return new Types([ - TypeFactory::reflectedClass($this->reflector, 'Closure'), - ...$this->args, - $this->returnType - ]); - } -} diff --git a/lib/WorseReflection/Core/Type/Comparable.php b/lib/WorseReflection/Core/Type/Comparable.php deleted file mode 100644 index 668d523bca..0000000000 --- a/lib/WorseReflection/Core/Type/Comparable.php +++ /dev/null @@ -1,24 +0,0 @@ -compare($right, '==='); - } - - public function greaterThan(Type $right): BooleanType - { - return $this->compare($right, '>'); - } - - public function greaterThanEqual(Type $right): BooleanType - { - return $this->compare($right, '>='); - } - - public function lessThan(Type $right): BooleanType - { - return $this->compare($right, '<'); - } - - public function notEqual(Type $right): BooleanType - { - return $this->compare($right, '!='); - } - - public function lessThanEqual(Type $right): BooleanType - { - return $this->compare($right, '<='); - } - - public function equal(Type $right): BooleanType - { - return $this->compare($right, '=='); - } - - public function notIdentical(Type $right): BooleanType - { - return $this->compare($right, '!=='); - } - - private function compare(Type $right, string $operator): BooleanType - { - if (!$this instanceof Literal) { - return TypeFactory::bool(); - } - - if ($right instanceof ScalarType && $right instanceof Literal) { - return TypeFactory::boolLiteral($this->doCompare($this->value(), $right->value(), $operator)); - } - - return TypeFactory::bool(); - } - - /** - * @param mixed $left - * @param mixed $right - */ - private function doCompare($left, $right, string $operator): bool - { - if ($operator === '===') { - return $left === $right; - } - if ($operator === '==') { - return $left == $right; - } - if ($operator === '!==') { - return $left !== $right; - } - if ($operator === '!=') { - return $left != $right; - } - if ($operator === '>') { - return $left > $right; - } - if ($operator === '>=') { - return $left >= $right; - } - if ($operator === '<') { - return $left < $right; - } - if ($operator === '<=') { - return $left <= $right; - } - - throw new RuntimeException(sprintf('Do not know how to handle operator "%s"', $operator)); - } -} diff --git a/lib/WorseReflection/Core/Type/Concatable.php b/lib/WorseReflection/Core/Type/Concatable.php deleted file mode 100644 index f7fbb24ce6..0000000000 --- a/lib/WorseReflection/Core/Type/Concatable.php +++ /dev/null @@ -1,10 +0,0 @@ -variable, - $this->isType->__toString(), - $this->left->__toString(), - $this->right->__toString() - ); - } - - public function toPhpString(): string - { - return 'mixed'; - } - - public function accepts(Type $type): Trinary - { - return Trinary::maybe(); - } - - public function evaluate(ReflectionFunctionLike $functionLike, FunctionArguments $functionArguments): Type - { - try { - $parameter = $functionLike->parameters()->get(ltrim($this->variable, '$')); - } catch (NotFound) { - return TypeFactory::undefined(); - } - - $argumentType = $this->resolveArgumentType($functionArguments, $parameter); - - $evaluator = function (Type $type) use ($functionLike, $functionArguments): Type { - if ($type instanceof ParenthesizedType && $type->type instanceof ConditionalType) { - return $type->type->evaluate($functionLike, $functionArguments); - } - return $type; - }; - - if (!$argumentType->isDefined()) { - return $evaluator($this->right); - } - - if ($this->isType->accepts($argumentType)->isTrue()) { - return $evaluator($this->left); - } - - return $evaluator($this->right); - } - - public function map(Closure $mapper): Type - { - return new ConditionalType( - $this->variable, - $this->isType->map($mapper), - $this->left->map($mapper), - $this->right->map($mapper) - ); - } - - private function resolveArgumentType( - FunctionArguments $functionArguments, - ReflectionParameter $parameter - ): Type { - if ($functionArguments->has($parameter->index())) { - return $functionArguments->at($parameter->index())->type(); - } - if ($parameter->default()->isDefined()) { - return TypeFactory::fromValue($parameter->default()->value()); - } - return TypeFactory::mixed(); - } -} diff --git a/lib/WorseReflection/Core/Type/EnumBackedCaseType.php b/lib/WorseReflection/Core/Type/EnumBackedCaseType.php deleted file mode 100644 index a071da4a66..0000000000 --- a/lib/WorseReflection/Core/Type/EnumBackedCaseType.php +++ /dev/null @@ -1,70 +0,0 @@ -enumType, $this->caseName); - } - - public function short(): string - { - return $this->enumType->short(); - } - - public function toPhpString(): string - { - return $this->enumType; - } - - public function accepts(Type $type): Trinary - { - return Trinary::maybe(); - } - - public function members(): ReflectionMemberCollection - { - $members = $this->enumType->members(); - try { - $case = $this->reflector->reflectClass('BackedEnumCase'); - } catch (NotFound) { - return $members; - } - /** @phpstan-ignore-next-line */ - return $members->merge($case->members()->properties()); - } - - public function isAugmented(): bool - { - return false; - } - - public function map(Closure $mapper): Type - { - return new self( - $this->reflector, - /** @phpstan-ignore-next-line Should always return a ClassType */ - $mapper($this->enumType), - $this->caseName, - $mapper($this->value) - ); - } -} diff --git a/lib/WorseReflection/Core/Type/EnumCaseType.php b/lib/WorseReflection/Core/Type/EnumCaseType.php deleted file mode 100644 index 0e07b865c6..0000000000 --- a/lib/WorseReflection/Core/Type/EnumCaseType.php +++ /dev/null @@ -1,55 +0,0 @@ -enumType, $this->caseName); - } - - public function short(): string - { - return $this->enumType->short(); - } - - public function toPhpString(): string - { - return $this->enumType; - } - - public function accepts(Type $type): Trinary - { - return Trinary::maybe(); - } - - public function map(Closure $mapper): Type - { - return new self( - $this->reflector, - /** @phpstan-ignore-next-line Should always return a ClassType */ - $mapper($this->enumType), - $this->caseName - ); - } - - public function isAugmented(): bool - { - return false; - } -} diff --git a/lib/WorseReflection/Core/Type/FalseType.php b/lib/WorseReflection/Core/Type/FalseType.php deleted file mode 100644 index dc5c33a205..0000000000 --- a/lib/WorseReflection/Core/Type/FalseType.php +++ /dev/null @@ -1,16 +0,0 @@ -value; - } - - public function value(): float - { - return $this->value; - } - - public function generalize(): Type - { - return new FloatType(); - } - - public function identity(): NumericType - { - return new self(+$this->value()); - } - - public function negative(): NumericType - { - return new self(-$this->value()); - } - - public function withValue(mixed $value): self - { - $new = clone $this; - $new->value = $value; - return $new; - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof FloatLiteralType) { - return Trinary::fromBoolean($type->equals($this)); - } - if ($type instanceof FloatType) { - return Trinary::maybe(); - } - - return parent::accepts($type); - } -} diff --git a/lib/WorseReflection/Core/Type/FloatType.php b/lib/WorseReflection/Core/Type/FloatType.php deleted file mode 100644 index 6a3b8842cc..0000000000 --- a/lib/WorseReflection/Core/Type/FloatType.php +++ /dev/null @@ -1,18 +0,0 @@ -isDefined() || $keyType instanceof ArrayKeyType) && $valueType->isDefined()) { - parent::__construct($reflector, ClassName::fromString('Generator'), [ $valueType ]); - return; - } - parent::__construct($reflector, ClassName::fromString('Generator'), [ $keyType, $valueType ]); - } - - public function keyType(): Type - { - if (count($this->arguments) >= 2) { - return $this->arguments[0]; - } - - return new MissingType(); - } - - public function valueType(): Type - { - if (count($this->arguments) === 1) { - return $this->arguments[0]; - } - if (count($this->arguments) >= 2) { - return $this->arguments[1]; - } - - return new MissingType(); - } - - public function withValue(Type $type): GeneratorType - { - $new = clone $this; - if (count($new->arguments) === 1) { - $new->replaceArgument(0, $type); - return $new; - } - if (count($this->arguments) === 2) { - $new->replaceArgument(1, $type); - return $new; - } - $new->arguments[] = $type; - return $new; - } - - public function withKey(Type $type): GeneratorType - { - $new = clone $this; - if (count($this->arguments) === 2) { - $new->replaceArgument(0, $type); - return $new; - } - if (count($this->arguments) === 1) { - $valueType = $this->arguments[0]; - $new->replaceArgument(0, $type); - $new->arguments[] = $valueType; - return $new; - } - return $new; - } - - public function map(Closure $mapper): Type - { - $t = new self( - $this->reflector, - $this->keyType()->map($mapper), - $this->valueType()->map($mapper), - ); - - return $t; - } -} diff --git a/lib/WorseReflection/Core/Type/GenericClassType.php b/lib/WorseReflection/Core/Type/GenericClassType.php deleted file mode 100644 index af1afa90bb..0000000000 --- a/lib/WorseReflection/Core/Type/GenericClassType.php +++ /dev/null @@ -1,136 +0,0 @@ -reflector = $reflector; - $this->name = $name; - $this->arguments = array_values($arguments); - } - - public function __toString(): string - { - return sprintf( - '%s<%s>', - $this->name->__toString(), - implode(',', array_map(fn (Type $t) => $t->__toString(), $this->arguments)) - ); - } - - /** - * @return Type[] - */ - public function arguments(): array - { - return array_values($this->arguments); - } - - public function iterableValueType(): Type - { - return IterableTypeResolver::resolveIterable($this->reflector, $this, $this->arguments); - } - - public function toPhpString(): string - { - return $this->name->__toString(); - } - - public function accepts(Type $type): Trinary - { - if (!$type instanceof GenericClassType) { - return parent::accepts($type); - } - - if (!parent::accepts($type)->isTrue()) { - return Trinary::false(); - } - - $typeArguments = $type->arguments; - - // horrible hack for "special" types which have > 1 "constructors" - if (in_array($type->name()->__toString(), IterableTypeResolver::iterableClasses())) { - array_unshift($typeArguments, TypeFactory::arrayKey()); - } - - foreach ($this->arguments as $index => $argument) { - if (!isset($typeArguments[$index])) { - return Trinary::false(); - } - if (!$argument->accepts($typeArguments[$index])->isTrue()) { - return Trinary::false(); - } - } - - return Trinary::true(); - } - - public function replaceArgument(int $offset, Type $type): self - { - if (!isset($this->arguments[$offset])) { - return $this; - } - - $this->arguments[$offset] = $type; - return $this; - } - - /** - * @param Type[] $arguments - */ - public function setArguments(array $arguments): self - { - $this->arguments = $arguments; - return $this; - } - - public function iterableKeyType(): Type - { - return new MissingType(); - } - - /** - * @param Type[] $arguments - */ - public function withArguments(array $arguments): self - { - return new self($this->reflector, $this->name, $arguments); - } - - public function map(Closure $mapper): Type - { - return new self( - $this->reflector, - ClassName::fromString((new ReflectedClassType($this->reflector, $this->name))->map($mapper)->__toString()), - array_map(fn (Type $type) => $type->map($mapper), $this->arguments) - ); - } - - public function allTypes(): Types - { - return new Types([ - TypeFactory::reflectedClass($this->reflector, $this->name), - ...array_values($this->arguments) - ]); - } -} diff --git a/lib/WorseReflection/Core/Type/GlobbedConstantUnionType.php b/lib/WorseReflection/Core/Type/GlobbedConstantUnionType.php deleted file mode 100644 index 516e487072..0000000000 --- a/lib/WorseReflection/Core/Type/GlobbedConstantUnionType.php +++ /dev/null @@ -1,61 +0,0 @@ -classType->__toString(), $this->glob); - } - - public function toPhpString(): string - { - return new MissingType(); - } - - public function accepts(Type $type): Trinary - { - return Trinary::maybe(); - } - - public function toUnion(): Type - { - if (!$this->classType instanceof ReflectedClassType) { - return new MissingType(); - } - - $reflection = $this->classType->reflectionOrNull(); - - if (null === $reflection) { - return new MissingType(); - } - - $types = []; - foreach ($reflection->members()->byMemberType(ReflectionMember::TYPE_CONSTANT) as $constant) { - $pattern = preg_quote(str_replace('*', '__ASTERISK__', $this->glob)); - $pattern = str_replace('__ASTERISK__', '.*', $pattern); - if (preg_match('{' . $pattern . '}', $constant->name())) { - $types[] = $constant->type(); - } - } - - return (new UnionType(...$types))->reduce(); - } - - public function map(Closure $mapper): Type - { - return new self($mapper($this->classType), $this->glob); - } -} diff --git a/lib/WorseReflection/Core/Type/HasEmptyType.php b/lib/WorseReflection/Core/Type/HasEmptyType.php deleted file mode 100644 index 2a9d975105..0000000000 --- a/lib/WorseReflection/Core/Type/HasEmptyType.php +++ /dev/null @@ -1,10 +0,0 @@ -value; - } - - public function value(): int|float - { - return hexdec(substr($this->value, 2)); - } - - public function generalize(): Type - { - return new IntType(); - } -} diff --git a/lib/WorseReflection/Core/Type/IntLiteralType.php b/lib/WorseReflection/Core/Type/IntLiteralType.php deleted file mode 100644 index 939f97ed32..0000000000 --- a/lib/WorseReflection/Core/Type/IntLiteralType.php +++ /dev/null @@ -1,60 +0,0 @@ -value; - } - - - public function value(): int - { - return $this->value; - } - - public function generalize(): Type - { - return new IntType(); - } - - public function identity(): NumericType - { - return new self(+$this->value()); - } - - public function negative(): NumericType - { - return new self(-$this->value()); - } - - public function withValue(mixed $value): IntLiteralType - { - $new = clone $this; - $new->value = (int)$value; - return $new; - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof IntLiteralType) { - return Trinary::fromBoolean($type->equals($this)); - } - if ($type instanceof IntType) { - return Trinary::maybe(); - } - - return parent::accepts($type); - } -} diff --git a/lib/WorseReflection/Core/Type/IntMaxType.php b/lib/WorseReflection/Core/Type/IntMaxType.php deleted file mode 100644 index c01dc3ce2b..0000000000 --- a/lib/WorseReflection/Core/Type/IntMaxType.php +++ /dev/null @@ -1,11 +0,0 @@ -', $this->lower ?? 'min', $this->upper ?? 'max'); - } -} diff --git a/lib/WorseReflection/Core/Type/IntType.php b/lib/WorseReflection/Core/Type/IntType.php deleted file mode 100644 index a0b7b28564..0000000000 --- a/lib/WorseReflection/Core/Type/IntType.php +++ /dev/null @@ -1,72 +0,0 @@ -withValue($this->value() >> $right->value()); - } - - return new BooleanType(); - } - - public function shiftLeft(Type $right): Type - { - if ($right instanceof IntType && $right instanceof Literal && $this instanceof Literal) { - return $this->withValue($this->value() << $right->value()); - } - - return new BooleanType(); - } - - public function bitwiseXor(Type $right): Type - { - if ($right instanceof IntType && $right instanceof Literal && $this instanceof Literal) { - return $this->withValue($this->value() ^ $right->value()); - } - - return new BooleanType(); - } - - public function bitwiseOr(Type $right): Type - { - if ($right instanceof IntType && $right instanceof Literal && $this instanceof Literal) { - return $this->withValue($this->value() | $right->value()); - } - - return new BooleanType(); - } - - public function bitwiseAnd(Type $right): Type - { - if ($right instanceof IntType && $right instanceof Literal && $this instanceof Literal) { - return $this->withValue($this->value() & $right->value()); - } - - return new BooleanType(); - } - - public function bitwiseNot(): Type - { - if ($this instanceof Literal) { - return $this->withValue(~(int)$this->value()); - } - - return $this; - } - - public function emptyType(): Type - { - return new IntLiteralType(0); - } -} diff --git a/lib/WorseReflection/Core/Type/IntersectionType.php b/lib/WorseReflection/Core/Type/IntersectionType.php deleted file mode 100644 index 75cf30bb20..0000000000 --- a/lib/WorseReflection/Core/Type/IntersectionType.php +++ /dev/null @@ -1,75 +0,0 @@ - $type->__toString(), $this->types)); - } - public static function toIntersection(Type $type): AggregateType - { - if ($type instanceof NullableType) { - return self::toIntersection($type->type)->add(TypeFactory::null()); - } - if ($type instanceof IntersectionType) { - return $type; - } - - return new IntersectionType($type); - } - - public static function fromTypes(Type ...$types): Type - { - if (count($types) === 0) { - return new MissingType(); - } - if (count($types) === 1) { - return $types[0]; - } - - return new IntersectionType(...$types); - } - - public function short(): string - { - return implode('&', array_map(fn (Type $t) => $t->short(), $this->types)); - } - - public function withTypes(Type ...$types): AggregateType - { - return new self(...$types); - } - - public function toPhpString(): string - { - return implode('&', array_map(fn (Type $type) => $type->toPhpString(), $this->types)); - } - - public function accepts(Type $type): Trinary - { - if (!$type instanceof ClassType && !$type instanceof IntersectionType) { - return Trinary::false(); - } - - if ($type->equals($this)) { - return Trinary::true(); - } - - if ($type instanceof ReflectedClassType) { - foreach ($this->types as $type) { - if ($type->instanceof($type)->isFalse()) { - return Trinary::false(); - } - } - return Trinary::true(); - } - - return Trinary::false(); - } -} diff --git a/lib/WorseReflection/Core/Type/InvokeableType.php b/lib/WorseReflection/Core/Type/InvokeableType.php deleted file mode 100644 index 0eaec33f7b..0000000000 --- a/lib/WorseReflection/Core/Type/InvokeableType.php +++ /dev/null @@ -1,15 +0,0 @@ -value = $value; - return $new; - } -} diff --git a/lib/WorseReflection/Core/Type/MissingType.php b/lib/WorseReflection/Core/Type/MissingType.php deleted file mode 100644 index 25d9b5890f..0000000000 --- a/lib/WorseReflection/Core/Type/MissingType.php +++ /dev/null @@ -1,24 +0,0 @@ -'; - } - - public function toPhpString(): string - { - return ''; - } - - public function accepts(Type $type): Trinary - { - return Trinary::true(); - } -} diff --git a/lib/WorseReflection/Core/Type/MixedType.php b/lib/WorseReflection/Core/Type/MixedType.php deleted file mode 100644 index ba61c142b8..0000000000 --- a/lib/WorseReflection/Core/Type/MixedType.php +++ /dev/null @@ -1,24 +0,0 @@ -__toString(); - } - - public function accepts(Type $type): Trinary - { - return Trinary::true(); - } -} diff --git a/lib/WorseReflection/Core/Type/NeverType.php b/lib/WorseReflection/Core/Type/NeverType.php deleted file mode 100644 index e01eb6c9dd..0000000000 --- a/lib/WorseReflection/Core/Type/NeverType.php +++ /dev/null @@ -1,24 +0,0 @@ -', $this->type); - } - - public function toPhpString(): string - { - return ''; - } - - public function accepts(Type $type): Trinary - { - return Trinary::maybe(); - } -} diff --git a/lib/WorseReflection/Core/Type/NullType.php b/lib/WorseReflection/Core/Type/NullType.php deleted file mode 100644 index d9567f4324..0000000000 --- a/lib/WorseReflection/Core/Type/NullType.php +++ /dev/null @@ -1,34 +0,0 @@ -type->__toString(); - } - - public function toPhpString(): string - { - return '?' . $this->type->toPhpString(); - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof NullableType) { - return Trinary::true(); - } - - return $this->type->accepts($type); - } - - public function expandTypes(): Types - { - return new Types([new NullType(), $this->type]); - } - - public function allTypes(): Types - { - $types = new Types([]); - foreach ($this->expandTypes() as $type) { - $types = $types->merge($type->allTypes()); - } - - return $types; - } - - public function isNull(): bool - { - return true; - } - - public function isNullable(): bool - { - return true; - } - - public function stripNullable(): Type - { - return $this->type; - } - - public function emptyType(): Type - { - return $this; - } - - public function map(Closure $mapper): Type - { - return new NullableType($mapper($this->type)); - } -} diff --git a/lib/WorseReflection/Core/Type/NumericType.php b/lib/WorseReflection/Core/Type/NumericType.php deleted file mode 100644 index 37a898aecb..0000000000 --- a/lib/WorseReflection/Core/Type/NumericType.php +++ /dev/null @@ -1,69 +0,0 @@ -withValue($this->value() + $right->value()); - } - return $this; - } - - public function modulo(NumericType $right): NumericType - { - if ($this instanceof Literal && $right instanceof Literal) { - return $this->withValue($this->value() % max(1, $right->value())); - } - return $this; - } - - public function divide(NumericType $right): NumericType - { - if ($this instanceof Literal && $right instanceof Literal) { - /** @phpstan-ignore-next-line it's a scalar */ - if (intval($right->value()) === 0) { - return $this->withValue(0); - } - - return $this->withValue($this->value() / $right->value()); - } - return $this; - } - - public function multiply(NumericType $right): NumericType - { - if ($this instanceof Literal && $right instanceof Literal) { - return $this->withValue($this->value() * $right->value()); - } - return $this; - } - - public function minus(NumericType $right): NumericType - { - if ($this instanceof Literal && $right instanceof Literal) { - return $this->withValue($this->value() - $right->value()); - } - return $this; - } - - public function exp(NumericType $right): NumericType - { - if ($this instanceof Literal && $right instanceof Literal) { - return $this->withValue($this->value() ** $right->value()); - } - return $this; - } -} diff --git a/lib/WorseReflection/Core/Type/ObjectType.php b/lib/WorseReflection/Core/Type/ObjectType.php deleted file mode 100644 index 7a38bb43ca..0000000000 --- a/lib/WorseReflection/Core/Type/ObjectType.php +++ /dev/null @@ -1,50 +0,0 @@ -__toString(); - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof ParenthesizedType) { - return $this->accepts($type->type); - } - if ($type instanceof ClassType) { - return Trinary::true(); - } - if ($type instanceof ObjectType) { - return Trinary::true(); - } - if ($type instanceof IntersectionType) { - return Trinary::true(); - } - if ($type instanceof UnionType) { - foreach ($type->types as $type) { - if ($this->accepts($type)->isTrue()) { - return Trinary::true(); - } - } - } - if ($type instanceof MixedType) { - return Trinary::maybe(); - } - if ($type instanceof MissingType) { - return Trinary::maybe(); - } - - return Trinary::false(); - } -} diff --git a/lib/WorseReflection/Core/Type/OctalLiteralType.php b/lib/WorseReflection/Core/Type/OctalLiteralType.php deleted file mode 100644 index fd8d61afe8..0000000000 --- a/lib/WorseReflection/Core/Type/OctalLiteralType.php +++ /dev/null @@ -1,29 +0,0 @@ -value; - } - - public function value(): int|float - { - return octdec(substr($this->value, 1)); - } - - public function generalize(): Type - { - return new IntType(); - } -} diff --git a/lib/WorseReflection/Core/Type/ParenthesizedType.php b/lib/WorseReflection/Core/Type/ParenthesizedType.php deleted file mode 100644 index c8242f4d17..0000000000 --- a/lib/WorseReflection/Core/Type/ParenthesizedType.php +++ /dev/null @@ -1,53 +0,0 @@ -type->__toString()); - } - - public function toPhpString(): string - { - return $this->type->toPhpString(); - } - - public function accepts(Type $type): Trinary - { - return $this->type->accepts($type); - } - - public function reduce(): Type - { - return $this->type; - } - - /** - * @return Types - */ - public function expandTypes(): Types - { - return $this->type->expandTypes(); - } - - public function allTypes(): Types - { - return $this->type->allTypes(); - } - - public function map(Closure $mapper): Type - { - return new self($this->type->map($mapper)); - } -} diff --git a/lib/WorseReflection/Core/Type/PrimitiveType.php b/lib/WorseReflection/Core/Type/PrimitiveType.php deleted file mode 100644 index fbefc3783e..0000000000 --- a/lib/WorseReflection/Core/Type/PrimitiveType.php +++ /dev/null @@ -1,9 +0,0 @@ -valueType = $keyType; - $this->keyType = null; - return; - } - - $this->valueType = $valueType; - $this->keyType = $keyType; - } - - public function __toString(): string - { - if ($this->valueType === null) { - return $this->toPhpString(); - } - if ($this->keyType === null) { - return sprintf('iterable<%s>', $this->valueType->__toString()); - } - - return sprintf('iterable<%s,%s>', $this->keyType->__toString(), $this->valueType->__toString()); - } - - public function toPhpString(): string - { - return 'iterable'; - } - - public function iterableValueType(): Type - { - return $this->valueType ?? new MissingType(); - } - - public function iterableKeyType(): Type - { - return $this->keyType ?? new ArrayKeyType(); - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof ArrayLiteral) { - return Trinary::fromBoolean( - $this->iterableKeyType()->accepts($type->keyType)->isTrue() && $this->iterableValueType()->accepts($type->valueType)->isTrue() - ); - } - return Trinary::fromBoolean($type instanceof ArrayType); - } - - public function expandTypes(): Types - { - return new Types([$this->iterableValueType()]); - } - - public function allTypes(): Types - { - $types = new Types([]); - foreach ($this->expandTypes() as $type) { - $types = $types->merge($type->allTypes()); - } - - return $types; - } - - public function map(Closure $mapper): Type - { - return new self( - $this->keyType ? $this->iterableKeyType()->map($mapper) : null, - $this->valueType ? $this->iterableValueType()->map($mapper) : null, - ); - } - - /** - * DANGEROUS: @see Phpactor\WorseReflection\Core\Inference\NodeToTypeConverter - */ - public function setValueType(Type $type): void - { - $this->valueType = $type; - } -} diff --git a/lib/WorseReflection/Core/Type/ReflectedClassType.php b/lib/WorseReflection/Core/Type/ReflectedClassType.php deleted file mode 100644 index 8d5c05f5ed..0000000000 --- a/lib/WorseReflection/Core/Type/ReflectedClassType.php +++ /dev/null @@ -1,222 +0,0 @@ -members = ClassLikeReflectionMemberCollection::empty(); - } - - public function __toString(): string - { - return $this->name->full(); - } - - public function toPhpString(): string - { - return $this->__toString(); - } - - /** - * @return ReflectionMemberCollection - */ - public function members(): ReflectionMemberCollection - { - $reflection = $this->reflectionOrNull(); - if (null === $reflection) { - return $this->members; - } - - return $this->members->merge($reflection->members()); - } - - public function isInvokable(): bool - { - $reflection = $this->reflectionOrNull(); - if (null === $reflection) { - return false; - } - - return $reflection->methods()->has('__invoke'); - } - - /** - * Accept if same class or class extends this class - */ - public function accepts(Type $type): Trinary - { - if ($type->equals($this)) { - return Trinary::true(); - } - - if ($type instanceof UnionType) { - foreach ($type->types as $uType) { - if (!$this->accepts($uType)->isTrue()) { - return Trinary::false(); - } - } - return Trinary::true(); - } - - if (!$type instanceof ClassType) { - return Trinary::false(); - } - - $reflectedThis = $this->reflectionOrNull(); - - if (null === $reflectedThis) { - return Trinary::maybe(); - } - - try { - $reflectedThat = $this->reflector->reflectClassLike($type->name()); - } catch (NotFound) { - return Trinary::maybe(); - } - - if ($reflectedThis instanceof ReflectionInterface || $reflectedThis instanceof ReflectionClass) { - return Trinary::fromBoolean($reflectedThat->isInstanceOf($reflectedThis->name())); - } - - if ($reflectedThat->name() == $this->name()) { - return Trinary::true(); - } - - if ($reflectedThat instanceof ReflectionClass) { - while ($parent = $reflectedThat->parent()) { - if ($parent->name() == $this->name()) { - return Trinary::true(); - } - $reflectedThat = $parent; - } - } - - return Trinary::false(); - } - - public function reflectionOrNull(): ?ReflectionClassLike - { - try { - return $this->reflector->reflectClassLike($this->name()); - } catch (NotFound $e) { - } - return null; - } - - public function iterableValueType(): Type - { - $class = $this->reflectionOrNull(); - if (!$class instanceof ReflectionClassLike) { - return new MissingType(); - } - $scope = $class->scope(); - - assert($class instanceof ReflectionClassLike); - $genericTypes = array_merge($class->docblock()->implements(), $class->docblock()->extends()); - - foreach ($genericTypes as $genericType) { - if (!$genericType instanceof GenericClassType) { - continue; - } - - $type = IterableTypeResolver::resolveIterable($this->reflector, $genericType, $genericType->arguments()); - if (!$type->isDefined()) { - continue; - } - return $type; - } - - return new MissingType(); - } - - public function instanceof(Type $type): Trinary - { - if ($type instanceof MissingType) { - return Trinary::maybe(); - } - - if ( - !$type instanceof StringType && - !$type instanceof ClassType - ) { - return Trinary::false(); - } - - $reflection = $this->reflectionOrNull(); - - if (!$reflection) { - return Trinary::maybe(); - } - - if ($type instanceof StringLiteralType) { - return Trinary::fromBoolean($reflection->isInstanceOf(ClassName::fromString($type->value()))); - } - if ($type instanceof ClassType) { - return Trinary::fromBoolean($reflection->isInstanceOf($type->name())); - } - - return Trinary::maybe(); - } - - /** - * If the class type has a template, then upcast it - */ - public function upcastToGeneric(): ReflectedClassType - { - if ($this instanceof GenericClassType) { - return $this; - } - $reflection = $this->reflectionOrNull(); - if (!$reflection) { - return $this; - } - - if (0 === $reflection->templateMap()->count()) { - return $this; - } - - return new GenericClassType($this->reflector, $this->name(), $reflection->templateMap()->toArray()); - } - - public function isInterface(): Trinary - { - $reflection = $this->reflectionOrNull(); - if (null === $reflection) { - return Trinary::maybe(); - } - - return Trinary::fromBoolean($reflection instanceof ReflectionInterface); - } - - public function invokeType(): Type - { - $reflection = $this->reflectionOrNull(); - if (null === $reflection) { - return TypeFactory::undefined(); - } - - try { - return $reflection->methods()->get('__invoke')->inferredType(); - } catch (NotFound) { - return TypeFactory::undefined(); - } - } -} diff --git a/lib/WorseReflection/Core/Type/Resolver/IterableTypeResolver.php b/lib/WorseReflection/Core/Type/Resolver/IterableTypeResolver.php deleted file mode 100644 index 5aa9429175..0000000000 --- a/lib/WorseReflection/Core/Type/Resolver/IterableTypeResolver.php +++ /dev/null @@ -1,100 +0,0 @@ - - */ - public static function iterableClasses(): array - { - return [ - 'IteratorAggregate', - 'Iterator', - 'Traversable', - 'iterable', - ]; - } - /** - * @param Type[] $arguments - */ - public static function resolveIterable(ClassReflector $reflector, Type $type, array $arguments): Type - { - $genericMapResolver = new GenericMapResolver($reflector); - - if (!$type instanceof ClassType) { - return new MissingType(); - } - - if ($type->name()->__toString() === 'Generator') { - if (count($arguments) === 1) { - return $arguments[0]; - } - - if (isset($arguments[1])) { - return $arguments[1]; - } - } - - $iterableClasses = self::iterableClasses(); - - if (in_array($type->name()->__toString(), $iterableClasses)) { - return self::valueTypeFromArgs($arguments); - } - - if (!$type instanceof ReflectedClassType) { - return new MissingType(); - } - - $class = $type->reflectionOrNull(); - - if (null === $class) { - return new MissingType(); - } - - foreach ($iterableClasses as $iterableClassName) { - if (false === $class->isInstanceOf(ClassName::fromString($iterableClassName))) { - continue; - } - - $templateMap = $genericMapResolver->resolveClassTemplateMap( - $class->type(), - ClassName::fromString($iterableClassName), - $type instanceof GenericClassType ? $type->arguments() : [] - ); - - if (null !== $templateMap) { - $value = $templateMap->get('TValue'); - if (!$value->isDefined()) { - return $templateMap->get('TKey'); - } - return $value; - } - } - - return new MissingType(); - } - - /** - * @param Type[] $arguments - */ - private static function valueTypeFromArgs(array $arguments): Type - { - if (isset($arguments[1])) { - return $arguments[1]; - } - - return $arguments[0] ?? new MissingType(); - } -} diff --git a/lib/WorseReflection/Core/Type/ResourceType.php b/lib/WorseReflection/Core/Type/ResourceType.php deleted file mode 100644 index ebb1925a60..0000000000 --- a/lib/WorseReflection/Core/Type/ResourceType.php +++ /dev/null @@ -1,24 +0,0 @@ -toPhpString(); - } - - public function accepts(Type $type): Trinary - { - if ($type->equals($this)) { - return Trinary::true(); - } - - if ($type instanceof $this) { - return Trinary::true(); - } - - if ($type instanceof MixedType) { - return Trinary::maybe(); - } - - if ($type instanceof MissingType) { - return Trinary::maybe(); - } - - return Trinary::false(); - } -} diff --git a/lib/WorseReflection/Core/Type/SelfType.php b/lib/WorseReflection/Core/Type/SelfType.php deleted file mode 100644 index e26d6c6c88..0000000000 --- a/lib/WorseReflection/Core/Type/SelfType.php +++ /dev/null @@ -1,41 +0,0 @@ -class) { - return sprintf('self(%s)', $this->class->__toString()); - } - return 'self'; - } - - public function toPhpString(): string - { - return 'self'; - } - - public function accepts(Type $type): Trinary - { - return Trinary::maybe(); - } - - public function type(): Type - { - if ($this->class) { - return $this->class; - } - - return TypeFactory::undefined(); - } -} diff --git a/lib/WorseReflection/Core/Type/StaticType.php b/lib/WorseReflection/Core/Type/StaticType.php deleted file mode 100644 index a9f7ea8c00..0000000000 --- a/lib/WorseReflection/Core/Type/StaticType.php +++ /dev/null @@ -1,50 +0,0 @@ -class) { - return sprintf('static(%s)', $this->class->__toString()); - } - return 'static'; - } - - public function type(): Type - { - if ($this->class) { - return $this->class; - } - - return TypeFactory::undefined(); - } - - public function toPhpString(): string - { - return 'static'; - } - - public function accepts(Type $type): Trinary - { - return Trinary::maybe(); - } - - public function map(Closure $mapper): Type - { - if (!$this->class) { - return $mapper($this); - } - return $mapper(new static($mapper($this->class))); - } -} diff --git a/lib/WorseReflection/Core/Type/StringLiteralType.php b/lib/WorseReflection/Core/Type/StringLiteralType.php deleted file mode 100644 index e14929264b..0000000000 --- a/lib/WorseReflection/Core/Type/StringLiteralType.php +++ /dev/null @@ -1,57 +0,0 @@ -value = (function (string $value, int $length) { - if (strlen($value) > $length) { - return substr($value, 0, -3) . '...'; - } - return $value; - })($value, 255); - } - - public function __toString(): string - { - return sprintf('"%s"', $this->value); - } - - public function value(): string - { - return $this->value; - } - - public function generalize(): Type - { - return new StringType(); - } - - public function concat(Type $right): Type - { - if ($right instanceof StringLiteralType) { - return new self(sprintf('%s%s', $this->value, (string)$right->value())); - } - return new StringType(); - } - - public function accepts(Type $type): Trinary - { - if ($type instanceof StringLiteralType) { - return Trinary::fromBoolean($type->equals($this)); - } - - if ($type instanceof StringType) { - return Trinary::maybe(); - } - - return parent::accepts($type); - } -} diff --git a/lib/WorseReflection/Core/Type/StringType.php b/lib/WorseReflection/Core/Type/StringType.php deleted file mode 100644 index ec13c4163f..0000000000 --- a/lib/WorseReflection/Core/Type/StringType.php +++ /dev/null @@ -1,18 +0,0 @@ -class) { - return sprintf('$this(%s)', $this->class->__toString()); - } - return '$this'; - } -} diff --git a/lib/WorseReflection/Core/Type/UnionType.php b/lib/WorseReflection/Core/Type/UnionType.php deleted file mode 100644 index 5d04192344..0000000000 --- a/lib/WorseReflection/Core/Type/UnionType.php +++ /dev/null @@ -1,56 +0,0 @@ - $type->__toString(), $this->types)); - } - - public static function toUnion(Type $type): AggregateType - { - if ($type instanceof NullableType) { - return self::toUnion($type->type)->addType(TypeFactory::null()); - } - if ($type instanceof UnionType) { - return $type; - } - - return new UnionType($type); - } - - public function withTypes(Type ...$types): AggregateType - { - return new self(...$types); - } - - public function toPhpString(): string - { - return implode('|', array_map(fn (Type $type) => $type->toPhpString(), $this->types)); - } - - public function accepts(Type $type): Trinary - { - $maybe = false; - foreach ($this->types as $uType) { - if ($uType->accepts($type)->isTrue()) { - return Trinary::true(); - } - if ($uType->accepts($type)->isMaybe()) { - $maybe = true; - } - } - - if ($maybe) { - return Trinary::maybe(); - } - - return Trinary::false(); - } -} diff --git a/lib/WorseReflection/Core/Type/VoidType.php b/lib/WorseReflection/Core/Type/VoidType.php deleted file mode 100644 index 469a75673d..0000000000 --- a/lib/WorseReflection/Core/Type/VoidType.php +++ /dev/null @@ -1,24 +0,0 @@ -__toString(); - } - - public function accepts(Type $type): Trinary - { - return Trinary::false(); - } -} diff --git a/lib/WorseReflection/Core/TypeFactory.php b/lib/WorseReflection/Core/TypeFactory.php deleted file mode 100644 index db2bab6270..0000000000 --- a/lib/WorseReflection/Core/TypeFactory.php +++ /dev/null @@ -1,491 +0,0 @@ - $elements - */ - public static function arrayLiteral(array $elements): ArrayLiteral - { - return new ArrayLiteral($elements); - } - - public static function fromNumericString(string $value): NumericType - { - return self::convertNumericStringToInternalType( - // Strip PHP 7.4 underscorse separator before comparison - str_replace('_', '', $value) - ); - } - - public static function not(Type $type): NotType - { - return new NotType($type); - } - - public static function unionEmpty(): UnionType - { - return new UnionType( - new IntLiteralType(0), - new FloatLiteralType(0.0), - new StringLiteralType(''), - new StringLiteralType('0'), - new ArrayLiteral([]), - new BooleanLiteralType(false), - new NullType() - ); - } - - /** - * @param mixed[] $values - * @return Type[] - */ - public static function fromValues(array $values): array - { - return array_map(fn ($value) => self::fromValue($value), $values); - } - - public static function parenthesized(Type $type): ParenthesizedType - { - return new ParenthesizedType($type); - } - - public static function toAggregateOrUnion(Type $type): AggregateType - { - if ($type instanceof AggregateType) { - return $type; - } - - return UnionType::toUnion($type); - } - - public static function toAggregateOrIntersection(Type $type): AggregateType - { - if ($type instanceof AggregateType) { - return $type; - } - - return IntersectionType::toIntersection($type); - } - - public static function generator(Reflector $reflector, Type $keyType, Type $valueType): GenericClassType - { - return new GeneratorType($reflector, $keyType, $valueType); - } - - public static function arrayKey(): ArrayKeyType - { - return new ArrayKeyType(); - } - - /** - * @param array $typeMap - */ - public static function arrayShape(array $typeMap): ArrayShapeType - { - return new ArrayShapeType($typeMap); - } - - public static function list(?Type $iterabletype = null): ArrayType - { - return new ArrayType(self::int(), $iterabletype ?: self::mixed()); - } - - public static function never(): NeverType - { - return new NeverType(); - } - - public static function false(): FalseType - { - return new FalseType(); - } - - public static function classString(string $classFqn): ClassStringType - { - return new ClassStringType(ClassName::fromString($classFqn)); - } - - public static function static(?Type $type = null): StaticType - { - return new StaticType($type); - } - - public static function this(?Type $type = null): ThisType - { - return new ThisType($type); - } - - public static function enumCaseType(Reflector $reflector, ClassType $enumType, string $name): EnumCaseType - { - return new EnumCaseType($reflector, $enumType, $name); - } - - public static function enumBackedCaseType(Reflector $reflector, ClassType $enumType, string $name, Type $value): EnumBackedCaseType - { - return new EnumBackedCaseType($reflector, $enumType, $name, $value); - } - - public static function intRange(Type $lower, Type $upper): IntRangeType - { - return new IntRangeType($lower, $upper); - } - - public static function intPositive(): IntPositive - { - return new IntPositive(); - } - - public static function intNegative(): IntNegative - { - return new IntNegative(); - } - - private static function typeFromString(string $type, ?Reflector $reflector = null): Type - { - if ('' === $type) { - return self::unknown(); - } - - if ($type === 'string') { - return self::string(); - } - - if ($type === 'int') { - return self::int(); - } - - if ($type === 'float') { - return self::float(); - } - - if ($type === 'array') { - return self::array(); - } - - if ($type === 'bool') { - return self::bool(); - } - - if ($type === 'mixed') { - return self::mixed(); - } - - if ($type === 'null') { - return self::null(); - } - - if ($type === 'object') { - return self::object(); - } - - if ($type === 'void') { - return self::void(); - } - - if ($type === 'callable') { - return self::callable(); - } - - if ($type === 'resource') { - return self::resource(); - } - - if ($type === 'iterable') { - return self::iterable(); - } - - if ($type === 'self') { - return new SelfType(); - } - - if ($type === 'static') { - return new StaticType(); - } - - if ($type === 'class-string') { - return new ClassStringType(); - } - - if ($type === '$this') { - return new StaticType(); - } - - if ($type === 'never') { - return new NeverType(); - } - - if ($type === 'false') { - return new FalseType(); - } - - return self::class(ClassName::fromString($type), $reflector); - } - - - private static function convertNumericStringToInternalType(string $value): NumericType - { - if (1 === preg_match('/^[1-9][0-9]*$/', $value)) { - return self::intLiteral((int)$value); - } - if (1 === preg_match('/^0[xX][0-9a-fA-F]+$/', $value)) { - return new HexLiteralType($value); - } - if (1 === preg_match('/^0[0-7]+$/', $value)) { - return new OctalLiteralType($value); - } - if (1 === preg_match('/^0[bB][01]+$/', $value)) { - return new BinLiteralType($value); - } - - if (!str_contains($value, '.')) { - return self::intLiteral((int)$value); - } - - return self::floatLiteral((float)$value); - } -} diff --git a/lib/WorseReflection/Core/TypeResolver.php b/lib/WorseReflection/Core/TypeResolver.php deleted file mode 100644 index 7c412ec6ec..0000000000 --- a/lib/WorseReflection/Core/TypeResolver.php +++ /dev/null @@ -1,8 +0,0 @@ - - */ -final class Types implements IteratorAggregate -{ - /** - * @param T[] $types - */ - public function __construct(private array $types) - { - } - - public function __toString(): string - { - return implode(', ', array_map(fn (Type $t) => $t->__toString(), $this->types)); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->types); - } - - /** - * @return T|null - */ - public function firstOrNull(): ?Type - { - if ($this->types === []) { - return null; - } - - return reset($this->types); - } - - /** - * @return Types - * @param Closure(Type): bool $predicate - */ - public function filter(Closure $predicate): Types - { - return new self(array_filter($this->types, $predicate)); - } - - /** - * @param Types $types - * @return Types - */ - public function merge(Types $types): self - { - $merged = $this->types; - foreach ($types as $type) { - $merged[] = $type; - } - - return new self($merged); - } - - /** - * Retrurns all class-like types - * @return Types - */ - public function classLike(): Types - { - // @phpstan-ignore-next-line no support for conditional types https://github.com/phpstan/phpstan/issues/3853 - return $this->filter(fn (Type $type) => $type instanceof ClassLikeType); - } - - public function at(int $index): Type - { - return $this->types[$index] ?? new MissingType(); - } - - /** - * @return list - */ - public function toArray(): array - { - return array_values($this->types); - } -} diff --git a/lib/WorseReflection/Core/Util/NodeUtil.php b/lib/WorseReflection/Core/Util/NodeUtil.php deleted file mode 100644 index 2a0470084e..0000000000 --- a/lib/WorseReflection/Core/Util/NodeUtil.php +++ /dev/null @@ -1,435 +0,0 @@ - $importTable - */ - public static function resolveNameFromImportTable(Node $node, array $importTable): ?ResolvedName - { - if (!$node instanceof QualifiedName) { - return null; - } - $content = $node->getFileContents(); - $nameParts = $node->getNameParts(); - if (count($nameParts) === 0) { - return null; - } - $base = $nameParts[0]->getText($content); - - if (isset($importTable[$base])) { - $resolvedName = $importTable[$base]; - $resolvedName->addNameParts(\array_slice($node->getNameParts(), 1), $content); - return $resolvedName; - } - return null; - } - - public static function nodeContainerClassLikeType(Reflector $reflector, Node $node): Type - { - $classNode = self::nodeContainerClassLikeDeclaration($node); - - if (null === $classNode) { - return TypeFactory::undefined(); - } - - assert($classNode instanceof NamespacedNameInterface); - - return TypeFactory::fromStringWithReflector($classNode->getNamespacedName(), $reflector); - } - - /** - * @return ClassDeclaration|TraitDeclaration|InterfaceDeclaration|null - */ - public static function nodeContainerClassLikeDeclaration(Node $node): ?Node - { - $ancestor = $node->getFirstAncestor(ObjectCreationExpression::class, ClassLike::class); - - if ($ancestor instanceof ObjectCreationExpression) { - if ($ancestor->classTypeDesignator instanceof Token) { - if ($ancestor->classTypeDesignator->kind == TokenKind::ClassKeyword) { - // Resolving anonymous classes is not currently supported - return null; - } - } - - return self::nodeContainerClassLikeDeclaration($ancestor); - } - - /** @var ClassDeclaration|TraitDeclaration|InterfaceDeclaration|null */ - return $ancestor; - } - - /** - * @param Token|Node|mixed $nodeOrToken - */ - public static function nameFromTokenOrNode(Node $node, $nodeOrToken): string - { - if ($nodeOrToken instanceof Token) { - return (string)$nodeOrToken->getText($node->getFileContents()); - } - if ($nodeOrToken instanceof Node) { - return (string)$nodeOrToken->getText(); - } - - return ''; - } - - /** - * @param Token|QualifiedName|mixed $name - */ - public static function nameFromTokenOrQualifiedName(Node $node, $name): string - { - if ($name instanceof Token) { - return (string)$name->getText($node->getFileContents()); - } - if ($name instanceof QualifiedName) { - return $name->__toString(); - } - - return ''; - } - - public static function qualifiedNameListContains(?QualifiedNameList $list, string $name): bool - { - if (null === $list) { - return false; - } - foreach ($list->getElements() as $element) { - if (!$element instanceof QualifiedName) { - continue; - } - if ((string)$element->getResolvedName() === $name) { - return true; - } - } - - return false; - } - - public static function qualfiiedNameIs(?QualifiedName $qualifiedName, string $name): bool - { - if (null === $qualifiedName) { - return false; - } - - return (string)$qualifiedName->getResolvedName() === $name; - } - - public static function shortName(Node $node): string - { - if (!$node instanceof QualifiedName) { - return ''; - } - - $parts = $node->getNameParts(); - $last = array_pop($parts); - - if (!$last instanceof Token) { - return ''; - } - - return (string)$last->getText($node->getFileContents()); - } - - public static function operatorKindForUnaryExpression(UnaryExpression $node): int - { - foreach ($node->getChildTokens() as $token) { - assert($token instanceof Token); - return $token->kind; - } - - return 0; - } - - /** - * For debugging: pretty print the AST - */ - public static function dump(Node|Token|null $node, ?Node $referenceNode = null, int $level = 0, ?string $name = null): string - { - if ($node === null) { - return 'null'; - } - if ($node instanceof Token) { - return sprintf( - '%s%sToken<%s>: %d:%d - %s', - str_repeat(' ', $level), - $name ? '$' . $name . ':' : '', - Token::getTokenKindNameFromValue($node->kind), - $node->getStartPosition(), - $node->getEndPosition(), - $referenceNode ? $node->getText($referenceNode->getFileContents()) : '', - ); - } - $out = [ - sprintf( - '%s%s%s (%s) %d:%d - %s', - str_repeat(' ', $level), - $name ? '$'.$name . ':' : '', - $node->getNodeKindName(), - spl_object_id($node), - $node->getStartPosition(), - $node->getEndPosition(), - str_replace("\n", '\\n', $node->getText()), - ) - ]; - - $level++; - foreach ($node->getChildNodesAndTokens() as $name => $child) { - assert(is_string($name)); - if ($child instanceof Node) { - $referenceNode = $child; - } - $out[] = self::dump($child, $referenceNode, $level, $name); - } - - return implode("\n", $out); - } - - /** - * @param null|Node|Token $nodeOrToken - */ - public static function typeFromQualfiedNameLike(Reflector $reflector, Node $node, $nodeOrToken, ?ClassName $classContext = null): Type - { - if ($nodeOrToken instanceof Token) { - $text = (string)$nodeOrToken->getText($node->getFileContents()); - - if ($text === 'static' && $classContext) { - $class = self::nodeContainerClassLikeDeclaration($node); - return TypeFactory::reflectedClass($reflector, $classContext->__toString()); - } - - return TypeFactory::fromStringWithReflector( - $text, - $reflector - ); - } - - if ($nodeOrToken instanceof QualifiedName) { - $text = $nodeOrToken->getText(); - if ($nodeOrToken->isUnqualifiedName() && in_array($text, self::RESERVED_NAMES)) { - return TypeFactory::fromStringWithReflector($text, $reflector); - } - - if ($text === 'self') { - return new SelfType(); - } - - if ($text === 'static') { - $class = self::nodeContainerClassLikeDeclaration($node); - return TypeFactory::reflectedClass($reflector, $classContext->__toString()); - } - - return TypeFactory::fromStringWithReflector($nodeOrToken->getResolvedName(), $reflector); - } - - if ($nodeOrToken instanceof QualifiedNameList) { - $isIntersection = false; - $types = array_filter(array_map(function ($name) use ($node, $reflector, &$isIntersection, $classContext) { - if ($name instanceof Token && $name->kind === TokenKind::AmpersandToken) { - $isIntersection = true; - return false; - } - if (null === $name) { - return new MissingType(); - } - return self::typeFromQualfiedNameLike($reflector, $node, $name, $classContext); - }, iterator_to_array($nodeOrToken->getElements(), true)), fn ($name) => $name !== false); - - return ($isIntersection ? IntersectionType::fromTypes(...$types) : UnionType::fromTypes(...$types))->reduce(); - } - - return TypeFactory::unknown(); - } - - public static function canAcceptTypeAssertion(Node ...$nodes): bool - { - foreach ($nodes as $node) { - if ($node instanceof Variable) { - return true; - } - - if ($node instanceof MemberAccessExpression) { - return true; - } - } - - return false; - } - - /** - * Return the descendant first node after the given offset - */ - public static function firstDescendantNodeAfterOffset(Node $node, int $offset): Node - { - foreach ($node->getDescendantNodes() as $node) { - if ($node->getStartPosition() > $offset) { - return $node; - } - } - - return $node; - } - public static function firstDescendantNodeBeforeOffset(Node $node, int $offset): Node - { - $lastNode = null; - foreach ($node->getDescendantNodes() as $node) { - if ($node->getStartPosition() >= $offset) { - return $lastNode ?? $node; - } - $lastNode = $node; - } - - return $node; - } - public static function lastDescendantNodeBeforeOffsetOfType(Node $node, int $offset, string $fqn): Node - { - $best = null; - $bestPos = null; - foreach ($node->getDescendantNodes() as $descendant) { - if (!$descendant instanceof $fqn) { - continue; - } - if ($descendant->getEndPosition() > $offset) { - continue; - } - if (null === $bestPos || $descendant->getEndPosition() > $bestPos) { - $best = $descendant; - $bestPos = $descendant->getEndPosition(); - } - } - - return $best ?? $node; - } - - public static function previousSibling(?Node $node): ?Node - { - if (null === $node) { - return null; - } - $parent = $node->parent; - if (null === $parent) { - return null; - } - $previous = null; - foreach ($parent->getChildNodes() as $childNode) { - if (null === $previous) { - $previous = $childNode; - continue; - } - if ($childNode === $node) { - return $previous; - } - $previous = $childNode; - } - - return null; - } - - public static function namespace(Node $node): ?string - { - $namespace = $node->getNamespaceDefinition(); - - if (null === $namespace) { - return null; - } - - if (!$namespace->name instanceof QualifiedName) { - return null; - } - - return $namespace->name->__toString(); - } - - public static function nullOrMissing(mixed $subject): bool - { - if (null === $subject) { - return true; - } - - if ($subject instanceof MissingToken) { - return true; - } - - return false; - } - - public static function byteOffsetRangeForNode(Node $node): ByteOffsetRange - { - return ByteOffsetRange::fromInts($node->getStartPosition(), $node->getEndPosition()); - } - - /** - * @return ?int<0,max> - */ - public static function argumentOffset(ArgumentExpressionList $argumentExpressionList, ArgumentExpression $argument): ?int - { - $offset = 0; - foreach ($argumentExpressionList->getChildNodes() as $funcArg) { - if ($argument === $funcArg) { - return $offset; - } - $offset++; - } - - return null; - } - - public static function isFirstClassCallable(?Node $node): bool - { - if (!$node instanceof CallExpression) { - return false; - } - - foreach ($node?->argumentExpressionList->children ?? [] as $child) { - if (!$child instanceof ArgumentExpression) { - continue; - } - - - if ($child->dotDotDotToken !== null && $child->expression === null) { - return true; - } - - break; - } - - return false; - } -} diff --git a/lib/WorseReflection/Core/Util/OriginalMethodResolver.php b/lib/WorseReflection/Core/Util/OriginalMethodResolver.php deleted file mode 100644 index 6eea0ed7c6..0000000000 --- a/lib/WorseReflection/Core/Util/OriginalMethodResolver.php +++ /dev/null @@ -1,71 +0,0 @@ -declaringClass(); - return $this->doResolveOriginalMember($classLike, $method); - } - - private function doResolveOriginalMember( - ReflectionClassLike $classLike, - ReflectionMember $member - ): ReflectionMember { - $members = $classLike->members()->byMemberType($member->memberType()); - - if ($members->has($member->name())) { - $member = $members->get($member->name()); - } - - if ($classLike instanceof ReflectionClass) { - return $this->resolveClass($classLike, $member); - } - - if ($classLike instanceof ReflectionInterface) { - return $this->resolveInterface($classLike, $member); - } - - return $member; - } - - private function resolveClass(ReflectionClass $classLike, ReflectionMember $member): ReflectionMember - { - $parent = $classLike->parent(); - - if ($parent !== null) { - $member = $this->doResolveOriginalMember( - $classLike->parent(), - $member - ); - } - - foreach ($classLike->interfaces() as $interface) { - $member = $this->doResolveOriginalMember( - $interface, - $member - ); - } - - return $member; - } - - private function resolveInterface(ReflectionInterface $classLike, ReflectionMember $member): ReflectionMember - { - foreach ($classLike->parents() as $parent) { - $member = $this->doResolveOriginalMember( - $parent, - $member - ); - } - - return $member; - } -} diff --git a/lib/WorseReflection/Core/Util/QualifiedNameListUtil.php b/lib/WorseReflection/Core/Util/QualifiedNameListUtil.php deleted file mode 100644 index f97b24b721..0000000000 --- a/lib/WorseReflection/Core/Util/QualifiedNameListUtil.php +++ /dev/null @@ -1,91 +0,0 @@ -children as $child) { - if (!$child instanceof QualifiedName) { - continue; - } - return $child; - } - - return null; - } - - /** - * @return MissingToken|Token|QualifiedName|null - */ - public static function firstQualifiedNameOrNullOrToken(QualifiedNameList|null|MissingToken $types) - { - if (!$types instanceof QualifiedNameList) { - return null; - } - - foreach ($types->children as $child) { - if (!$child instanceof QualifiedName && !$child instanceof Token) { - continue; - } - return $child; - } - - return null; - } -} diff --git a/lib/WorseReflection/Core/Virtual/ChainReflectionMemberProvider.php b/lib/WorseReflection/Core/Virtual/ChainReflectionMemberProvider.php deleted file mode 100644 index 6af3584e6b..0000000000 --- a/lib/WorseReflection/Core/Virtual/ChainReflectionMemberProvider.php +++ /dev/null @@ -1,34 +0,0 @@ -providers = $providers; - } - - public function provideMembers(ServiceLocator $locator, ReflectionClassLike $class): ReflectionMemberCollection - { - $virtualMethods = ClassLikeReflectionMemberCollection::empty(); - foreach ($this->providers as $provider) { - /** @phpstan-ignore-next-line */ - $virtualMethods = $virtualMethods->merge($provider->provideMembers($locator, $class)); - } - - return $virtualMethods; - } -} diff --git a/lib/WorseReflection/Core/Virtual/DummyReflectionScope.php b/lib/WorseReflection/Core/Virtual/DummyReflectionScope.php deleted file mode 100644 index 7fdab7201f..0000000000 --- a/lib/WorseReflection/Core/Virtual/DummyReflectionScope.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ - public function provideMembers(ServiceLocator $locator, ReflectionClassLike $class): ReflectionMemberCollection; -} diff --git a/lib/WorseReflection/Core/Virtual/StubFileMemberProvider.php b/lib/WorseReflection/Core/Virtual/StubFileMemberProvider.php deleted file mode 100644 index 2e1c689f0b..0000000000 --- a/lib/WorseReflection/Core/Virtual/StubFileMemberProvider.php +++ /dev/null @@ -1,64 +0,0 @@ - - */ - private array $stubClasses = []; - - private bool $initialized = false; - - /** - * @param list $stubFiles - */ - public function __construct(private array $stubFiles) - { - } - - public function provideMembers(ServiceLocator $locator, ReflectionClassLike $class): ReflectionMemberCollection - { - $this->buildMap($locator); - - if (!isset($this->stubClasses[$class->name()->__toString()])) { - return ChainReflectionMemberCollection::fromCollections([]); - } - - $stubClass = $this->stubClasses[$class->name()->__toString()]; - return $stubClass->members(); - } - - private function buildMap(ServiceLocator $locator): void - { - if ($this->initialized === true) { - return; - } - - $classes = []; - foreach ($this->stubFiles as $stubFile) { - try { - $document = TextDocumentBuilder::fromUri($stubFile)->language('php')->build(); - } catch (RuntimeException) { - // depend on validation on startup rather than break everything - continue; - } - foreach ($locator->stubReflector()->reflectClassesIn( - $document, - ) as $class) { - $classes[$class->name()->__toString()] = $class; - } - } - - $this->stubClasses = $classes; - $this->initialized = true; - } -} diff --git a/lib/WorseReflection/Core/Virtual/VirtualReflectionFunction.php b/lib/WorseReflection/Core/Virtual/VirtualReflectionFunction.php deleted file mode 100644 index 5a6c67c481..0000000000 --- a/lib/WorseReflection/Core/Virtual/VirtualReflectionFunction.php +++ /dev/null @@ -1,104 +0,0 @@ -parameters; - } - - public function body(): NodeText - { - return $this->body; - } - - public function position(): ByteOffsetRange - { - return $this->range; - } - - public function frame(): Frame - { - return $this->frame; - } - - public function docblock(): DocBlock - { - return $this->docblock; - } - - public function scope(): ReflectionScope - { - return $this->scope; - } - - public function inferredType(): Type - { - return $this->inferredType; - } - - public function type(): Type - { - return $this->type; - } - - public function sourceCode(): TextDocument - { - return $this->source; - } - - public function name(): Name - { - return $this->name; - } -} diff --git a/lib/WorseReflection/Core/Virtual/VirtualReflectionMember.php b/lib/WorseReflection/Core/Virtual/VirtualReflectionMember.php deleted file mode 100644 index 87c402ffc0..0000000000 --- a/lib/WorseReflection/Core/Virtual/VirtualReflectionMember.php +++ /dev/null @@ -1,155 +0,0 @@ -contextualizer = new MemberTypeContextualiser(); - } - - public function position(): ByteOffsetRange - { - return $this->position; - } - - public function declaringClass(): ReflectionClassLike - { - return $this->declaringClass; - } - - /** - * @return $this - */ - public function withDeclaringClass(ReflectionClassLike $contextClass): self - { - $new = clone $this; - $new->declaringClass = $contextClass; - return $new; - } - - /** - * @return $this - */ - public function withVisibility(Visibility $visibility): self - { - $new = clone $this; - $new->visibility = $visibility; - return $new; - } - - public function class(): ReflectionClassLike - { - return $this->class; - } - - public function name(): string - { - return $this->name; - } - - public function nameRange(): ByteOffsetRange - { - return ByteOffsetRange::fromInts( - $this->position()->start()->toInt(), - $this->position()->end()->toInt(), - ); - } - - /** - * @return $this - */ - public function withName(string $name): self - { - $new = clone $this; - $new->name = $name; - return $new; - } - - /** - * @return $this - */ - public function withInferredType(Type $type): self - { - $new = clone $this; - $new->inferredType = $type; - - return $new; - } - - /** - * @return $this - */ - public function withType(Type $type): self - { - $new = clone $this; - $new->type = $type; - - return $new; - } - - public function frame(): Frame - { - return $this->frame; - } - - public function docblock(): DocBlock - { - return $this->docblock; - } - - public function scope(): ReflectionScope - { - return $this->scope; - } - - public function visibility(): Visibility - { - return $this->visibility; - } - - public function inferredType(): Type - { - return $this->contextualizer->contextualise($this->declaringClass, $this->class, $this->inferredType); - } - - public function type(): Type - { - return $this->contextualizer->contextualise($this->declaringClass, $this->class, $this->type); - } - - public function original(): ReflectionMember - { - return $this; - } - - public function deprecation(): Deprecation - { - return $this->deprecation; - } -} diff --git a/lib/WorseReflection/Core/Virtual/VirtualReflectionMethod.php b/lib/WorseReflection/Core/Virtual/VirtualReflectionMethod.php deleted file mode 100644 index 904dfba9e9..0000000000 --- a/lib/WorseReflection/Core/Virtual/VirtualReflectionMethod.php +++ /dev/null @@ -1,104 +0,0 @@ -position(), - $reflectionMethod->declaringClass(), - $reflectionMethod->class(), - $reflectionMethod->name(), - $reflectionMethod->frame(), - $reflectionMethod->docblock(), - $reflectionMethod->scope(), - $reflectionMethod->visibility(), - $reflectionMethod->inferredType(), - $reflectionMethod->type(), - $reflectionMethod->parameters(), - $reflectionMethod->body(), - $reflectionMethod->isAbstract(), - $reflectionMethod->isStatic(), - $reflectionMethod->deprecation() - ); - } - - public function parameters(): ReflectionParameterCollection - { - return $this->parameters; - } - - public function body(): NodeText - { - return $this->body; - } - - public function returnType(): Type - { - return $this->type(); - } - - public function isAbstract(): bool - { - return $this->isAbstract; - } - - public function isStatic(): bool - { - return $this->isStatic; - } - - public function isVirtual(): bool - { - return true; - } - - public function memberType(): string - { - return ReflectionMember::TYPE_METHOD; - } - - public function withClass(ReflectionClassLike $class): ReflectionMember - { - $new = clone $this; - $new->class = $class; - return $new; - } -} diff --git a/lib/WorseReflection/Core/Virtual/VirtualReflectionParameter.php b/lib/WorseReflection/Core/Virtual/VirtualReflectionParameter.php deleted file mode 100644 index ef06f851d6..0000000000 --- a/lib/WorseReflection/Core/Virtual/VirtualReflectionParameter.php +++ /dev/null @@ -1,93 +0,0 @@ -scope; - } - - public function position(): ByteOffsetRange - { - return $this->position; - } - - public function name(): string - { - return $this->name; - } - - public function method(): ReflectionFunctionLike - { - return $this->functionLike; - } - - public function functionLike(): ReflectionFunctionLike - { - return $this->functionLike; - } - - public function type(): Type - { - return $this->type; - } - - public function inferredType(): Type - { - return $this->inferredType; - } - - public function default(): DefaultValue - { - return $this->default; - } - - public function byReference(): bool - { - return $this->byReference; - } - - public function isPromoted(): bool - { - return false; - } - - public function isVariadic(): bool - { - return false; - } - - public function index(): int - { - return $this->index; - } - - public function docblock(): DocBlock - { - return new PlainDocblock(''); - } -} diff --git a/lib/WorseReflection/Core/Virtual/VirtualReflectionProperty.php b/lib/WorseReflection/Core/Virtual/VirtualReflectionProperty.php deleted file mode 100644 index fdd48c713d..0000000000 --- a/lib/WorseReflection/Core/Virtual/VirtualReflectionProperty.php +++ /dev/null @@ -1,37 +0,0 @@ -class = $class; - return $new; - } -} diff --git a/lib/WorseReflection/Core/Visibility.php b/lib/WorseReflection/Core/Visibility.php deleted file mode 100644 index d00e09a914..0000000000 --- a/lib/WorseReflection/Core/Visibility.php +++ /dev/null @@ -1,51 +0,0 @@ -visibility; - } - - public static function public(): self - { - return self::create('public'); - } - - public static function private(): self - { - return self::create('private'); - } - - public static function protected(): self - { - return self::create('protected'); - } - - public function isPublic(): bool - { - return $this->visibility === 'public'; - } - - public function isProtected(): bool - { - return $this->visibility === 'protected'; - } - - public function isPrivate(): bool - { - return $this->visibility === 'private'; - } - - private static function create(string $visibility): self - { - return new self($visibility); - } -} diff --git a/lib/WorseReflection/Reflector.php b/lib/WorseReflection/Reflector.php deleted file mode 100644 index 759f0d0cd5..0000000000 --- a/lib/WorseReflection/Reflector.php +++ /dev/null @@ -1,12 +0,0 @@ - - */ - private array $locators = []; - - private bool $enableCache = false; - - private bool $enableContextualSourceLocation = false; - - private ?SourceCodeReflectorFactory $sourceReflectorFactory = null; - - /** - * @var Walker[] - */ - private array $framewalkers = []; - - /** - * @var ReflectionMemberProvider[] - */ - private array $memberProviders = []; - - private float $cacheLifetime = 5.0; - - private ?Cache $cache = null; - - /** - * @var DiagnosticProvider[] - */ - private array $diagnosticProviders = []; - - /** - * @var MemberContextResolver[] - */ - private array $memberContextResolvers = []; - - private CacheForDocument $cacheForDocument; - - /** - * Create a new instance of the builder - */ - public static function create(): ReflectorBuilder - { - return new self(); - } - - public function withSourceReflectorFactory(SourceCodeReflectorFactory $sourceReflectorFactory): ReflectorBuilder - { - $this->sourceReflectorFactory = $sourceReflectorFactory; - return $this; - } - - /** - * Replace the logger implementation. - */ - public function withLogger(LoggerInterface $logger): ReflectorBuilder - { - $this->logger = $logger; - - return $this; - } - - /** - * Add a source locator - */ - public function addLocator(SourceCodeLocator $locator, int $priority = 0): ReflectorBuilder - { - $this->locators[] = [$priority, $locator]; - - return $this; - } - - /** - * Add some source code - */ - public function addSource(TextDocument|string $code): ReflectorBuilder - { - $source = TextDocumentBuilder::fromUnknown($code); - - $this->addLocator(new StringSourceLocator($source)); - - return $this; - } - - public function addFrameWalker(Walker $frameWalker): ReflectorBuilder - { - $this->framewalkers[] = $frameWalker; - return $this; - } - - public function addMemberProvider(ReflectionMemberProvider $provider): ReflectorBuilder - { - $this->memberProviders[] = $provider; - return $this; - } - - public function addDiagnosticProvider(DiagnosticProvider $provider): self - { - $this->diagnosticProviders[] = $provider; - return $this; - } - - /** - * Build the reflector - */ - public function build(): Reflector - { - $this->addLocator(InternalLocator::forInternalStubs(), 255); - return (new ServiceLocator( - $this->buildLocator(), - $this->buildLogger(), - $this->buildReflectorFactory(), - $this->framewalkers, - $this->memberProviders, - $this->diagnosticProviders, - $this->memberContextResolvers, - $this->buildCache(), - $this->cacheForDocument ?? new CacheForDocument(fn () => new NullCache()), - $this->enableContextualSourceLocation - ))->reflector(); - } - - /** - * Enable contextual source location. - * - * Enable WR to locate classes from any source code passed - * to the SourceReflector (this is to enable property / class - * reflection on the current class. - * - * WARNING: This makes the reflector stateful - any source code - * passed to source reflector methods will be retained - * for the duration of the process. - */ - public function enableContextualSourceLocation(): ReflectorBuilder - { - $this->enableContextualSourceLocation = true; - - return $this; - } - - /** - * Enable class reflection cache. - * - * Wraps the ClassReflector in a memonizing cache. - */ - public function enableCache(): ReflectorBuilder - { - $this->enableCache = true; - - return $this; - } - - public function withCache(Cache $cache): ReflectorBuilder - { - $this->cache = $cache; - - return $this; - } - - public function withCacheForDocument(CacheForDocument $cacheForDocument): ReflectorBuilder - { - $this->cacheForDocument = $cacheForDocument; - - return $this; - } - - /** - * Set the cache lifetime in seconds (floats accepted) - */ - public function cacheLifetime(float $lifetime): ReflectorBuilder - { - $this->cacheLifetime = $lifetime; - - return $this; - } - - public function addMemberContextResolver(MemberContextResolver $memberContextResolver): self - { - $this->memberContextResolvers[] = $memberContextResolver; - return $this; - } - - private function buildLocator(): SourceCodeLocator - { - $locators = $this->locators; - usort($locators, function ($locator1, $locator2) { - [ $priority1 ] = $locator1; - [ $priority2 ] = $locator2; - return $priority2 <=> $priority1; - }); - - $locators = array_map(function (array $locator) { - return $locator[1]; - }, $locators); - - if ($locators === []) { - return new NullSourceLocator(); - } - - if (count($locators) > 1) { - $args = [$locators]; - if ($this->logger !== null) { - $args[] = $this->logger; - } - return new ChainSourceLocator(...$args); - } - - return reset($locators); - } - - private function buildLogger(): LoggerInterface - { - return $this->logger ?? new ArrayLogger(); - } - - private function buildReflectorFactory(): SourceCodeReflectorFactory - { - return $this->sourceReflectorFactory ?: new TolerantFactory(); - } - - private function buildCache(): Cache - { - if ($this->enableCache) { - return $this->cache ?: new TtlCache($this->cacheLifetime); - } - - return new NullCache(); - } -} diff --git a/lib/WorseReflection/Tests/Assert/TrinaryAssert.php b/lib/WorseReflection/Tests/Assert/TrinaryAssert.php deleted file mode 100644 index a2a3292a9c..0000000000 --- a/lib/WorseReflection/Tests/Assert/TrinaryAssert.php +++ /dev/null @@ -1,23 +0,0 @@ -getReflector()->reflectOffset(TextDocumentBuilder::fromUri(__DIR__ . '/../../../../vendor/phpactor/tolerant-php-parser/src/Parser.php')->build(), 183744); - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/BaseBenchCase.php b/lib/WorseReflection/Tests/Benchmarks/BaseBenchCase.php deleted file mode 100644 index 386040669e..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/BaseBenchCase.php +++ /dev/null @@ -1,68 +0,0 @@ -workspace(); - $workspace->reset(); - $stubLocator = new StubSourceLocator( - ReflectorBuilder::create()->build(), - __DIR__ . '/../../../../vendor/jetbrains/phpstorm-stubs', - __DIR__ . '/../Cache', - ); - - $builder = ReflectorBuilder::create(); - foreach ($this->diagnosticProviders as $provider) { - $builder->addDiagnosticProvider($provider); - } - $this->reflector = $builder - ->addLocator($composerLocator) - ->addLocator($stubLocator) - ->enableCache() - ->cacheLifetime(5) - ->enableContextualSourceLocation() - ->build(); - } - - public function loadFixture(string $name): void - { - foreach ((array)glob(sprintf('%s/%s/%s/*.php.test', __DIR__, 'fixtures', $name)) as $path) { - $this->workspace()->put( - substr(basename((string)$path), 0, -5), - (string)file_get_contents((string)$path) - ); - } - } - - public function getReflector(): Reflector - { - return $this->reflector; - } - - private function workspace(): Workspace - { - return new Workspace(__DIR__ . '/../Workspace'); - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/CarbonReflectBench.php b/lib/WorseReflection/Tests/Benchmarks/CarbonReflectBench.php deleted file mode 100644 index 496747cf9a..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/CarbonReflectBench.php +++ /dev/null @@ -1,20 +0,0 @@ -getReflector()->reflectClassesIn(TextDocumentBuilder::fromUri(__DIR__ . '/fixtures/reflection/carbon.test')->build()); - $carbon = $classes->get('Carbon\Carbon'); - foreach ($carbon->methods() as $method) { - } - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/DiagnosticsBench.php b/lib/WorseReflection/Tests/Benchmarks/DiagnosticsBench.php deleted file mode 100644 index eba5d2b576..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/DiagnosticsBench.php +++ /dev/null @@ -1,53 +0,0 @@ -reflector = ReflectorBuilder::create() - ->addDiagnosticProvider(new MissingMemberProvider()) - ->build(); - } - - /** - * @BeforeMethods({"init"}) - * @ParamProviders({"providePaths"}) - * @param array{path:string} $params - */ - public function benchDiagnostics(array $params): void - { - $diagnostics = wait($this->reflector->diagnostics( - TextDocumentBuilder::fromUri($params['path'])->build() - )); - } - - /** - * @return Generator - */ - public function providePaths(): Generator - { - foreach ((new GlobIterator(__DIR__ . '/fixtures/diagnostics/*.test')) as $info) { - assert($info instanceof SplFileInfo); - yield $info->getFilename() => [ - 'path' => $info->getRealPath() - ]; - } - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/Examples/MethodClass.php b/lib/WorseReflection/Tests/Benchmarks/Examples/MethodClass.php deleted file mode 100644 index 80a339c0ba..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/Examples/MethodClass.php +++ /dev/null @@ -1,21 +0,0 @@ -getReflector()->reflectClassLike(ClassName::fromString(TestCase::class)); - } - - /** - * @Subject() - * @OutputTimeUnit("milliseconds", precision=2) - * @Assert("mode(variant.time.avg) <= mode(baseline.time.avg) +/- 10%") - */ - public function test_case_methods_and_properties(): void - { - $class = $this->getReflector()->reflectClassLike(ClassName::fromString(TestCase::class)); - - foreach ($class->methods() as $method) { - foreach ($method->parameters() as $parameter) { - $method->type(); - } - } - } - - /** - * @Subject() - * @Revs(1) - * @OutputTimeUnit("milliseconds", precision=2) - * @Assert("mode(variant.time.avg) <= mode(baseline.time.avg) +/- 10%") - */ - public function test_case_method_frames(): void - { - $class = $this->getReflector()->reflectClassLike(ClassName::fromString(TestCase::class)); - - foreach ($class->methods() as $method) { - $method->frame(); - } - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/ReflectMethodBench.php b/lib/WorseReflection/Tests/Benchmarks/ReflectMethodBench.php deleted file mode 100644 index 92a589c71f..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/ReflectMethodBench.php +++ /dev/null @@ -1,47 +0,0 @@ -class = $this->getReflector()->reflectClassLike(ClassName::fromString(MethodClass::class)); - } - - /** - * @Subject() - */ - public function method(): void - { - $this->class->methods()->get('methodNoReturnType'); - } - - /** - * @Subject() - */ - public function method_return_type(): void - { - $this->class->methods()->get('methodWithReturnType')->returnType(); - } - - /** - * @Subject() - */ - public function method_inferred_return_type(): void - { - $this->class->methods()->get('methodWithDocblockReturnType')->type(); - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/ReflectPropertyBench.php b/lib/WorseReflection/Tests/Benchmarks/ReflectPropertyBench.php deleted file mode 100644 index 1ddb6a15e3..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/ReflectPropertyBench.php +++ /dev/null @@ -1,40 +0,0 @@ -class = $this->getReflector()->reflectClassLike(ClassName::fromString(PropertyClass::class)); - } - - /** - * @Subject() - */ - public function property(): void - { - $this->class->properties()->get('noType'); - } - - /** - * @Subject() - */ - public function property_return_type(): void - { - $this->class->properties()->get('withType')->inferredType(); - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/ReflectionStubsBench.php b/lib/WorseReflection/Tests/Benchmarks/ReflectionStubsBench.php deleted file mode 100644 index ff3476e828..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/ReflectionStubsBench.php +++ /dev/null @@ -1,35 +0,0 @@ -reflector = $this->getReflector(); - } - - /** - * @Subject() - */ - public function test_classes_and_methods(): void - { - $classes = $this->reflector->reflectClassesIn(TextDocumentBuilder::fromUri(__DIR__ . '/../../../../vendor/jetbrains/phpstorm-stubs/Reflection/Reflection.php')->build()); - - foreach ($classes as $class) { - foreach ($class->methods() as $method) { - } - } - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/SelfReflectClassBench.php b/lib/WorseReflection/Tests/Benchmarks/SelfReflectClassBench.php deleted file mode 100644 index 58ef74d3f4..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/SelfReflectClassBench.php +++ /dev/null @@ -1,35 +0,0 @@ -getReflector()->reflectClassLike(ClassName::fromString(self::class)); - - foreach ($class->methods() as $method) { - foreach ($method->parameters() as $parameter) { - $method->inferredType(); - } - } - } - - public function benchFrames(): void - { - $class = $this->getReflector()->reflectClassLike(ClassName::fromString(self::class)); - - foreach ($class->methods() as $method) { - $method->frame(); - } - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/YiiBench.php b/lib/WorseReflection/Tests/Benchmarks/YiiBench.php deleted file mode 100644 index 55fdd54603..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/YiiBench.php +++ /dev/null @@ -1,26 +0,0 @@ -loadFixture('yii'); - } - - /** - * @BeforeMethods({"setUp", "install"}) - */ - public function benchMembers(): void - { - $reflection = $this->getReflector()->reflectClass('Phpactor\WorseReflection\Tests\Workspace\Record'); - foreach ($reflection->members() as $method) { - $method->inferredType(); - } - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/lots_of_missing_methods.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/lots_of_missing_methods.test deleted file mode 100644 index 6cc8d3d0a0..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/lots_of_missing_methods.test +++ /dev/null @@ -1,1179 +0,0 @@ -expectException(\Phpactor\WorseReflection\Core\Exception\ClassNotFound::class); - $this->createReflector('')->reflectClassLike(ClassName::fromString('Foobar')); - } - - /** - * @dataProvider provideReflectionClass - */ - public function testReflectClass(string $source, string $class, Closure $assertion): void - { - $class = $this->createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion($class); - } - - public function provideReflectionClass(): Generator - { - yield 'It reflects an empty class' => [ - <<<'EOT' - assertEquals('Foobar', (string) $class->name()->short()); - $this->assertInstanceOf(ReflectionClass::class, $class); - $this->assertFalse($class->isInterface()); - }, - ]; - - yield 'It reflects a class which extends another' => [ - <<<'EOT' - assertEquals('Foobar', (string) $class->name()->short()); - $this->assertEquals('Barfoo', (string) $class->parent()->name()->short()); - }, - ]; - - yield 'It reflects class constants' => [ - <<<'EOT' - assertCount(3, $class->constants()); - $this->assertInstanceOf(ReflectionConstant::class, $class->constants()->get('FOOBAR')); - $this->assertInstanceOf(ReflectionConstant::class, $class->constants()->get('EEEBAR')); - }, - ]; - - yield 'It can provide the name of its last member' => [ - <<<'EOT' - assertEquals('bar', $class->properties()->last()->name()); - }, - ]; - - yield 'It can provide the name of its first member' => [ - <<<'EOT' - assertEquals('foo', $class->properties()->first()->name()); - }, - ]; - - yield 'It can provide its position' => [ - <<<'EOT' - assertEquals(7, $class->position()->start()); - }, - ]; - - yield 'It can provide the position of its member declarations' => [ - <<<'EOT' - assertEquals(20, $class->memberListPosition()->start()); - }, - ]; - - yield 'It provides list of its interfaces' => [ - <<<'EOT' - assertEquals(1, $class->interfaces()->count()); - $this->assertEquals('InterfaceOne', $class->interfaces()->first()->name()); - }, - ]; - - yield 'It list of interfaces includes interfaces from parent classes' => [ - <<<'EOT' - assertEquals(1, $class->interfaces()->count()); - $this->assertEquals('InterfaceOne', $class->interfaces()->first()->name()); - }, - ]; - - yield 'It provides list of its traits' => [ - <<<'EOT' - assertEquals(2, $class->traits()->count()); - $this->assertEquals('TraitNUMBERone', $class->traits()->get('TraitNUMBERone')->name()); - $this->assertEquals('TraitNUMBERtwo', $class->traits()->get('TraitNUMBERtwo')->name()); - }, - ]; - - yield 'Traits are inherited from parent classes (?)' => [ - <<<'EOT' - assertEquals(1, $class->traits()->count()); - $this->assertEquals('TraitNUMBERone', $class->traits()->first()->name()); - }, - ]; - - yield 'Get methods includes trait methods' => [ - <<<'EOT' - assertEquals(3, $class->methods()->count()); - $this->assertTrue($class->methods()->has('traitMethod1')); - $this->assertTrue($class->methods()->has('traitMethod2')); - }, - ]; - - yield 'Tolerates not found traits' => [ - <<<'EOT' - assertEquals(1, $class->methods()->count()); - }, - ]; - - yield 'Get methods includes aliased trait methods' => [ - <<<'EOT' - assertEquals(4, $class->methods()->count()); - $this->assertTrue($class->methods()->has('one')); - $this->assertTrue($class->methods()->has('two')); - $this->assertTrue($class->methods()->has('three')); - $this->assertTrue($class->methods()->has('four')); - $this->assertEquals(Visibility::private(), $class->methods()->get('two')->visibility()); - $this->assertEquals(Visibility::protected(), $class->methods()->get('three')->visibility()); - $this->assertFalse($class->methods()->belongingTo(ClassName::fromString(Class2::class))->has('two')); - $this->assertEquals('TraitOne', $class->methods()->get('two')->declaringClass()->name()->short()); - }, - ]; - - yield 'Get methods includes namespaced aliased trait methods' => [ - <<<'EOT' - assertEquals(3, $class->methods()->count()); - $this->assertTrue($class->methods()->has('one')); - $this->assertTrue($class->methods()->has('three')); - }, - ]; - - yield 'Get properties includes trait properties' => [ - <<<'EOT' - assertEquals(1, $class->properties()->count()); - $this->assertEquals('prop1', $class->properties()->first()->name()); - }, - ]; - - yield 'Get methods at offset' => [ - <<<'EOT' - assertEquals(1, $class->methods()->atOffset(27)->count()); - }, - ]; - - yield 'Get properties includes trait methods' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - }, - ]; - - yield 'Get properties for belonging to' => [ - <<<'EOT' - assertCount(1, $class->properties()->belongingTo(ClassName::fromString('Class1'))); - $this->assertCount(0, $class->properties()->belongingTo(ClassName::fromString('Class2'))); - }, - ]; - - - yield 'If it extends an interface, then ignore' => [ - <<<'EOT' - assertEquals(0, $class->methods()->count()); - }, - ]; - - - yield 'isInstanceOf returns false when it is not an instance of' => [ - <<<'EOT' - assertFalse($class->isInstanceOf(ClassName::fromString('Foobar'))); - }, - ]; - - yield 'isInstanceOf returns true for itself' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('Class2'))); - }, - ]; - - yield 'isInstanceOf returns true when it is not an instance of an interface' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('SomeInterface'))); - }, - ]; - - yield 'isInstanceOf returns true when a class implements the interface and has a parent' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('SomeInterface'))); - }, - ]; - - yield 'isInstanceOf returns true for a parent class' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('SomeParent'))); - }, - ]; - - yield 'Returns source code' => [ - <<<'EOT' - assertStringContainsString('class Class2', (string) $class->sourceCode()); - }, - ]; - - yield 'Returns imported classes' => [ - <<<'EOT' - assertEquals(NameImports::fromNames([ - 'Barfoo' => Name::fromString('Foobar\\Barfoo'), - 'Carzatz' => Name::fromString('Barfoo\\Foobaz'), - ]), $class->scope()->nameImports()); - }, - ]; - - yield 'Inherits constants from interface' => [ - <<<'EOT' - assertCount(1, $class->constants()); - $this->assertEquals('SOME_CONSTANT', $class->constants()->get('SOME_CONSTANT')->name()); - }, - ]; - - yield 'Returns all members' => [ - <<<'EOT' - assertCount(3, $class->members()); - $this->assertTrue($class->members()->has('FOOBAR')); - $this->assertTrue($class->members()->has('foobar')); - $this->assertTrue($class->members()->has('foo')); - }, - ]; - - yield 'Incomplete extends' => [ - <<<'EOT' - assertNull($class->parent()); - $this->assertEquals('Class1', $class->name()->short()); - }, - ]; - - yield 'Does not infinite loop with self-referencing class on get interfaces' => [ - <<<'EOT' - assertCount(0, $class->interfaces()); - }, - ]; - - yield 'Says if class is abstract' => [ - <<<'EOT' - assertTrue($class->isAbstract()); - }, - ]; - - yield 'Says if class is not abstract' => [ - <<<'EOT' - assertFalse($class->isAbstract()); - }, - ]; - - yield 'Says if class is final' => [ - <<<'EOT' - assertTrue($class->isFinal()); - }, - ]; - - yield 'Says if class is deprecated' => [ - <<<'EOT' - assertTrue($class->deprecation()->isDefined()); - }, - ]; - } - - /** - * @dataProvider provideVirtualMethods - */ - public function testVirtualMethods(string $source, string $class, Closure $assertion): void - { - $class = $this->createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion($class); - } - - public function provideVirtualMethods() - { - yield 'virtual methods' => [ - <<<'EOT' - assertEquals(2, $class->methods()->count()); - $this->assertEquals('foobar', $class->methods()->first()->name()); - } - ]; - - yield 'virtual methods merge onto existing ones' => [ - <<<'EOT' - assertCount(1, $class->methods()); - - // originally this returned the declared type - $this->assertEquals( - 'Foobar', - $class->methods()->first()->type()->__toString(), - ); - $this->assertEquals( - 'Foobar', - $class->methods()->first()->inferredType()->__toString(), - ); - }, - ]; - - yield 'virtual methods are inherited' => [ - <<<'EOT' - assertCount(2, $class->methods()); - $this->assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods are inherited from interface' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods are inherited from multiple layers of interfaces' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods are inherited from parent class which implements interface' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - $this->assertEquals( - 'ParentInterface', - $class->methods()->get('foobar')->declaringClass()->name()->__toString() - ); - }, - ]; - - yield 'virtual method types can be relative' => [ - 'assertEquals( - 'Bosh\Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual method types can be absolute' => [ - 'assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods of child classes override those of parents' => [ - <<<'EOT' - assertCount(2, $class->methods()); - $this->assertEquals( - 'Barfoo', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods are extracted from traits' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals('Foobar', $class->methods()->first()->inferredType()->__toString()); - }, - ]; - - yield 'virtual methods are extracted from traits of a parent class' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals('Foobar', $class->methods()->first()->inferredType()->__toString()); - }, - ]; - } - - /** - * @dataProvider provideVirtualProperties - */ - public function testVirtualProperties(string $source, string $class, Closure $assertion): void - { - $class = $this->createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion($class); - } - - public function provideVirtualProperties() - { - yield 'virtual properties' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - } - ]; - - yield 'invalid properties' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - } - ]; - - yield 'multiple types' => [ - <<<'EOT' - assertEquals(1, $class->properties()->count()); - self::assertInstanceOf(UnionType::class, $class->properties()->first()->type()); - self::assertEquals('string|int', $class->properties()->first()->type()); - } - ]; - - yield 'virtual properties are extracted from traits' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - $this->assertEquals('Foobar', $class->properties()->first()->inferredType()->__toString()); - $this->assertEquals('barfoo', $class->properties()->last()->name()); - $this->assertEquals('Barfoo', $class->properties()->last()->inferredType()->__toString()); - } - ]; - - yield 'virtual properties are extracted from traits of a parent class' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - $this->assertEquals('Foobar', $class->properties()->first()->inferredType()->__toString()); - $this->assertEquals('barfoo', $class->properties()->last()->name()); - $this->assertEquals('Barfoo', $class->properties()->last()->inferredType()->__toString()); - } - ]; - } -} - - -wrAssertDiagnostics(108, ['MissingMethod']); - diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/lots_of_new_generic_objects.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/lots_of_new_generic_objects.test deleted file mode 100644 index 829464a40f..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/lots_of_new_generic_objects.test +++ /dev/null @@ -1,66 +0,0 @@ -bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang(->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang(->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang(->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang(->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang(->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang(->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang(->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang()->bang(->bang()->bang()->bang(->bang()->bang(); diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/phpstan.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/phpstan.test deleted file mode 100644 index 5a2deb4239..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/diagnostics/phpstan.test +++ /dev/null @@ -1,4977 +0,0 @@ - */ - private array $truthyScopes = []; - - /** @var array */ - private array $falseyScopes = []; - - private ?string $namespace; - - private ?self $scopeOutOfFirstLevelStatement = null; - - private ?self $scopeWithPromotedNativeTypes = null; - - /** - * @param array $expressionTypes - * @param array $conditionalExpressions - * @param list $inClosureBindScopeClasses - * @param array $currentlyAssignedExpressions - * @param array $currentlyAllowedUndefinedExpressions - * @param array $nativeExpressionTypes - * @param array $inFunctionCallsStack - */ - public function __construct( - private InternalScopeFactory $scopeFactory, - private ReflectionProvider $reflectionProvider, - private InitializerExprTypeResolver $initializerExprTypeResolver, - private DynamicReturnTypeExtensionRegistry $dynamicReturnTypeExtensionRegistry, - private ExprPrinter $exprPrinter, - private TypeSpecifier $typeSpecifier, - private PropertyReflectionFinder $propertyReflectionFinder, - private Parser $parser, - private NodeScopeResolver $nodeScopeResolver, - private ConstantResolver $constantResolver, - private ScopeContext $context, - private PhpVersion $phpVersion, - private bool $declareStrictTypes = false, - private FunctionReflection|ExtendedMethodReflection|null $function = null, - ?string $namespace = null, - private array $expressionTypes = [], - private array $nativeExpressionTypes = [], - private array $conditionalExpressions = [], - private array $inClosureBindScopeClasses = [], - private ?ParametersAcceptor $anonymousFunctionReflection = null, - private bool $inFirstLevelStatement = true, - private array $currentlyAssignedExpressions = [], - private array $currentlyAllowedUndefinedExpressions = [], - private array $inFunctionCallsStack = [], - private bool $afterExtractCall = false, - private ?Scope $parentScope = null, - private bool $nativeTypesPromoted = false, - private bool $explicitMixedInUnknownGenericNew = false, - private bool $explicitMixedForGlobalVariables = false, - ) - { - if ($namespace === '') { - $namespace = null; - } - - $this->namespace = $namespace; - } - - /** @api */ - public function getFile(): string - { - return $this->context->getFile(); - } - - /** @api */ - public function getFileDescription(): string - { - if ($this->context->getTraitReflection() === null) { - return $this->getFile(); - } - - /** @var ClassReflection $classReflection */ - $classReflection = $this->context->getClassReflection(); - - $className = $classReflection->getDisplayName(); - if (!$classReflection->isAnonymous()) { - $className = sprintf('class %s', $className); - } - - $traitReflection = $this->context->getTraitReflection(); - if ($traitReflection->getFileName() === null) { - throw new ShouldNotHappenException(); - } - - return sprintf( - '%s (in context of %s)', - $traitReflection->getFileName(), - $className, - ); - } - - /** @api */ - public function isDeclareStrictTypes(): bool - { - return $this->declareStrictTypes; - } - - public function enterDeclareStrictTypes(): self - { - return $this->scopeFactory->create( - $this->context, - true, - null, - null, - $this->expressionTypes, - $this->nativeExpressionTypes, - ); - - } - - /** @api */ - public function isInClass(): bool - { - return $this->context->getClassReflection() !== null; - } - - /** @api */ - public function isInTrait(): bool - { - return $this->context->getTraitReflection() !== null; - } - - /** @api */ - public function getClassReflection(): ?ClassReflection - { - return $this->context->getClassReflection(); - } - - /** @api */ - public function getTraitReflection(): ?ClassReflection - { - return $this->context->getTraitReflection(); - } - - /** - * @api - * @return FunctionReflection|ExtendedMethodReflection|null - */ - public function getFunction() - { - return $this->function; - } - - /** @api */ - public function getFunctionName(): ?string - { - return $this->function !== null ? $this->function->getName() : null; - } - - /** @api */ - public function getNamespace(): ?string - { - return $this->namespace; - } - - /** @api */ - public function getParentScope(): ?Scope - { - return $this->parentScope; - } - - /** @api */ - public function canAnyVariableExist(): bool - { - return ($this->function === null && !$this->isInAnonymousFunction()) || $this->afterExtractCall; - } - - public function afterExtractCall(): self - { - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - [], - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $this->inFunctionCallsStack, - true, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - public function afterClearstatcacheCall(): self - { - $expressionTypes = $this->expressionTypes; - foreach (array_keys($expressionTypes) as $exprString) { - // list from https://www.php.net/manual/en/function.clearstatcache.php - - // stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), filetype(), and fileperms(). - foreach ([ - 'stat', - 'lstat', - 'file_exists', - 'is_writable', - 'is_writeable', - 'is_readable', - 'is_executable', - 'is_file', - 'is_dir', - 'is_link', - 'filectime', - 'fileatime', - 'filemtime', - 'fileinode', - 'filegroup', - 'fileowner', - 'filesize', - 'filetype', - 'fileperms', - ] as $functionName) { - if (!str_starts_with($exprString, $functionName . '(') && !str_starts_with($exprString, '\\' . $functionName . '(')) { - continue; - } - - unset($expressionTypes[$exprString]); - continue 2; - } - } - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $this->inFunctionCallsStack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - public function afterOpenSslCall(string $openSslFunctionName): self - { - $expressionTypes = $this->expressionTypes; - - if (in_array($openSslFunctionName, [ - 'openssl_cipher_iv_length', - 'openssl_cms_decrypt', - 'openssl_cms_encrypt', - 'openssl_cms_read', - 'openssl_cms_sign', - 'openssl_cms_verify', - 'openssl_csr_export_to_file', - 'openssl_csr_export', - 'openssl_csr_get_public_key', - 'openssl_csr_get_subject', - 'openssl_csr_new', - 'openssl_csr_sign', - 'openssl_decrypt', - 'openssl_dh_compute_key', - 'openssl_digest', - 'openssl_encrypt', - 'openssl_get_curve_names', - 'openssl_get_privatekey', - 'openssl_get_publickey', - 'openssl_open', - 'openssl_pbkdf2', - 'openssl_pkcs12_export_to_file', - 'openssl_pkcs12_export', - 'openssl_pkcs12_read', - 'openssl_pkcs7_decrypt', - 'openssl_pkcs7_encrypt', - 'openssl_pkcs7_read', - 'openssl_pkcs7_sign', - 'openssl_pkcs7_verify', - 'openssl_pkey_derive', - 'openssl_pkey_export_to_file', - 'openssl_pkey_export', - 'openssl_pkey_get_private', - 'openssl_pkey_get_public', - 'openssl_pkey_new', - 'openssl_private_decrypt', - 'openssl_private_encrypt', - 'openssl_public_decrypt', - 'openssl_public_encrypt', - 'openssl_random_pseudo_bytes', - 'openssl_seal', - 'openssl_sign', - 'openssl_spki_export_challenge', - 'openssl_spki_export', - 'openssl_spki_new', - 'openssl_spki_verify', - 'openssl_verify', - 'openssl_x509_checkpurpose', - 'openssl_x509_export_to_file', - 'openssl_x509_export', - 'openssl_x509_fingerprint', - 'openssl_x509_read', - 'openssl_x509_verify', - ], true)) { - unset($expressionTypes['\openssl_error_string()']); - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $this->inFunctionCallsStack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - /** @api */ - public function hasVariableType(string $variableName): TrinaryLogic - { - if ($this->isGlobalVariable($variableName)) { - return TrinaryLogic::createYes(); - } - - $varExprString = '$' . $variableName; - if (!isset($this->expressionTypes[$varExprString])) { - if ($this->canAnyVariableExist()) { - return TrinaryLogic::createMaybe(); - } - - return TrinaryLogic::createNo(); - } - - return $this->expressionTypes[$varExprString]->getCertainty(); - } - - /** @api */ - public function getVariableType(string $variableName): Type - { - if ($this->hasVariableType($variableName)->maybe()) { - if ($variableName === 'argc') { - return IntegerRangeType::fromInterval(1, null); - } - if ($variableName === 'argv') { - return AccessoryArrayListType::intersectWith(TypeCombinator::intersect( - new ArrayType(new IntegerType(), new StringType()), - new NonEmptyArrayType(), - )); - } - if ($this->canAnyVariableExist()) { - return new MixedType(); - } - } - - if ($this->isGlobalVariable($variableName)) { - return new ArrayType(new StringType(), new MixedType($this->explicitMixedForGlobalVariables)); - } - - if ($this->hasVariableType($variableName)->no()) { - throw new UndefinedVariableException($this, $variableName); - } - - $varExprString = '$' . $variableName; - if (!array_key_exists($varExprString, $this->expressionTypes)) { - return new MixedType(); - } - - return TypeUtils::resolveLateResolvableTypes($this->expressionTypes[$varExprString]->getType()); - } - - /** - * @api - * @return array - */ - public function getDefinedVariables(): array - { - $variables = []; - foreach ($this->expressionTypes as $exprString => $holder) { - if (!$holder->getExpr() instanceof Variable) { - continue; - } - if (!$holder->getCertainty()->yes()) { - continue; - } - - $variables[] = substr($exprString, 1); - } - - return $variables; - } - - private function isGlobalVariable(string $variableName): bool - { - return in_array($variableName, [ - 'GLOBALS', - '_SERVER', - '_GET', - '_POST', - '_FILES', - '_COOKIE', - '_SESSION', - '_REQUEST', - '_ENV', - ], true); - } - - /** @api */ - public function hasConstant(Name $name): bool - { - $isCompilerHaltOffset = $name->toString() === '__COMPILER_HALT_OFFSET__'; - if ($isCompilerHaltOffset) { - return $this->fileHasCompilerHaltStatementCalls(); - } - - if (!$name->isFullyQualified() && $this->getNamespace() !== null) { - if ($this->hasExpressionType(new ConstFetch(new FullyQualified([$this->getNamespace(), $name->toString()])))->yes()) { - return true; - } - } - if ($this->hasExpressionType(new ConstFetch(new FullyQualified($name->toString())))->yes()) { - return true; - } - - return $this->reflectionProvider->hasConstant($name, $this); - } - - private function fileHasCompilerHaltStatementCalls(): bool - { - $nodes = $this->parser->parseFile($this->getFile()); - foreach ($nodes as $node) { - if ($node instanceof Node\Stmt\HaltCompiler) { - return true; - } - } - - return false; - } - - /** @api */ - public function isInAnonymousFunction(): bool - { - return $this->anonymousFunctionReflection !== null; - } - - /** @api */ - public function getAnonymousFunctionReflection(): ?ParametersAcceptor - { - return $this->anonymousFunctionReflection; - } - - /** @api */ - public function getAnonymousFunctionReturnType(): ?Type - { - if ($this->anonymousFunctionReflection === null) { - return null; - } - - return $this->anonymousFunctionReflection->getReturnType(); - } - - /** @api */ - public function getType(Expr $node): Type - { - if ($node instanceof GetIterableKeyTypeExpr) { - return $this->getType($node->getExpr())->getIterableKeyType(); - } - if ($node instanceof GetIterableValueTypeExpr) { - return $this->getType($node->getExpr())->getIterableValueType(); - } - if ($node instanceof GetOffsetValueTypeExpr) { - return $this->getType($node->getVar())->getOffsetValueType($this->getType($node->getDim())); - } - if ($node instanceof SetOffsetValueTypeExpr) { - return $this->getType($node->getVar())->setOffsetValueType( - $node->getDim() !== null ? $this->getType($node->getDim()) : null, - $this->getType($node->getValue()), - ); - } - if ($node instanceof TypeExpr) { - return $node->getExprType(); - } - - if ($node instanceof OriginalPropertyTypeExpr) { - $propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($node->getPropertyFetch(), $this); - if ($propertyReflection === null) { - return new ErrorType(); - } - - return $propertyReflection->getReadableType(); - } - - $key = $this->getNodeKey($node); - - if (!array_key_exists($key, $this->resolvedTypes)) { - $this->resolvedTypes[$key] = TypeUtils::resolveLateResolvableTypes($this->resolveType($key, $node)); - } - return $this->resolvedTypes[$key]; - } - - private function getNodeKey(Expr $node): string - { - $key = $this->exprPrinter->printExpr($node); - - if ( - $node instanceof Node\FunctionLike - && $node->hasAttribute(ArrayMapArgVisitor::ATTRIBUTE_NAME) - && $node->hasAttribute('startFilePos') - ) { - $key .= '/*' . $node->getAttribute('startFilePos') . '*/'; - } - - return $key; - } - - private function resolveType(string $exprString, Expr $node): Type - { - if ($node instanceof Expr\Exit_ || $node instanceof Expr\Throw_) { - return new NeverType(true); - } - - if (!$node instanceof Variable && $this->hasExpressionType($node)->yes()) { - return $this->expressionTypes[$exprString]->getType(); - } - - if ($node instanceof Expr\BinaryOp\Smaller) { - return $this->getType($node->left)->isSmallerThan($this->getType($node->right))->toBooleanType(); - } - - if ($node instanceof Expr\BinaryOp\SmallerOrEqual) { - return $this->getType($node->left)->isSmallerThanOrEqual($this->getType($node->right))->toBooleanType(); - } - - if ($node instanceof Expr\BinaryOp\Greater) { - return $this->getType($node->right)->isSmallerThan($this->getType($node->left))->toBooleanType(); - } - - if ($node instanceof Expr\BinaryOp\GreaterOrEqual) { - return $this->getType($node->right)->isSmallerThanOrEqual($this->getType($node->left))->toBooleanType(); - } - - if ($node instanceof Expr\BinaryOp\Equal) { - if ( - $node->left instanceof Variable - && is_string($node->left->name) - && $node->right instanceof Variable - && is_string($node->right->name) - && $node->left->name === $node->right->name - ) { - return new ConstantBooleanType(true); - } - - $leftType = $this->getType($node->left); - $rightType = $this->getType($node->right); - - return $this->initializerExprTypeResolver->resolveEqualType($leftType, $rightType); - } - - if ($node instanceof Expr\BinaryOp\NotEqual) { - return $this->getType(new Expr\BooleanNot(new BinaryOp\Equal($node->left, $node->right))); - } - - if ($node instanceof Expr\Empty_) { - $result = $this->issetCheck($node->expr, static function (Type $type): ?bool { - $isNull = (new NullType())->isSuperTypeOf($type); - $isFalsey = (new ConstantBooleanType(false))->isSuperTypeOf($type->toBoolean()); - if ($isNull->maybe()) { - return null; - } - if ($isFalsey->maybe()) { - return null; - } - - if ($isNull->yes()) { - if ($isFalsey->yes()) { - return false; - } - if ($isFalsey->no()) { - return true; - } - - return false; - } - - return !$isFalsey->yes(); - }); - if ($result === null) { - return new BooleanType(); - } - - return new ConstantBooleanType(!$result); - } - - if ($node instanceof Node\Expr\BooleanNot) { - $exprBooleanType = $this->getType($node->expr)->toBoolean(); - if ($exprBooleanType instanceof ConstantBooleanType) { - return new ConstantBooleanType(!$exprBooleanType->getValue()); - } - - return new BooleanType(); - } - - if ($node instanceof Node\Expr\BitwiseNot) { - return $this->initializerExprTypeResolver->getBitwiseNotType($node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ( - $node instanceof Node\Expr\BinaryOp\BooleanAnd - || $node instanceof Node\Expr\BinaryOp\LogicalAnd - ) { - $leftBooleanType = $this->getType($node->left)->toBoolean(); - if ( - $leftBooleanType->isFalse()->yes() - ) { - return new ConstantBooleanType(false); - } - - $rightBooleanType = $this->filterByTruthyValue($node->left)->getType($node->right)->toBoolean(); - - if ( - $rightBooleanType->isFalse()->yes() - ) { - return new ConstantBooleanType(false); - } - - if ( - $leftBooleanType->isTrue()->yes() - && $rightBooleanType->isTrue()->yes() - ) { - return new ConstantBooleanType(true); - } - - return new BooleanType(); - } - - if ( - $node instanceof Node\Expr\BinaryOp\BooleanOr - || $node instanceof Node\Expr\BinaryOp\LogicalOr - ) { - $leftBooleanType = $this->getType($node->left)->toBoolean(); - if ( - $leftBooleanType->isTrue()->yes() - ) { - return new ConstantBooleanType(true); - } - - $rightBooleanType = $this->filterByFalseyValue($node->left)->getType($node->right)->toBoolean(); - - if ( - $rightBooleanType->isTrue()->yes() - ) { - return new ConstantBooleanType(true); - } - - if ( - $leftBooleanType->isFalse()->yes() - && $rightBooleanType->isFalse()->yes() - ) { - return new ConstantBooleanType(false); - } - - return new BooleanType(); - } - - if ($node instanceof Node\Expr\BinaryOp\LogicalXor) { - $leftBooleanType = $this->getType($node->left)->toBoolean(); - $rightBooleanType = $this->getType($node->right)->toBoolean(); - - if ( - $leftBooleanType instanceof ConstantBooleanType - && $rightBooleanType instanceof ConstantBooleanType - ) { - return new ConstantBooleanType( - $leftBooleanType->getValue() xor $rightBooleanType->getValue(), - ); - } - - return new BooleanType(); - } - - if ($node instanceof Expr\BinaryOp\Identical) { - if ( - $node->left instanceof Variable - && is_string($node->left->name) - && $node->right instanceof Variable - && is_string($node->right->name) - && $node->left->name === $node->right->name - ) { - return new ConstantBooleanType(true); - } - - $leftType = $this->getType($node->left); - $rightType = $this->getType($node->right); - - if ( - ( - $node->left instanceof Node\Expr\PropertyFetch - || $node->left instanceof Node\Expr\StaticPropertyFetch - ) - && $rightType->isNull()->yes() - && !$this->hasPropertyNativeType($node->left) - ) { - return new BooleanType(); - } - - if ( - ( - $node->right instanceof Node\Expr\PropertyFetch - || $node->right instanceof Node\Expr\StaticPropertyFetch - ) - && $leftType->isNull()->yes() - && !$this->hasPropertyNativeType($node->right) - ) { - return new BooleanType(); - } - - return $this->initializerExprTypeResolver->resolveIdenticalType($leftType, $rightType); - } - - if ($node instanceof Expr\BinaryOp\NotIdentical) { - return $this->getType(new Expr\BooleanNot(new BinaryOp\Identical($node->left, $node->right))); - } - - if ($node instanceof Expr\Instanceof_) { - $expressionType = $this->getType($node->expr); - if ( - $this->isInTrait() - && TypeUtils::findThisType($expressionType) !== null - ) { - return new BooleanType(); - } - if ($expressionType instanceof NeverType) { - return new ConstantBooleanType(false); - } - - $uncertainty = false; - - if ($node->class instanceof Node\Name) { - $unresolvedClassName = $node->class->toString(); - if ( - strtolower($unresolvedClassName) === 'static' - && $this->isInClass() - ) { - $classType = new StaticType($this->getClassReflection()); - } else { - $className = $this->resolveName($node->class); - $classType = new ObjectType($className); - } - } else { - $classType = $this->getType($node->class); - $classType = TypeTraverser::map($classType, static function (Type $type, callable $traverse) use (&$uncertainty): Type { - if ($type instanceof UnionType || $type instanceof IntersectionType) { - return $traverse($type); - } - if ($type->getObjectClassNames() !== []) { - $uncertainty = true; - return $type; - } - if ($type instanceof GenericClassStringType) { - $uncertainty = true; - return $type->getGenericType(); - } - if ($type instanceof ConstantStringType) { - return new ObjectType($type->getValue()); - } - return new MixedType(); - }); - } - - if ($classType->isSuperTypeOf(new MixedType())->yes()) { - return new BooleanType(); - } - - $isSuperType = $classType->isSuperTypeOf($expressionType); - - if ($isSuperType->no()) { - return new ConstantBooleanType(false); - } elseif ($isSuperType->yes() && !$uncertainty) { - return new ConstantBooleanType(true); - } - - return new BooleanType(); - } - - if ($node instanceof Node\Expr\UnaryPlus) { - return $this->getType($node->expr)->toNumber(); - } - - if ($node instanceof Expr\ErrorSuppress - || $node instanceof Expr\Assign - ) { - return $this->getType($node->expr); - } - - if ($node instanceof Node\Expr\UnaryMinus) { - return $this->initializerExprTypeResolver->getUnaryMinusType($node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\BinaryOp\Concat) { - return $this->initializerExprTypeResolver->getConcatType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\Concat) { - return $this->initializerExprTypeResolver->getConcatType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\BitwiseAnd) { - return $this->initializerExprTypeResolver->getBitwiseAndType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\BitwiseAnd) { - return $this->initializerExprTypeResolver->getBitwiseAndType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\BitwiseOr) { - return $this->initializerExprTypeResolver->getBitwiseOrType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\BitwiseOr) { - return $this->initializerExprTypeResolver->getBitwiseOrType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\BitwiseXor) { - return $this->initializerExprTypeResolver->getBitwiseXorType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\BitwiseXor) { - return $this->initializerExprTypeResolver->getBitwiseXorType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\BinaryOp\Spaceship) { - return $this->initializerExprTypeResolver->getSpaceshipType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\Div) { - return $this->initializerExprTypeResolver->getDivType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\Div) { - return $this->initializerExprTypeResolver->getDivType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\Mod) { - return $this->initializerExprTypeResolver->getModType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\Mod) { - return $this->initializerExprTypeResolver->getModType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\Plus) { - return $this->initializerExprTypeResolver->getPlusType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\Plus) { - return $this->initializerExprTypeResolver->getPlusType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\Minus) { - return $this->initializerExprTypeResolver->getMinusType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\Minus) { - return $this->initializerExprTypeResolver->getMinusType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\Mul) { - return $this->initializerExprTypeResolver->getMulType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\Mul) { - return $this->initializerExprTypeResolver->getMulType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\Pow) { - return $this->initializerExprTypeResolver->getPowType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\Pow) { - return $this->initializerExprTypeResolver->getPowType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\ShiftLeft) { - return $this->initializerExprTypeResolver->getShiftLeftType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\ShiftLeft) { - return $this->initializerExprTypeResolver->getShiftLeftType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof BinaryOp\ShiftRight) { - return $this->initializerExprTypeResolver->getShiftRightType($node->left, $node->right, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\AssignOp\ShiftRight) { - return $this->initializerExprTypeResolver->getShiftRightType($node->var, $node->expr, fn (Expr $expr): Type => $this->getType($expr)); - } - - if ($node instanceof Expr\Clone_) { - return $this->getType($node->expr); - } - - if ($node instanceof LNumber) { - return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this)); - } elseif ($node instanceof String_) { - return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this)); - } elseif ($node instanceof Node\Scalar\Encapsed) { - $resultType = null; - - foreach ($node->parts as $part) { - $partType = $part instanceof EncapsedStringPart - ? new ConstantStringType($part->value) - : $this->getType($part)->toString(); - if ($resultType === null) { - $resultType = $partType; - - continue; - } - - $resultType = $this->initializerExprTypeResolver->resolveConcatType($resultType, $partType); - if (count($resultType->getConstantStrings()) === 0) { - return $resultType; - } - } - - return $resultType ?? new ConstantStringType(''); - } elseif ($node instanceof DNumber) { - return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this)); - } elseif ($node instanceof Expr\CallLike && $node->isFirstClassCallable()) { - if ($node instanceof FuncCall) { - if ($node->name instanceof Name) { - if ($this->reflectionProvider->hasFunction($node->name, $this)) { - return $this->createFirstClassCallable( - $this->reflectionProvider->getFunction($node->name, $this)->getVariants(), - ); - } - - return new ObjectType(Closure::class); - } - - $callableType = $this->getType($node->name); - if (!$callableType->isCallable()->yes()) { - return new ObjectType(Closure::class); - } - - return $this->createFirstClassCallable( - $callableType->getCallableParametersAcceptors($this), - ); - } - - if ($node instanceof MethodCall) { - if (!$node->name instanceof Node\Identifier) { - return new ObjectType(Closure::class); - } - - $varType = $this->getType($node->var); - $method = $this->getMethodReflection($varType, $node->name->toString()); - if ($method === null) { - return new ObjectType(Closure::class); - } - - return $this->createFirstClassCallable($method->getVariants()); - } - - if ($node instanceof Expr\StaticCall) { - if (!$node->class instanceof Name) { - return new ObjectType(Closure::class); - } - - $classType = $this->resolveTypeByName($node->class); - if (!$node->name instanceof Node\Identifier) { - return new ObjectType(Closure::class); - } - - $methodName = $node->name->toString(); - if (!$classType->hasMethod($methodName)->yes()) { - return new ObjectType(Closure::class); - } - - return $this->createFirstClassCallable($classType->getMethod($methodName, $this)->getVariants()); - } - - if ($node instanceof New_) { - return new ErrorType(); - } - - throw new ShouldNotHappenException(); - } elseif ($node instanceof Expr\Closure || $node instanceof Expr\ArrowFunction) { - $parameters = []; - $isVariadic = false; - - $firstOptionalParameterIndex = null; - foreach ($node->params as $i => $param) { - $isOptionalCandidate = $param->default !== null || $param->variadic; - - if ($isOptionalCandidate) { - if ($firstOptionalParameterIndex === null) { - $firstOptionalParameterIndex = $i; - } - } else { - $firstOptionalParameterIndex = null; - } - } - - foreach ($node->params as $i => $param) { - if ($param->variadic) { - $isVariadic = true; - } - if (!$param->var instanceof Variable || !is_string($param->var->name)) { - throw new ShouldNotHappenException(); - } - $parameters[] = new NativeParameterReflection( - $param->var->name, - $firstOptionalParameterIndex !== null && $i >= $firstOptionalParameterIndex, - $this->getFunctionType($param->type, $this->isParameterValueNullable($param), false), - $param->byRef - ? PassedByReference::createCreatesNewVariable() - : PassedByReference::createNo(), - $param->variadic, - $param->default !== null ? $this->getType($param->default) : null, - ); - } - - $callableParameters = null; - $arrayMapArgs = $node->getAttribute(ArrayMapArgVisitor::ATTRIBUTE_NAME); - if ($arrayMapArgs !== null) { - $callableParameters = []; - foreach ($arrayMapArgs as $funcCallArg) { - $callableParameters[] = new DummyParameter('item', $this->getType($funcCallArg->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null); - } - } - - if ($node instanceof Expr\ArrowFunction) { - $arrowScope = $this->enterArrowFunctionWithoutReflection($node, $callableParameters); - - if ($node->expr instanceof Expr\Yield_ || $node->expr instanceof Expr\YieldFrom) { - $yieldNode = $node->expr; - - if ($yieldNode instanceof Expr\Yield_) { - if ($yieldNode->key === null) { - $keyType = new IntegerType(); - } else { - $keyType = $arrowScope->getType($yieldNode->key); - } - - if ($yieldNode->value === null) { - $valueType = new NullType(); - } else { - $valueType = $arrowScope->getType($yieldNode->value); - } - } else { - $yieldFromType = $arrowScope->getType($yieldNode->expr); - $keyType = $yieldFromType->getIterableKeyType(); - $valueType = $yieldFromType->getIterableValueType(); - } - - $returnType = new GenericObjectType(Generator::class, [ - $keyType, - $valueType, - new MixedType(), - new VoidType(), - ]); - } else { - $returnType = $arrowScope->getType($node->expr); - if ($node->returnType !== null) { - $returnType = TypehintHelper::decideType($this->getFunctionType($node->returnType, false, false), $returnType); - } - } - } else { - $closureScope = $this->enterAnonymousFunctionWithoutReflection($node, $callableParameters); - $closureReturnStatements = []; - $closureYieldStatements = []; - $closureExecutionEnds = []; - $this->nodeScopeResolver->processStmtNodes($node, $node->stmts, $closureScope, static function (Node $node, Scope $scope) use ($closureScope, &$closureReturnStatements, &$closureYieldStatements, &$closureExecutionEnds): void { - if ($scope->getAnonymousFunctionReflection() !== $closureScope->getAnonymousFunctionReflection()) { - return; - } - - if ($node instanceof ExecutionEndNode) { - if ($node->getStatementResult()->isAlwaysTerminating()) { - foreach ($node->getStatementResult()->getExitPoints() as $exitPoint) { - if ($exitPoint->getStatement() instanceof Node\Stmt\Return_) { - continue; - } - - $closureExecutionEnds[] = $node; - break; - } - - if (count($node->getStatementResult()->getExitPoints()) === 0) { - $closureExecutionEnds[] = $node; - } - } - - return; - } - - if ($node instanceof Node\Stmt\Return_) { - $closureReturnStatements[] = [$node, $scope]; - } - - if (!$node instanceof Expr\Yield_ && !$node instanceof Expr\YieldFrom) { - return; - } - - $closureYieldStatements[] = [$node, $scope]; - }, StatementContext::createTopLevel()); - - $returnTypes = []; - $hasNull = false; - foreach ($closureReturnStatements as [$returnNode, $returnScope]) { - if ($returnNode->expr === null) { - $hasNull = true; - continue; - } - - $returnTypes[] = $returnScope->getType($returnNode->expr); - } - - if (count($returnTypes) === 0) { - if (count($closureExecutionEnds) > 0 && !$hasNull) { - $returnType = new NeverType(true); - } else { - $returnType = new VoidType(); - } - } else { - if (count($closureExecutionEnds) > 0) { - $returnTypes[] = new NeverType(true); - } - if ($hasNull) { - $returnTypes[] = new NullType(); - } - $returnType = TypeCombinator::union(...$returnTypes); - } - - if (count($closureYieldStatements) > 0) { - $keyTypes = []; - $valueTypes = []; - foreach ($closureYieldStatements as [$yieldNode, $yieldScope]) { - if ($yieldNode instanceof Expr\Yield_) { - if ($yieldNode->key === null) { - $keyTypes[] = new IntegerType(); - } else { - $keyTypes[] = $yieldScope->getType($yieldNode->key); - } - - if ($yieldNode->value === null) { - $valueTypes[] = new NullType(); - } else { - $valueTypes[] = $yieldScope->getType($yieldNode->value); - } - - continue; - } - - $yieldFromType = $yieldScope->getType($yieldNode->expr); - $keyTypes[] = $yieldFromType->getIterableKeyType(); - $valueTypes[] = $yieldFromType->getIterableValueType(); - } - - $returnType = new GenericObjectType(Generator::class, [ - TypeCombinator::union(...$keyTypes), - TypeCombinator::union(...$valueTypes), - new MixedType(), - $returnType, - ]); - } else { - $returnType = TypehintHelper::decideType($this->getFunctionType($node->returnType, false, false), $returnType); - } - } - - return new ClosureType( - $parameters, - $returnType, - $isVariadic, - ); - } elseif ($node instanceof New_) { - if ($node->class instanceof Name) { - $type = $this->exactInstantiation($node, $node->class->toString()); - if ($type !== null) { - return $type; - } - - $lowercasedClassName = strtolower($node->class->toString()); - if ($lowercasedClassName === 'static') { - if (!$this->isInClass()) { - return new ErrorType(); - } - - return new StaticType($this->getClassReflection()); - } - if ($lowercasedClassName === 'parent') { - return new NonexistentParentClassType(); - } - - return new ObjectType($node->class->toString()); - } - if ($node->class instanceof Node\Stmt\Class_) { - $anonymousClassReflection = $this->reflectionProvider->getAnonymousClassReflection($node->class, $this); - - return new ObjectType($anonymousClassReflection->getName()); - } - - $exprType = $this->getType($node->class); - return $exprType->getObjectTypeOrClassStringObjectType(); - - } elseif ($node instanceof Array_) { - return $this->initializerExprTypeResolver->getArrayType($node, fn (Expr $expr): Type => $this->getType($expr)); - } elseif ($node instanceof Int_) { - return $this->getType($node->expr)->toInteger(); - } elseif ($node instanceof Bool_) { - return $this->getType($node->expr)->toBoolean(); - } elseif ($node instanceof Double) { - return $this->getType($node->expr)->toFloat(); - } elseif ($node instanceof Node\Expr\Cast\String_) { - return $this->getType($node->expr)->toString(); - } elseif ($node instanceof Node\Expr\Cast\Array_) { - return $this->getType($node->expr)->toArray(); - } elseif ($node instanceof Node\Scalar\MagicConst) { - return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this)); - } elseif ($node instanceof Object_) { - $castToObject = static function (Type $type): Type { - if ($type->isObject()->yes()) { - return $type; - } - - return new ObjectType('stdClass'); - }; - - $exprType = $this->getType($node->expr); - if ($exprType instanceof UnionType) { - return TypeCombinator::union(...array_map($castToObject, $exprType->getTypes())); - } - - return $castToObject($exprType); - } elseif ($node instanceof Unset_) { - return new NullType(); - } elseif ($node instanceof Expr\PostInc || $node instanceof Expr\PostDec) { - return $this->getType($node->var); - } elseif ($node instanceof Expr\PreInc || $node instanceof Expr\PreDec) { - $varType = $this->getType($node->var); - $varScalars = $varType->getConstantScalarValues(); - $stringType = new StringType(); - if (count($varScalars) > 0) { - $newTypes = []; - - foreach ($varScalars as $varValue) { - if ($node instanceof Expr\PreInc) { - ++$varValue; - } else { - --$varValue; - } - - $newTypes[] = $this->getTypeFromValue($varValue); - } - return TypeCombinator::union(...$newTypes); - } elseif ($varType->isString()->yes()) { - if ($varType->isLiteralString()->yes()) { - return new IntersectionType([$stringType, new AccessoryLiteralStringType()]); - } - return $stringType; - } - - if ($node instanceof Expr\PreInc) { - return $this->getType(new BinaryOp\Plus($node->var, new LNumber(1))); - } - - return $this->getType(new BinaryOp\Minus($node->var, new LNumber(1))); - } elseif ($node instanceof Expr\Yield_) { - $functionReflection = $this->getFunction(); - if ($functionReflection === null) { - return new MixedType(); - } - - $returnType = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(); - $generatorSendType = $returnType->getTemplateType(Generator::class, 'TSend'); - if ($generatorSendType instanceof ErrorType) { - return new MixedType(); - } - - return $generatorSendType; - } elseif ($node instanceof Expr\YieldFrom) { - $yieldFromType = $this->getType($node->expr); - $generatorReturnType = $yieldFromType->getTemplateType(Generator::class, 'TReturn'); - if ($generatorReturnType instanceof ErrorType) { - return new MixedType(); - } - - return $generatorReturnType; - } elseif ($node instanceof Expr\Match_) { - $cond = $node->cond; - $types = []; - - $matchScope = $this; - foreach ($node->arms as $arm) { - if ($arm->conds === null) { - $types[] = $matchScope->getType($arm->body); - continue; - } - - if (count($arm->conds) === 0) { - throw new ShouldNotHappenException(); - } - - $filteringExpr = null; - foreach ($arm->conds as $armCond) { - $armCondExpr = new BinaryOp\Identical($cond, $armCond); - - if ($filteringExpr === null) { - $filteringExpr = $armCondExpr; - continue; - } - - $filteringExpr = new BinaryOp\BooleanOr($filteringExpr, $armCondExpr); - } - - $filteringExprType = $matchScope->getType($filteringExpr); - - if (!(new ConstantBooleanType(false))->isSuperTypeOf($filteringExprType)->yes()) { - $truthyScope = $matchScope->filterByTruthyValue($filteringExpr); - $types[] = $truthyScope->getType($arm->body); - } - - $matchScope = $matchScope->filterByFalseyValue($filteringExpr); - } - - return TypeCombinator::union(...$types); - } - - if ($node instanceof Expr\Isset_) { - $issetResult = true; - foreach ($node->vars as $var) { - $result = $this->issetCheck($var, static function (Type $type): ?bool { - $isNull = (new NullType())->isSuperTypeOf($type); - if ($isNull->maybe()) { - return null; - } - - return !$isNull->yes(); - }); - if ($result !== null) { - if (!$result) { - return new ConstantBooleanType($result); - } - - continue; - } - - $issetResult = $result; - } - - if ($issetResult === null) { - return new BooleanType(); - } - - return new ConstantBooleanType($issetResult); - } - - if ($node instanceof Expr\AssignOp\Coalesce) { - return $this->getType(new BinaryOp\Coalesce($node->var, $node->expr, $node->getAttributes())); - } - - if ($node instanceof Expr\BinaryOp\Coalesce) { - $leftType = $this->getType($node->left); - - $result = $this->issetCheck($node->left, static function (Type $type): ?bool { - $isNull = (new NullType())->isSuperTypeOf($type); - if ($isNull->maybe()) { - return null; - } - - return !$isNull->yes(); - }); - - if ($result !== null && $result !== false) { - return TypeCombinator::removeNull($leftType); - } - - $rightType = $this->filterByFalseyValue( - new BinaryOp\NotIdentical($node->left, new ConstFetch(new Name('null'))), - )->getType($node->right); - - if ($result === null) { - return TypeCombinator::union( - TypeCombinator::removeNull($leftType), - $rightType, - ); - } - - return $rightType; - } - - if ($node instanceof ConstFetch) { - $constName = (string) $node->name; - $loweredConstName = strtolower($constName); - if ($loweredConstName === 'true') { - return new ConstantBooleanType(true); - } elseif ($loweredConstName === 'false') { - return new ConstantBooleanType(false); - } elseif ($loweredConstName === 'null') { - return new NullType(); - } - - $namespacedName = null; - if (!$node->name->isFullyQualified() && $this->getNamespace() !== null) { - $namespacedName = new FullyQualified([$this->getNamespace(), $node->name->toString()]); - } - $globalName = new FullyQualified($node->name->toString()); - - foreach ([$namespacedName, $globalName] as $name) { - if ($name === null) { - continue; - } - $constFetch = new ConstFetch($name); - if ($this->hasExpressionType($constFetch)->yes()) { - return $this->constantResolver->resolveConstantType( - $name->toString(), - $this->expressionTypes[$this->getNodeKey($constFetch)]->getType(), - ); - } - } - - $constantType = $this->constantResolver->resolveConstant($node->name, $this); - if ($constantType !== null) { - return $constantType; - } - - return new ErrorType(); - } elseif ($node instanceof Node\Expr\ClassConstFetch && $node->name instanceof Node\Identifier) { - if ($this->hasExpressionType($node)->yes()) { - return $this->expressionTypes[$exprString]->getType(); - } - return $this->initializerExprTypeResolver->getClassConstFetchTypeByReflection( - $node->class, - $node->name->name, - $this->isInClass() ? $this->getClassReflection() : null, - fn (Expr $expr): Type => $this->getType($expr), - ); - } - - if ($node instanceof Expr\Ternary) { - if ($node->if === null) { - $conditionType = $this->getType($node->cond); - $booleanConditionType = $conditionType->toBoolean(); - if ($booleanConditionType->isTrue()->yes()) { - return $this->filterByTruthyValue($node->cond)->getType($node->cond); - } - - if ($booleanConditionType->isFalse()->yes()) { - return $this->filterByFalseyValue($node->cond)->getType($node->else); - } - - return TypeCombinator::union( - TypeCombinator::removeFalsey($this->filterByTruthyValue($node->cond)->getType($node->cond)), - $this->filterByFalseyValue($node->cond)->getType($node->else), - ); - } - - $booleanConditionType = $this->getType($node->cond)->toBoolean(); - if ($booleanConditionType->isTrue()->yes()) { - return $this->filterByTruthyValue($node->cond)->getType($node->if); - } - - if ($booleanConditionType->isFalse()->yes()) { - return $this->filterByFalseyValue($node->cond)->getType($node->else); - } - - return TypeCombinator::union( - $this->filterByTruthyValue($node->cond)->getType($node->if), - $this->filterByFalseyValue($node->cond)->getType($node->else), - ); - } - - if ($node instanceof Variable && is_string($node->name)) { - if ($this->hasVariableType($node->name)->no()) { - return new ErrorType(); - } - - return $this->getVariableType($node->name); - } - - if ($node instanceof Expr\ArrayDimFetch && $node->dim !== null) { - return $this->getNullsafeShortCircuitingType( - $node->var, - $this->getTypeFromArrayDimFetch( - $node, - $this->getType($node->dim), - $this->getType($node->var), - ), - ); - } - - if ($node instanceof MethodCall && $node->name instanceof Node\Identifier) { - if ($this->nativeTypesPromoted) { - $typeCallback = function () use ($node): Type { - $methodReflection = $this->getMethodReflection( - $this->getNativeType($node->var), - $node->name->name, - ); - if ($methodReflection === null) { - return new ErrorType(); - } - - return ParametersAcceptorSelector::combineAcceptors($methodReflection->getVariants())->getNativeReturnType(); - }; - - return $this->getNullsafeShortCircuitingType($node->var, $typeCallback()); - } - - $typeCallback = function () use ($node): Type { - $returnType = $this->methodCallReturnType( - $this->getType($node->var), - $node->name->name, - $node, - ); - if ($returnType === null) { - return new ErrorType(); - } - return $returnType; - }; - - return $this->getNullsafeShortCircuitingType($node->var, $typeCallback()); - } - - if ($node instanceof Expr\NullsafeMethodCall) { - $varType = $this->getType($node->var); - if (!TypeCombinator::containsNull($varType)) { - return $this->getType(new MethodCall($node->var, $node->name, $node->args)); - } - - return TypeCombinator::union( - $this->filterByTruthyValue(new BinaryOp\NotIdentical($node->var, new ConstFetch(new Name('null')))) - ->getType(new MethodCall($node->var, $node->name, $node->args)), - new NullType(), - ); - } - - if ($node instanceof Expr\StaticCall && $node->name instanceof Node\Identifier) { - if ($this->nativeTypesPromoted) { - $typeCallback = function () use ($node): Type { - if ($node->class instanceof Name) { - $staticMethodCalledOnType = $this->resolveTypeByName($node->class); - } else { - $staticMethodCalledOnType = $this->getNativeType($node->class); - } - $methodReflection = $this->getMethodReflection( - $staticMethodCalledOnType, - $node->name->name, - ); - if ($methodReflection === null) { - return new ErrorType(); - } - - return ParametersAcceptorSelector::combineAcceptors($methodReflection->getVariants())->getNativeReturnType(); - }; - - $callType = $typeCallback(); - if ($node->class instanceof Expr) { - return $this->getNullsafeShortCircuitingType($node->class, $callType); - } - - return $callType; - } - - $typeCallback = function () use ($node): Type { - if ($node->class instanceof Name) { - $staticMethodCalledOnType = $this->resolveTypeByName($node->class); - } else { - $staticMethodCalledOnType = $this->getType($node->class)->getObjectTypeOrClassStringObjectType(); - } - - $returnType = $this->methodCallReturnType( - $staticMethodCalledOnType, - $node->name->toString(), - $node, - ); - if ($returnType === null) { - return new ErrorType(); - } - return $returnType; - }; - - $callType = $typeCallback(); - if ($node->class instanceof Expr) { - return $this->getNullsafeShortCircuitingType($node->class, $callType); - } - - return $callType; - } - - if ($node instanceof PropertyFetch && $node->name instanceof Node\Identifier) { - if ($this->nativeTypesPromoted) { - $propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($node, $this); - if ($propertyReflection === null) { - return new ErrorType(); - } - $nativeType = $propertyReflection->getNativeType(); - if ($nativeType === null) { - return new ErrorType(); - } - - return $this->getNullsafeShortCircuitingType($node->var, $nativeType); - } - - $typeCallback = function () use ($node): Type { - $returnType = $this->propertyFetchType( - $this->getType($node->var), - $node->name->name, - $node, - ); - if ($returnType === null) { - return new ErrorType(); - } - return $returnType; - }; - - return $this->getNullsafeShortCircuitingType($node->var, $typeCallback()); - } - - if ($node instanceof Expr\NullsafePropertyFetch) { - $varType = $this->getType($node->var); - if (!TypeCombinator::containsNull($varType)) { - return $this->getType(new PropertyFetch($node->var, $node->name)); - } - - return TypeCombinator::union( - $this->filterByTruthyValue(new BinaryOp\NotIdentical($node->var, new ConstFetch(new Name('null')))) - ->getType(new PropertyFetch($node->var, $node->name)), - new NullType(), - ); - } - - if ( - $node instanceof Expr\StaticPropertyFetch - && $node->name instanceof Node\VarLikeIdentifier - ) { - if ($this->nativeTypesPromoted) { - $propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($node, $this); - if ($propertyReflection === null) { - return new ErrorType(); - } - $nativeType = $propertyReflection->getNativeType(); - if ($nativeType === null) { - return new ErrorType(); - } - - if ($node->class instanceof Expr) { - return $this->getNullsafeShortCircuitingType($node->class, $nativeType); - } - - return $nativeType; - } - - $typeCallback = function () use ($node): Type { - if ($node->class instanceof Name) { - $staticPropertyFetchedOnType = $this->resolveTypeByName($node->class); - } else { - $staticPropertyFetchedOnType = $this->getType($node->class)->getObjectTypeOrClassStringObjectType(); - } - - $returnType = $this->propertyFetchType( - $staticPropertyFetchedOnType, - $node->name->toString(), - $node, - ); - if ($returnType === null) { - return new ErrorType(); - } - return $returnType; - }; - - $fetchType = $typeCallback(); - if ($node->class instanceof Expr) { - return $this->getNullsafeShortCircuitingType($node->class, $fetchType); - } - - return $fetchType; - } - - if ($node instanceof FuncCall) { - if ($node->name instanceof Expr) { - $calledOnType = $this->getType($node->name); - if ($calledOnType->isCallable()->no()) { - return new ErrorType(); - } - - return ParametersAcceptorSelector::selectFromArgs( - $this, - $node->getArgs(), - $calledOnType->getCallableParametersAcceptors($this), - )->getReturnType(); - } - - if (!$this->reflectionProvider->hasFunction($node->name, $this)) { - return new ErrorType(); - } - - $functionReflection = $this->reflectionProvider->getFunction($node->name, $this); - if ($this->nativeTypesPromoted) { - return ParametersAcceptorSelector::combineAcceptors($functionReflection->getVariants())->getNativeReturnType(); - } - - $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( - $this, - $node->getArgs(), - $functionReflection->getVariants(), - ); - $normalizedNode = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node); - if ($normalizedNode !== null) { - foreach ($this->dynamicReturnTypeExtensionRegistry->getDynamicFunctionReturnTypeExtensions() as $dynamicFunctionReturnTypeExtension) { - if (!$dynamicFunctionReturnTypeExtension->isFunctionSupported($functionReflection)) { - continue; - } - - $resolvedType = $dynamicFunctionReturnTypeExtension->getTypeFromFunctionCall( - $functionReflection, - $normalizedNode, - $this, - ); - if ($resolvedType !== null) { - return $resolvedType; - } - } - } - - return $parametersAcceptor->getReturnType(); - } - - return new MixedType(); - } - - private function getNullsafeShortCircuitingType(Expr $expr, Type $type): Type - { - if ($expr instanceof Expr\NullsafePropertyFetch || $expr instanceof Expr\NullsafeMethodCall) { - $varType = $this->getType($expr->var); - if (TypeCombinator::containsNull($varType)) { - return TypeCombinator::addNull($type); - } - - return $type; - } - - if ($expr instanceof Expr\ArrayDimFetch) { - return $this->getNullsafeShortCircuitingType($expr->var, $type); - } - - if ($expr instanceof PropertyFetch) { - return $this->getNullsafeShortCircuitingType($expr->var, $type); - } - - if ($expr instanceof Expr\StaticPropertyFetch && $expr->class instanceof Expr) { - return $this->getNullsafeShortCircuitingType($expr->class, $type); - } - - if ($expr instanceof MethodCall) { - return $this->getNullsafeShortCircuitingType($expr->var, $type); - } - - if ($expr instanceof Expr\StaticCall && $expr->class instanceof Expr) { - return $this->getNullsafeShortCircuitingType($expr->class, $type); - } - - return $type; - } - - /** - * @param callable(Type): ?bool $typeCallback - */ - public function issetCheck(Expr $expr, callable $typeCallback, ?bool $result = null): ?bool - { - // mirrored in PHPStan\Rules\IssetCheck - if ($expr instanceof Node\Expr\Variable && is_string($expr->name)) { - $hasVariable = $this->hasVariableType($expr->name); - if ($hasVariable->maybe()) { - return null; - } - - if ($result === null) { - if ($hasVariable->yes()) { - if ($expr->name === '_SESSION') { - return null; - } - - return $typeCallback($this->getVariableType($expr->name)); - } - - return false; - } - - return $result; - } elseif ($expr instanceof Node\Expr\ArrayDimFetch && $expr->dim !== null) { - $type = $this->getType($expr->var); - $dimType = $this->getType($expr->dim); - $hasOffsetValue = $type->hasOffsetValueType($dimType); - if (!$type->isOffsetAccessible()->yes()) { - return $result ?? $this->issetCheckUndefined($expr->var); - } - - if ($hasOffsetValue->no()) { - return false; - } - - // If offset is cannot be null, store this error message and see if one of the earlier offsets is. - // E.g. $array['a']['b']['c'] ?? null; is a valid coalesce if a OR b or C might be null. - if ($hasOffsetValue->yes()) { - $result = $typeCallback($type->getOffsetValueType($dimType)); - - if ($result !== null) { - return $this->issetCheck($expr->var, $typeCallback, $result); - } - } - - // Has offset, it is nullable - return null; - - } elseif ($expr instanceof Node\Expr\PropertyFetch || $expr instanceof Node\Expr\StaticPropertyFetch) { - - $propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $this); - - if ($propertyReflection === null) { - if ($expr instanceof Node\Expr\PropertyFetch) { - return $this->issetCheckUndefined($expr->var); - } - - if ($expr->class instanceof Expr) { - return $this->issetCheckUndefined($expr->class); - } - - return null; - } - - if (!$propertyReflection->isNative()) { - if ($expr instanceof Node\Expr\PropertyFetch) { - return $this->issetCheckUndefined($expr->var); - } - - if ($expr->class instanceof Expr) { - return $this->issetCheckUndefined($expr->class); - } - - return null; - } - - $nativeType = $propertyReflection->getNativeType(); - if (!$nativeType instanceof MixedType) { - if (!$this->hasExpressionType($expr)->yes()) { - if ($expr instanceof Node\Expr\PropertyFetch) { - return $this->issetCheckUndefined($expr->var); - } - - if ($expr->class instanceof Expr) { - return $this->issetCheckUndefined($expr->class); - } - - return null; - } - } - - if ($result !== null) { - return $result; - } - - $result = $typeCallback($propertyReflection->getWritableType()); - if ($result !== null) { - if ($expr instanceof Node\Expr\PropertyFetch) { - return $this->issetCheck($expr->var, $typeCallback, $result); - } - - if ($expr->class instanceof Expr) { - return $this->issetCheck($expr->class, $typeCallback, $result); - } - } - - return $result; - } - - if ($result !== null) { - return $result; - } - - return $typeCallback($this->getType($expr)); - } - - private function issetCheckUndefined(Expr $expr): ?bool - { - if ($expr instanceof Node\Expr\Variable && is_string($expr->name)) { - $hasVariable = $this->hasVariableType($expr->name); - if (!$hasVariable->no()) { - return null; - } - - return false; - } - - if ($expr instanceof Node\Expr\ArrayDimFetch && $expr->dim !== null) { - $type = $this->getType($expr->var); - $dimType = $this->getType($expr->dim); - $hasOffsetValue = $type->hasOffsetValueType($dimType); - if (!$type->isOffsetAccessible()->yes()) { - return $this->issetCheckUndefined($expr->var); - } - - if (!$hasOffsetValue->no()) { - return $this->issetCheckUndefined($expr->var); - } - - return false; - } - - if ($expr instanceof Expr\PropertyFetch) { - return $this->issetCheckUndefined($expr->var); - } - - if ($expr instanceof Expr\StaticPropertyFetch && $expr->class instanceof Expr) { - return $this->issetCheckUndefined($expr->class); - } - - return null; - } - - /** - * @param ParametersAcceptor[] $variants - */ - private function createFirstClassCallable(array $variants): Type - { - $closureTypes = []; - foreach ($variants as $variant) { - $returnType = $variant->getReturnType(); - if ($variant instanceof ParametersAcceptorWithPhpDocs) { - $returnType = $this->nativeTypesPromoted ? $variant->getNativeReturnType() : $returnType; - } - $parameters = $variant->getParameters(); - $closureTypes[] = new ClosureType( - $parameters, - $returnType, - $variant->isVariadic(), - $variant->getTemplateTypeMap(), - $variant->getResolvedTemplateTypeMap(), - ); - } - - return TypeCombinator::union(...$closureTypes); - } - - /** @api */ - public function getNativeType(Expr $expr): Type - { - return $this->promoteNativeTypes()->getType($expr); - } - - /** - * @api - * @deprecated Use getNativeType() - */ - public function doNotTreatPhpDocTypesAsCertain(): Scope - { - return $this->promoteNativeTypes(); - } - - private function promoteNativeTypes(): self - { - if ($this->nativeTypesPromoted) { - return $this; - } - - if ($this->scopeWithPromotedNativeTypes !== null) { - return $this->scopeWithPromotedNativeTypes; - } - - return $this->scopeWithPromotedNativeTypes = $this->scopeFactory->create( - $this->context, - $this->declareStrictTypes, - $this->function, - $this->namespace, - $this->nativeExpressionTypes, - [], - [], - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $this->inFunctionCallsStack, - $this->afterExtractCall, - $this->parentScope, - true, - ); - } - - /** - * @param Node\Expr\PropertyFetch|Node\Expr\StaticPropertyFetch $propertyFetch - */ - private function hasPropertyNativeType($propertyFetch): bool - { - $propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($propertyFetch, $this); - if ($propertyReflection === null) { - return false; - } - - if (!$propertyReflection->isNative()) { - return false; - } - - return !$propertyReflection->getNativeType() instanceof MixedType; - } - - /** @api */ - protected function getTypeFromArrayDimFetch( - Expr\ArrayDimFetch $arrayDimFetch, - Type $offsetType, - Type $offsetAccessibleType, - ): Type - { - if ($arrayDimFetch->dim === null) { - throw new ShouldNotHappenException(); - } - - if (!$offsetAccessibleType->isArray()->yes() && (new ObjectType(ArrayAccess::class))->isSuperTypeOf($offsetAccessibleType)->yes()) { - return $this->getType( - new MethodCall( - $arrayDimFetch->var, - new Node\Identifier('offsetGet'), - [ - new Node\Arg($arrayDimFetch->dim), - ], - ), - ); - } - - return $offsetAccessibleType->getOffsetValueType($offsetType); - } - - private function resolveExactName(Name $name): ?string - { - $originalClass = (string) $name; - - switch (strtolower($originalClass)) { - case 'self': - if (!$this->isInClass()) { - return null; - } - return $this->getClassReflection()->getName(); - case 'parent': - if (!$this->isInClass()) { - return null; - } - $currentClassReflection = $this->getClassReflection(); - if ($currentClassReflection->getParentClass() !== null) { - return $currentClassReflection->getParentClass()->getName(); - } - return null; - case 'static': - return null; - } - - return $originalClass; - } - - /** @api */ - public function resolveName(Name $name): string - { - $originalClass = (string) $name; - if ($this->isInClass()) { - if (in_array(strtolower($originalClass), [ - 'self', - 'static', - ], true)) { - if ($this->inClosureBindScopeClasses !== [] && $this->inClosureBindScopeClasses !== ['static']) { - return $this->inClosureBindScopeClasses[0]; - } - return $this->getClassReflection()->getName(); - } elseif ($originalClass === 'parent') { - $currentClassReflection = $this->getClassReflection(); - if ($currentClassReflection->getParentClass() !== null) { - return $currentClassReflection->getParentClass()->getName(); - } - } - } - - return $originalClass; - } - - /** @api */ - public function resolveTypeByName(Name $name): TypeWithClassName - { - if ($name->toLowerString() === 'static' && $this->isInClass()) { - if ($this->inClosureBindScopeClasses !== [] && $this->inClosureBindScopeClasses !== ['static']) { - if ($this->reflectionProvider->hasClass($this->inClosureBindScopeClasses[0])) { - return new StaticType($this->reflectionProvider->getClass($this->inClosureBindScopeClasses[0])); - } - } - - return new StaticType($this->getClassReflection()); - } - - $originalClass = $this->resolveName($name); - if ($this->isInClass()) { - if ($this->inClosureBindScopeClasses === [$originalClass]) { - if ($this->reflectionProvider->hasClass($originalClass)) { - return new ThisType($this->reflectionProvider->getClass($originalClass)); - } - return new ObjectType($originalClass); - } - - $thisType = new ThisType($this->getClassReflection()); - $ancestor = $thisType->getAncestorWithClassName($originalClass); - if ($ancestor !== null) { - return $ancestor; - } - } - - return new ObjectType($originalClass); - } - - /** - * @api - * @param mixed $value - */ - public function getTypeFromValue($value): Type - { - return ConstantTypeHelper::getTypeFromValue($value); - } - - /** - * @api - * @deprecated use hasExpressionType instead - */ - public function isSpecified(Expr $node): bool - { - return !$node instanceof Variable && $this->hasExpressionType($node)->yes(); - } - - /** @api */ - public function hasExpressionType(Expr $node): TrinaryLogic - { - $exprString = $this->getNodeKey($node); - if (!isset($this->expressionTypes[$exprString])) { - return TrinaryLogic::createNo(); - } - return $this->expressionTypes[$exprString]->getCertainty(); - } - - /** - * @param MethodReflection|FunctionReflection $reflection - */ - public function pushInFunctionCall($reflection): self - { - $stack = $this->inFunctionCallsStack; - $stack[] = $reflection; - - $scope = $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $stack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - $scope->resolvedTypes = $this->resolvedTypes; - $scope->truthyScopes = $this->truthyScopes; - $scope->falseyScopes = $this->falseyScopes; - - return $scope; - } - - public function popInFunctionCall(): self - { - $stack = $this->inFunctionCallsStack; - array_pop($stack); - - $scope = $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $stack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - $scope->resolvedTypes = $this->resolvedTypes; - $scope->truthyScopes = $this->truthyScopes; - $scope->falseyScopes = $this->falseyScopes; - - return $scope; - } - - /** @api */ - public function isInClassExists(string $className): bool - { - foreach ($this->inFunctionCallsStack as $inFunctionCall) { - if (!$inFunctionCall instanceof FunctionReflection) { - continue; - } - - if (in_array($inFunctionCall->getName(), [ - 'class_exists', - 'interface_exists', - 'trait_exists', - ], true)) { - return true; - } - } - $expr = new FuncCall(new FullyQualified('class_exists'), [ - new Arg(new String_(ltrim($className, '\\'))), - ]); - - return (new ConstantBooleanType(true))->isSuperTypeOf($this->getType($expr))->yes(); - } - - /** @api */ - public function isInFunctionExists(string $functionName): bool - { - $expr = new FuncCall(new FullyQualified('function_exists'), [ - new Arg(new String_(ltrim($functionName, '\\'))), - ]); - - return (new ConstantBooleanType(true))->isSuperTypeOf($this->getType($expr))->yes(); - } - - /** @api */ - public function enterClass(ClassReflection $classReflection): self - { - $thisHolder = ExpressionTypeHolder::createYes(new Variable('this'), new ThisType($classReflection)); - $constantTypes = $this->getConstantTypes(); - $constantTypes['$this'] = $thisHolder; - $nativeConstantTypes = $this->getNativeConstantTypes(); - $nativeConstantTypes['$this'] = $thisHolder; - - return $this->scopeFactory->create( - $this->context->enterClass($classReflection), - $this->isDeclareStrictTypes(), - null, - $this->getNamespace(), - $constantTypes, - $nativeConstantTypes, - [], - [], - null, - true, - [], - [], - [], - false, - $classReflection->isAnonymous() ? $this : null, - ); - } - - public function enterTrait(ClassReflection $traitReflection): self - { - $namespace = null; - $traitName = $traitReflection->getName(); - $traitNameParts = explode('\\', $traitName); - if (count($traitNameParts) > 1) { - $namespace = implode('\\', array_slice($traitNameParts, 0, -1)); - } - return $this->scopeFactory->create( - $this->context->enterTrait($traitReflection), - $this->isDeclareStrictTypes(), - $this->getFunction(), - $namespace, - $this->expressionTypes, - $this->nativeExpressionTypes, - [], - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - ); - } - - /** - * @api - * @param Type[] $phpDocParameterTypes - * @param Type[] $parameterOutTypes - */ - public function enterClassMethod( - Node\Stmt\ClassMethod $classMethod, - TemplateTypeMap $templateTypeMap, - array $phpDocParameterTypes, - ?Type $phpDocReturnType, - ?Type $throwType, - ?string $deprecatedDescription, - bool $isDeprecated, - bool $isInternal, - bool $isFinal, - ?bool $isPure = null, - bool $acceptsNamedArguments = true, - ?Assertions $asserts = null, - ?Type $selfOutType = null, - ?string $phpDocComment = null, - array $parameterOutTypes = [], - ): self - { - if (!$this->isInClass()) { - throw new ShouldNotHappenException(); - } - - return $this->enterFunctionLike( - new PhpMethodFromParserNodeReflection( - $this->getClassReflection(), - $classMethod, - $this->getFile(), - $templateTypeMap, - $this->getRealParameterTypes($classMethod), - array_map(static fn (Type $type): Type => TemplateTypeHelper::toArgument($type), $phpDocParameterTypes), - $this->getRealParameterDefaultValues($classMethod), - $this->transformStaticType($this->getFunctionType($classMethod->returnType, false, false)), - $phpDocReturnType !== null ? TemplateTypeHelper::toArgument($phpDocReturnType) : null, - $throwType, - $deprecatedDescription, - $isDeprecated, - $isInternal, - $isFinal, - $isPure, - $acceptsNamedArguments, - $asserts ?? Assertions::createEmpty(), - $selfOutType, - $phpDocComment, - array_map(static fn (Type $type): Type => TemplateTypeHelper::toArgument($type), $parameterOutTypes), - ), - !$classMethod->isStatic(), - ); - } - - private function transformStaticType(Type $type): Type - { - return TypeTraverser::map($type, function (Type $type, callable $traverse): Type { - if (!$this->isInClass()) { - return $type; - } - if ($type instanceof StaticType) { - $classReflection = $this->getClassReflection(); - $changedType = $type->changeBaseClass($classReflection); - if ($classReflection->isFinal()) { - $changedType = $changedType->getStaticObjectType(); - } - return $traverse($changedType); - } - - return $traverse($type); - }); - } - - /** - * @return Type[] - */ - private function getRealParameterTypes(Node\FunctionLike $functionLike): array - { - $realParameterTypes = []; - foreach ($functionLike->getParams() as $parameter) { - if (!$parameter->var instanceof Variable || !is_string($parameter->var->name)) { - throw new ShouldNotHappenException(); - } - $realParameterTypes[$parameter->var->name] = $this->getFunctionType( - $parameter->type, - $this->isParameterValueNullable($parameter), - false, - ); - } - - return $realParameterTypes; - } - - /** - * @return Type[] - */ - private function getRealParameterDefaultValues(Node\FunctionLike $functionLike): array - { - $realParameterDefaultValues = []; - foreach ($functionLike->getParams() as $parameter) { - if ($parameter->default === null) { - continue; - } - if (!$parameter->var instanceof Variable || !is_string($parameter->var->name)) { - throw new ShouldNotHappenException(); - } - $realParameterDefaultValues[$parameter->var->name] = $this->getType($parameter->default); - } - - return $realParameterDefaultValues; - } - - /** - * @api - * @param Type[] $phpDocParameterTypes - * @param Type[] $parameterOutTypes - */ - public function enterFunction( - Node\Stmt\Function_ $function, - TemplateTypeMap $templateTypeMap, - array $phpDocParameterTypes, - ?Type $phpDocReturnType, - ?Type $throwType, - ?string $deprecatedDescription, - bool $isDeprecated, - bool $isInternal, - bool $isFinal, - ?bool $isPure = null, - bool $acceptsNamedArguments = true, - ?Assertions $asserts = null, - ?string $phpDocComment = null, - array $parameterOutTypes = [], - ): self - { - return $this->enterFunctionLike( - new PhpFunctionFromParserNodeReflection( - $function, - $this->getFile(), - $templateTypeMap, - $this->getRealParameterTypes($function), - array_map(static fn (Type $type): Type => TemplateTypeHelper::toArgument($type), $phpDocParameterTypes), - $this->getRealParameterDefaultValues($function), - $this->getFunctionType($function->returnType, $function->returnType === null, false), - $phpDocReturnType !== null ? TemplateTypeHelper::toArgument($phpDocReturnType) : null, - $throwType, - $deprecatedDescription, - $isDeprecated, - $isInternal, - $isFinal, - $isPure, - $acceptsNamedArguments, - $asserts ?? Assertions::createEmpty(), - $phpDocComment, - array_map(static fn (Type $type): Type => TemplateTypeHelper::toArgument($type), $parameterOutTypes), - ), - false, - ); - } - - private function enterFunctionLike( - PhpFunctionFromParserNodeReflection $functionReflection, - bool $preserveThis, - ): self - { - $expressionTypes = []; - $nativeExpressionTypes = []; - foreach (ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getParameters() as $parameter) { - $parameterType = $parameter->getType(); - $paramExprString = '$' . $parameter->getName(); - if ($parameter->isVariadic()) { - if ($this->phpVersion->supportsNamedArguments() && $functionReflection->acceptsNamedArguments()) { - $parameterType = new ArrayType(new UnionType([new IntegerType(), new StringType()]), $parameterType); - } else { - $parameterType = AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), $parameterType)); - } - } - $parameterNode = new Variable($parameter->getName()); - $expressionTypes[$paramExprString] = ExpressionTypeHolder::createYes($parameterNode, $parameterType); - - $nativeParameterType = $parameter->getNativeType(); - if ($parameter->isVariadic()) { - if ($this->phpVersion->supportsNamedArguments() && $functionReflection->acceptsNamedArguments()) { - $nativeParameterType = new ArrayType(new UnionType([new IntegerType(), new StringType()]), $nativeParameterType); - } else { - $nativeParameterType = AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), $nativeParameterType)); - } - } - $nativeExpressionTypes[$paramExprString] = ExpressionTypeHolder::createYes($parameterNode, $nativeParameterType); - } - - if ($preserveThis && array_key_exists('$this', $this->expressionTypes)) { - $expressionTypes['$this'] = $this->expressionTypes['$this']; - } - if ($preserveThis && array_key_exists('$this', $this->nativeExpressionTypes)) { - $nativeExpressionTypes['$this'] = $this->nativeExpressionTypes['$this']; - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $functionReflection, - $this->getNamespace(), - array_merge($this->getConstantTypes(), $expressionTypes), - array_merge($this->getNativeConstantTypes(), $nativeExpressionTypes), - ); - } - - /** @api */ - public function enterNamespace(string $namespaceName): self - { - return $this->scopeFactory->create( - $this->context->beginFile(), - $this->isDeclareStrictTypes(), - null, - $namespaceName, - ); - } - - /** - * @param list $scopeClasses - */ - public function enterClosureBind(?Type $thisType, ?Type $nativeThisType, array $scopeClasses): self - { - $expressionTypes = $this->expressionTypes; - if ($thisType !== null) { - $expressionTypes['$this'] = ExpressionTypeHolder::createYes(new Variable('this'), $thisType); - } else { - unset($expressionTypes['$this']); - } - - $nativeExpressionTypes = $this->nativeExpressionTypes; - if ($nativeThisType !== null) { - $nativeExpressionTypes['$this'] = ExpressionTypeHolder::createYes(new Variable('this'), $nativeThisType); - } else { - unset($nativeExpressionTypes['$this']); - } - - if ($scopeClasses === ['static'] && $this->isInClass()) { - $scopeClasses = [$this->getClassReflection()->getName()]; - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $nativeExpressionTypes, - $this->conditionalExpressions, - $scopeClasses, - $this->anonymousFunctionReflection, - ); - } - - public function restoreOriginalScopeAfterClosureBind(self $originalScope): self - { - $expressionTypes = $this->expressionTypes; - if (isset($originalScope->expressionTypes['$this'])) { - $expressionTypes['$this'] = $originalScope->expressionTypes['$this']; - } else { - unset($expressionTypes['$this']); - } - - $nativeExpressionTypes = $this->nativeExpressionTypes; - if (isset($originalScope->nativeExpressionTypes['$this'])) { - $nativeExpressionTypes['$this'] = $originalScope->nativeExpressionTypes['$this']; - } else { - unset($nativeExpressionTypes['$this']); - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $nativeExpressionTypes, - $this->conditionalExpressions, - $originalScope->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - ); - } - - public function enterClosureCall(Type $thisType, Type $nativeThisType): self - { - $expressionTypes = $this->expressionTypes; - $expressionTypes['$this'] = ExpressionTypeHolder::createYes(new Variable('this'), $thisType); - - $nativeExpressionTypes = $this->nativeExpressionTypes; - $nativeExpressionTypes['$this'] = ExpressionTypeHolder::createYes(new Variable('this'), $nativeThisType); - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $nativeExpressionTypes, - $this->conditionalExpressions, - $thisType->getObjectClassNames(), - $this->anonymousFunctionReflection, - ); - } - - /** @api */ - public function isInClosureBind(): bool - { - return $this->inClosureBindScopeClasses !== []; - } - - /** - * @api - * @param ParameterReflection[]|null $callableParameters - */ - public function enterAnonymousFunction( - Expr\Closure $closure, - ?array $callableParameters = null, - ): self - { - $anonymousFunctionReflection = $this->getType($closure); - if (!$anonymousFunctionReflection instanceof ClosureType) { - throw new ShouldNotHappenException(); - } - - $scope = $this->enterAnonymousFunctionWithoutReflection($closure, $callableParameters); - - return $this->scopeFactory->create( - $scope->context, - $scope->isDeclareStrictTypes(), - $scope->getFunction(), - $scope->getNamespace(), - $scope->expressionTypes, - $scope->nativeExpressionTypes, - [], - $scope->inClosureBindScopeClasses, - $anonymousFunctionReflection, - true, - [], - [], - [], - false, - $this, - $this->nativeTypesPromoted, - ); - } - - /** - * @param ParameterReflection[]|null $callableParameters - */ - private function enterAnonymousFunctionWithoutReflection( - Expr\Closure $closure, - ?array $callableParameters = null, - ): self - { - $expressionTypes = []; - $nativeTypes = []; - foreach ($closure->params as $i => $parameter) { - if (!$parameter->var instanceof Variable || !is_string($parameter->var->name)) { - throw new ShouldNotHappenException(); - } - $paramExprString = sprintf('$%s', $parameter->var->name); - $isNullable = $this->isParameterValueNullable($parameter); - $parameterType = $this->getFunctionType($parameter->type, $isNullable, $parameter->variadic); - if ($callableParameters !== null) { - if (isset($callableParameters[$i])) { - $parameterType = TypehintHelper::decideType($parameterType, $callableParameters[$i]->getType()); - } elseif (count($callableParameters) > 0) { - $lastParameter = $callableParameters[count($callableParameters) - 1]; - if ($lastParameter->isVariadic()) { - $parameterType = TypehintHelper::decideType($parameterType, $lastParameter->getType()); - } else { - $parameterType = TypehintHelper::decideType($parameterType, new MixedType()); - } - } else { - $parameterType = TypehintHelper::decideType($parameterType, new MixedType()); - } - } - $holder = ExpressionTypeHolder::createYes($parameter->var, $parameterType); - $expressionTypes[$paramExprString] = $holder; - $nativeTypes[$paramExprString] = $holder; - } - - $nonRefVariableNames = []; - foreach ($closure->uses as $use) { - if (!is_string($use->var->name)) { - throw new ShouldNotHappenException(); - } - $variableName = $use->var->name; - $paramExprString = '$' . $use->var->name; - if ($use->byRef) { - $holder = ExpressionTypeHolder::createYes($use->var, new MixedType()); - $expressionTypes[$paramExprString] = $holder; - $nativeTypes[$paramExprString] = $holder; - continue; - } - $nonRefVariableNames[$variableName] = true; - if ($this->hasVariableType($variableName)->no()) { - $variableType = new ErrorType(); - $variableNativeType = new ErrorType(); - } else { - $variableType = $this->getVariableType($variableName); - $variableNativeType = $this->getNativeType($use->var); - } - $expressionTypes[$paramExprString] = ExpressionTypeHolder::createYes($use->var, $variableType); - $nativeTypes[$paramExprString] = ExpressionTypeHolder::createYes($use->var, $variableNativeType); - } - - foreach ($this->invalidateStaticExpressions($this->expressionTypes) as $exprString => $typeHolder) { - $expr = $typeHolder->getExpr(); - if ($expr instanceof Variable) { - continue; - } - $variables = (new NodeFinder())->findInstanceOf([$expr], Variable::class); - if ($variables === [] && !$this->expressionTypeIsUnchangeable($typeHolder)) { - continue; - } - foreach ($variables as $variable) { - if (!$variable instanceof Variable) { - continue 2; - } - if (!is_string($variable->name)) { - continue 2; - } - if (!array_key_exists($variable->name, $nonRefVariableNames)) { - continue 2; - } - } - - $expressionTypes[$exprString] = $typeHolder; - } - - if ($this->hasVariableType('this')->yes() && !$closure->static) { - $node = new Variable('this'); - $expressionTypes['$this'] = ExpressionTypeHolder::createYes($node, $this->getType($node)); - $nativeTypes['$this'] = ExpressionTypeHolder::createYes($node, $this->getNativeType($node)); - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - array_merge($this->getConstantTypes(), $expressionTypes), - array_merge($this->getNativeConstantTypes(), $nativeTypes), - [], - $this->inClosureBindScopeClasses, - new TrivialParametersAcceptor(), - true, - [], - [], - [], - false, - $this, - $this->nativeTypesPromoted, - ); - } - - private function expressionTypeIsUnchangeable(ExpressionTypeHolder $typeHolder): bool - { - $expr = $typeHolder->getExpr(); - $type = $typeHolder->getType(); - - return $expr instanceof FuncCall - && !$expr->isFirstClassCallable() - && $expr->name instanceof FullyQualified - && $expr->name->toLowerString() === 'function_exists' - && isset($expr->getArgs()[0]) - && count($this->getType($expr->getArgs()[0]->value)->getConstantStrings()) === 1 - && (new ConstantBooleanType(true))->isSuperTypeOf($type)->yes(); - } - - /** - * @param array $expressionTypes - * @return array - */ - private function invalidateStaticExpressions(array $expressionTypes): array - { - $filteredExpressionTypes = []; - $nodeFinder = new NodeFinder(); - foreach ($expressionTypes as $exprString => $expressionType) { - $staticExpression = $nodeFinder->findFirst( - [$expressionType->getExpr()], - static fn ($node) => $node instanceof Expr\StaticCall || $node instanceof Expr\StaticPropertyFetch, - ); - if ($staticExpression !== null) { - continue; - } - $filteredExpressionTypes[$exprString] = $expressionType; - } - return $filteredExpressionTypes; - } - - /** - * @api - * @param ParameterReflection[]|null $callableParameters - */ - public function enterArrowFunction(Expr\ArrowFunction $arrowFunction, ?array $callableParameters = null): self - { - $anonymousFunctionReflection = $this->getType($arrowFunction); - if (!$anonymousFunctionReflection instanceof ClosureType) { - throw new ShouldNotHappenException(); - } - - $scope = $this->enterArrowFunctionWithoutReflection($arrowFunction, $callableParameters); - - return $this->scopeFactory->create( - $scope->context, - $scope->isDeclareStrictTypes(), - $scope->getFunction(), - $scope->getNamespace(), - $scope->expressionTypes, - $scope->nativeExpressionTypes, - $scope->conditionalExpressions, - $scope->inClosureBindScopeClasses, - $anonymousFunctionReflection, - true, - [], - [], - [], - $scope->afterExtractCall, - $scope->parentScope, - $this->nativeTypesPromoted, - ); - } - - /** - * @param ParameterReflection[]|null $callableParameters - */ - private function enterArrowFunctionWithoutReflection(Expr\ArrowFunction $arrowFunction, ?array $callableParameters): self - { - $arrowFunctionScope = $this; - foreach ($arrowFunction->params as $i => $parameter) { - if ($parameter->type === null) { - $parameterType = new MixedType(); - } else { - $isNullable = $this->isParameterValueNullable($parameter); - $parameterType = $this->getFunctionType($parameter->type, $isNullable, $parameter->variadic); - } - - if ($callableParameters !== null) { - if (isset($callableParameters[$i])) { - $parameterType = TypehintHelper::decideType($parameterType, $callableParameters[$i]->getType()); - } elseif (count($callableParameters) > 0) { - $lastParameter = $callableParameters[count($callableParameters) - 1]; - if ($lastParameter->isVariadic()) { - $parameterType = TypehintHelper::decideType($parameterType, $lastParameter->getType()); - } else { - $parameterType = TypehintHelper::decideType($parameterType, new MixedType()); - } - } else { - $parameterType = TypehintHelper::decideType($parameterType, new MixedType()); - } - } - - if (!$parameter->var instanceof Variable || !is_string($parameter->var->name)) { - throw new ShouldNotHappenException(); - } - $arrowFunctionScope = $arrowFunctionScope->assignVariable($parameter->var->name, $parameterType, $parameterType); - } - - if ($arrowFunction->static) { - $arrowFunctionScope = $arrowFunctionScope->invalidateExpression(new Variable('this')); - } - - return $this->scopeFactory->create( - $arrowFunctionScope->context, - $this->isDeclareStrictTypes(), - $arrowFunctionScope->getFunction(), - $arrowFunctionScope->getNamespace(), - $this->invalidateStaticExpressions($arrowFunctionScope->expressionTypes), - $arrowFunctionScope->nativeExpressionTypes, - $arrowFunctionScope->conditionalExpressions, - $arrowFunctionScope->inClosureBindScopeClasses, - null, - true, - [], - [], - [], - $arrowFunctionScope->afterExtractCall, - $arrowFunctionScope->parentScope, - $this->nativeTypesPromoted, - ); - } - - public function isParameterValueNullable(Node\Param $parameter): bool - { - if ($parameter->default instanceof ConstFetch) { - return strtolower((string) $parameter->default->name) === 'null'; - } - - return false; - } - - /** - * @api - * @param Node\Name|Node\Identifier|Node\ComplexType|null $type - */ - public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type - { - if ($isNullable) { - return TypeCombinator::addNull( - $this->getFunctionType($type, false, $isVariadic), - ); - } - if ($isVariadic) { - if ($this->phpVersion->supportsNamedArguments()) { - return new ArrayType(new UnionType([new IntegerType(), new StringType()]), $this->getFunctionType( - $type, - false, - false, - )); - } - - return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), $this->getFunctionType( - $type, - false, - false, - ))); - } - - if ($type instanceof Name) { - $className = (string) $type; - $lowercasedClassName = strtolower($className); - if ($lowercasedClassName === 'parent') { - if ($this->isInClass() && $this->getClassReflection()->getParentClass() !== null) { - return new ObjectType($this->getClassReflection()->getParentClass()->getName()); - } - - return new NonexistentParentClassType(); - } - } - - return ParserNodeTypeToPHPStanType::resolve($type, $this->isInClass() ? $this->getClassReflection() : null); - } - - public function enterForeach(Expr $iteratee, string $valueName, ?string $keyName): self - { - $iterateeType = $this->getType($iteratee); - $nativeIterateeType = $this->getNativeType($iteratee); - $scope = $this->assignVariable($valueName, $iterateeType->getIterableValueType(), $nativeIterateeType->getIterableValueType()); - if ($keyName !== null) { - $scope = $scope->enterForeachKey($iteratee, $keyName); - } - - return $scope; - } - - public function enterForeachKey(Expr $iteratee, string $keyName): self - { - $iterateeType = $this->getType($iteratee); - $nativeIterateeType = $this->getNativeType($iteratee); - $scope = $this->assignVariable($keyName, $iterateeType->getIterableKeyType(), $nativeIterateeType->getIterableKeyType()); - - if ($iterateeType->isArray()->yes()) { - $scope = $scope->assignExpression( - new Expr\ArrayDimFetch($iteratee, new Variable($keyName)), - $iterateeType->getIterableValueType(), - $nativeIterateeType->getIterableValueType(), - ); - } - - return $scope; - } - - /** - * @deprecated Use enterCatchType - * @param Node\Name[] $classes - */ - public function enterCatch(array $classes, ?string $variableName): self - { - $type = TypeCombinator::union(...array_map(static fn (Node\Name $class): ObjectType => new ObjectType((string) $class), $classes)); - - return $this->enterCatchType($type, $variableName); - } - - public function enterCatchType(Type $catchType, ?string $variableName): self - { - if ($variableName === null) { - return $this; - } - - return $this->assignVariable( - $variableName, - TypeCombinator::intersect($catchType, new ObjectType(Throwable::class)), - TypeCombinator::intersect($catchType, new ObjectType(Throwable::class)), - ); - } - - public function enterExpressionAssign(Expr $expr): self - { - $exprString = $this->getNodeKey($expr); - $currentlyAssignedExpressions = $this->currentlyAssignedExpressions; - $currentlyAssignedExpressions[$exprString] = true; - - $scope = $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - $scope->resolvedTypes = $this->resolvedTypes; - $scope->truthyScopes = $this->truthyScopes; - $scope->falseyScopes = $this->falseyScopes; - - return $scope; - } - - public function exitExpressionAssign(Expr $expr): self - { - $exprString = $this->getNodeKey($expr); - $currentlyAssignedExpressions = $this->currentlyAssignedExpressions; - unset($currentlyAssignedExpressions[$exprString]); - - $scope = $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - $scope->resolvedTypes = $this->resolvedTypes; - $scope->truthyScopes = $this->truthyScopes; - $scope->falseyScopes = $this->falseyScopes; - - return $scope; - } - - /** @api */ - public function isInExpressionAssign(Expr $expr): bool - { - $exprString = $this->getNodeKey($expr); - return array_key_exists($exprString, $this->currentlyAssignedExpressions); - } - - public function setAllowedUndefinedExpression(Expr $expr): self - { - if ($this->phpVersion->deprecatesDynamicProperties() && $expr instanceof Expr\StaticPropertyFetch) { - return $this; - } - - $exprString = $this->getNodeKey($expr); - $currentlyAllowedUndefinedExpressions = $this->currentlyAllowedUndefinedExpressions; - $currentlyAllowedUndefinedExpressions[$exprString] = true; - - $scope = $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $this->currentlyAssignedExpressions, - $currentlyAllowedUndefinedExpressions, - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - $scope->resolvedTypes = $this->resolvedTypes; - $scope->truthyScopes = $this->truthyScopes; - $scope->falseyScopes = $this->falseyScopes; - - return $scope; - } - - public function unsetAllowedUndefinedExpression(Expr $expr): self - { - $exprString = $this->getNodeKey($expr); - $currentlyAllowedUndefinedExpressions = $this->currentlyAllowedUndefinedExpressions; - unset($currentlyAllowedUndefinedExpressions[$exprString]); - - $scope = $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->isInFirstLevelStatement(), - $this->currentlyAssignedExpressions, - $currentlyAllowedUndefinedExpressions, - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - $scope->resolvedTypes = $this->resolvedTypes; - $scope->truthyScopes = $this->truthyScopes; - $scope->falseyScopes = $this->falseyScopes; - - return $scope; - } - - /** @api */ - public function isUndefinedExpressionAllowed(Expr $expr): bool - { - $exprString = $this->getNodeKey($expr); - return array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions); - } - - public function assignVariable(string $variableName, Type $type, Type $nativeType, ?TrinaryLogic $certainty = null): self - { - $node = new Variable($variableName); - $scope = $this->assignExpression($node, $type, $nativeType); - if ($certainty !== null) { - if ($certainty->no()) { - throw new ShouldNotHappenException(); - } elseif (!$certainty->yes()) { - $exprString = '$' . $variableName; - $scope->expressionTypes[$exprString] = new ExpressionTypeHolder($node, $type, $certainty); - $scope->nativeExpressionTypes[$exprString] = new ExpressionTypeHolder($node, $nativeType, $certainty); - } - } - - return $scope; - } - - public function unsetExpression(Expr $expr): self - { - $scope = $this; - if ($expr instanceof Expr\ArrayDimFetch && $expr->dim !== null) { - $exprVarType = $scope->getType($expr->var); - $dimType = $scope->getType($expr->dim); - $unsetType = $exprVarType->unsetOffset($dimType); - $exprVarNativeType = $scope->getNativeType($expr->var); - $dimNativeType = $scope->getNativeType($expr->dim); - $unsetNativeType = $exprVarNativeType->unsetOffset($dimNativeType); - $scope = $scope->assignExpression($expr->var, $unsetType, $unsetNativeType)->invalidateExpression( - new FuncCall(new FullyQualified('count'), [new Arg($expr->var)]), - )->invalidateExpression( - new FuncCall(new FullyQualified('sizeof'), [new Arg($expr->var)]), - )->invalidateExpression( - new FuncCall(new Name('count'), [new Arg($expr->var)]), - )->invalidateExpression( - new FuncCall(new Name('sizeof'), [new Arg($expr->var)]), - ); - - if ($expr->var instanceof Expr\ArrayDimFetch && $expr->var->dim !== null) { - $scope = $scope->assignExpression( - $expr->var->var, - $this->getType($expr->var->var)->setOffsetValueType( - $scope->getType($expr->var->dim), - $scope->getType($expr->var), - ), - $this->getNativeType($expr->var->var)->setOffsetValueType( - $scope->getNativeType($expr->var->dim), - $scope->getNativeType($expr->var), - ), - ); - } - } - - return $scope->invalidateExpression($expr); - } - - public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType): self - { - if ($expr instanceof ConstFetch) { - $loweredConstName = strtolower($expr->name->toString()); - if (in_array($loweredConstName, ['true', 'false', 'null'], true)) { - return $this; - } - } - - if ($expr instanceof FuncCall && $expr->name instanceof Name && $type->isFalse()->yes()) { - $functionName = $this->reflectionProvider->resolveFunctionName($expr->name, $this); - if ($functionName !== null && in_array(strtolower($functionName), [ - 'is_dir', - 'is_file', - 'file_exists', - ], true)) { - return $this; - } - } - - $scope = $this; - if ($expr instanceof Expr\ArrayDimFetch && $expr->dim !== null) { - $dimType = $scope->getType($expr->dim)->toArrayKey(); - if ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType) { - $exprVarType = $scope->getType($expr->var); - if (!$exprVarType instanceof MixedType && !$exprVarType->isArray()->no()) { - $types = [ - new ArrayType(new MixedType(), new MixedType()), - new ObjectType(ArrayAccess::class), - new NullType(), - ]; - if ($dimType instanceof ConstantIntegerType) { - $types[] = new StringType(); - } - $scope = $scope->specifyExpressionType( - $expr->var, - TypeCombinator::intersect( - TypeCombinator::intersect($exprVarType, TypeCombinator::union(...$types)), - new HasOffsetValueType($dimType, $type), - ), - $scope->getNativeType($expr->var), - ); - } - } - } - - $exprString = $this->getNodeKey($expr); - $expressionTypes = $scope->expressionTypes; - $expressionTypes[$exprString] = ExpressionTypeHolder::createYes($expr, $type); - $nativeTypes = $scope->nativeExpressionTypes; - $nativeTypes[$exprString] = ExpressionTypeHolder::createYes($expr, $nativeType); - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $nativeTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $this->inFunctionCallsStack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - public function assignExpression(Expr $expr, Type $type, ?Type $nativeType = null): self - { - if ($nativeType === null) { - $nativeType = new MixedType(); - } - $scope = $this; - if ($expr instanceof PropertyFetch) { - $scope = $this->invalidateExpression($expr) - ->invalidateMethodsOnExpression($expr->var); - } elseif ($expr instanceof Expr\StaticPropertyFetch) { - $scope = $this->invalidateExpression($expr); - } elseif ($expr instanceof Variable) { - $scope = $this->invalidateExpression($expr); - } - - return $scope->specifyExpressionType($expr, $type, $nativeType); - } - - public function invalidateExpression(Expr $expressionToInvalidate, bool $requireMoreCharacters = false): self - { - $expressionTypes = $this->expressionTypes; - $nativeExpressionTypes = $this->nativeExpressionTypes; - $invalidated = false; - $exprStringToInvalidate = $this->getNodeKey($expressionToInvalidate); - - foreach ($expressionTypes as $exprString => $exprTypeHolder) { - $exprExpr = $exprTypeHolder->getExpr(); - if (!$this->shouldInvalidateExpression($exprStringToInvalidate, $expressionToInvalidate, $exprExpr, $requireMoreCharacters)) { - continue; - } - - unset($expressionTypes[$exprString]); - unset($nativeExpressionTypes[$exprString]); - $invalidated = true; - } - - $newConditionalExpressions = []; - foreach ($this->conditionalExpressions as $conditionalExprString => $holders) { - if (count($holders) === 0) { - continue; - } - if ($this->shouldInvalidateExpression($exprStringToInvalidate, $expressionToInvalidate, $holders[array_key_first($holders)]->getTypeHolder()->getExpr())) { - $invalidated = true; - continue; - } - foreach ($holders as $holder) { - $conditionalTypeHolders = $holder->getConditionExpressionTypeHolders(); - foreach ($conditionalTypeHolders as $conditionalTypeHolder) { - if ($this->shouldInvalidateExpression($exprStringToInvalidate, $expressionToInvalidate, $conditionalTypeHolder->getExpr())) { - $invalidated = true; - continue 3; - } - } - } - $newConditionalExpressions[$conditionalExprString] = $holders; - } - - if (!$invalidated) { - return $this; - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $nativeExpressionTypes, - $newConditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - private function shouldInvalidateExpression(string $exprStringToInvalidate, Expr $exprToInvalidate, Expr $expr, bool $requireMoreCharacters = false): bool - { - if ($requireMoreCharacters && $exprStringToInvalidate === $this->getNodeKey($expr)) { - return false; - } - - // Variables will not contain traversable expressions. skip the NodeFinder overhead - if ($expr instanceof Variable && is_string($expr->name)) { - return $exprStringToInvalidate === $this->getNodeKey($expr); - } - - $nodeFinder = new NodeFinder(); - $expressionToInvalidateClass = get_class($exprToInvalidate); - $found = $nodeFinder->findFirst([$expr], function (Node $node) use ($expressionToInvalidateClass, $exprStringToInvalidate): bool { - if (!$node instanceof $expressionToInvalidateClass) { - return false; - } - - $nodeString = $this->getNodeKey($node); - - return $nodeString === $exprStringToInvalidate; - }); - - if ($found === null) { - return false; - } - - if ($this->phpVersion->supportsReadOnlyProperties() && $expr instanceof PropertyFetch && $expr->name instanceof Node\Identifier && $requireMoreCharacters) { - $propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $this); - if ($propertyReflection !== null) { - $nativePropertyReflection = $propertyReflection->getNativeReflection(); - if ($nativePropertyReflection !== null && $nativePropertyReflection->isReadOnly()) { - return false; - } - } - } - - return true; - } - - private function invalidateMethodsOnExpression(Expr $expressionToInvalidate): self - { - $exprStringToInvalidate = $this->getNodeKey($expressionToInvalidate); - $expressionTypes = $this->expressionTypes; - $nativeExpressionTypes = $this->nativeExpressionTypes; - $invalidated = false; - $nodeFinder = new NodeFinder(); - foreach ($expressionTypes as $exprString => $exprTypeHolder) { - $expr = $exprTypeHolder->getExpr(); - $found = $nodeFinder->findFirst([$expr], function (Node $node) use ($exprStringToInvalidate): bool { - if (!$node instanceof MethodCall) { - return false; - } - - return $this->getNodeKey($node->var) === $exprStringToInvalidate; - }); - if ($found === null) { - continue; - } - - unset($expressionTypes[$exprString]); - unset($nativeExpressionTypes[$exprString]); - $invalidated = true; - } - - if (!$invalidated) { - return $this; - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - private function addTypeToExpression(Expr $expr, Type $type): self - { - $originalExprType = $this->getType($expr); - $nativeType = $this->getNativeType($expr); - - if ($originalExprType->equals($nativeType)) { - $newType = TypeCombinator::intersect($type, $originalExprType); - return $this->specifyExpressionType($expr, $newType, $newType); - } - - return $this->specifyExpressionType( - $expr, - TypeCombinator::intersect($type, $originalExprType), - TypeCombinator::intersect($type, $nativeType), - ); - } - - public function removeTypeFromExpression(Expr $expr, Type $typeToRemove): self - { - $exprType = $this->getType($expr); - if ( - $exprType instanceof NeverType || - $typeToRemove instanceof NeverType - ) { - return $this; - } - return $this->specifyExpressionType( - $expr, - TypeCombinator::remove($exprType, $typeToRemove), - TypeCombinator::remove($this->getNativeType($expr), $typeToRemove), - ); - } - - /** - * @api - * @return MutatingScope - */ - public function filterByTruthyValue(Expr $expr): Scope - { - $exprString = $this->getNodeKey($expr); - if (array_key_exists($exprString, $this->truthyScopes)) { - return $this->truthyScopes[$exprString]; - } - - $specifiedTypes = $this->typeSpecifier->specifyTypesInCondition($this, $expr, TypeSpecifierContext::createTruthy()); - $scope = $this->filterBySpecifiedTypes($specifiedTypes); - $this->truthyScopes[$exprString] = $scope; - - return $scope; - } - - /** - * @api - * @return MutatingScope - */ - public function filterByFalseyValue(Expr $expr): Scope - { - $exprString = $this->getNodeKey($expr); - if (array_key_exists($exprString, $this->falseyScopes)) { - return $this->falseyScopes[$exprString]; - } - - $specifiedTypes = $this->typeSpecifier->specifyTypesInCondition($this, $expr, TypeSpecifierContext::createFalsey()); - $scope = $this->filterBySpecifiedTypes($specifiedTypes); - $this->falseyScopes[$exprString] = $scope; - - return $scope; - } - - public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self - { - $typeSpecifications = []; - foreach ($specifiedTypes->getSureTypes() as $exprString => [$expr, $type]) { - if ($expr instanceof Node\Scalar || $expr instanceof Array_ || $expr instanceof Expr\UnaryMinus && $expr->expr instanceof Node\Scalar) { - continue; - } - $typeSpecifications[] = [ - 'sure' => true, - 'exprString' => $exprString, - 'expr' => $expr, - 'type' => $type, - ]; - } - foreach ($specifiedTypes->getSureNotTypes() as $exprString => [$expr, $type]) { - if ($expr instanceof Node\Scalar || $expr instanceof Array_ || $expr instanceof Expr\UnaryMinus && $expr->expr instanceof Node\Scalar) { - continue; - } - $typeSpecifications[] = [ - 'sure' => false, - 'exprString' => $exprString, - 'expr' => $expr, - 'type' => $type, - ]; - } - - usort($typeSpecifications, static function (array $a, array $b): int { - $length = strlen($a['exprString']) - strlen($b['exprString']); - if ($length !== 0) { - return $length; - } - - return $b['sure'] - $a['sure']; // @phpstan-ignore-line - }); - - $scope = $this; - $specifiedExpressions = []; - foreach ($typeSpecifications as $typeSpecification) { - $expr = $typeSpecification['expr']; - $type = $typeSpecification['type']; - if ($typeSpecification['sure']) { - if ($specifiedTypes->shouldOverwrite()) { - $scope = $scope->assignExpression($expr, $type, $type); - } else { - $scope = $scope->addTypeToExpression($expr, $type); - } - } else { - $scope = $scope->removeTypeFromExpression($expr, $type); - } - $specifiedExpressions[$this->getNodeKey($expr)] = ExpressionTypeHolder::createYes($expr, $scope->getType($expr)); - } - - foreach ($scope->conditionalExpressions as $conditionalExprString => $conditionalExpressions) { - foreach ($conditionalExpressions as $conditionalExpression) { - foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) { - if (!array_key_exists($holderExprString, $specifiedExpressions) || !$specifiedExpressions[$holderExprString]->equals($conditionalTypeHolder)) { - continue 2; - } - } - - if ($conditionalExpression->getTypeHolder()->getCertainty()->no()) { - unset($scope->expressionTypes[$conditionalExprString]); - } else { - $scope->expressionTypes[$conditionalExprString] = array_key_exists($conditionalExprString, $scope->expressionTypes) - ? new ExpressionTypeHolder( - $scope->expressionTypes[$conditionalExprString]->getExpr(), - TypeCombinator::intersect($scope->expressionTypes[$conditionalExprString]->getType(), $conditionalExpression->getTypeHolder()->getType()), - TrinaryLogic::maxMin($scope->expressionTypes[$conditionalExprString]->getCertainty(), $conditionalExpression->getTypeHolder()->getCertainty()), - ) - : $conditionalExpression->getTypeHolder(); - $specifiedExpressions[$conditionalExprString] = $conditionalExpression->getTypeHolder(); - } - } - } - - return $scope->scopeFactory->create( - $scope->context, - $scope->isDeclareStrictTypes(), - $scope->getFunction(), - $scope->getNamespace(), - $scope->expressionTypes, - $scope->nativeExpressionTypes, - array_merge($specifiedTypes->getNewConditionalExpressionHolders(), $scope->conditionalExpressions), - $scope->inClosureBindScopeClasses, - $scope->anonymousFunctionReflection, - $scope->inFirstLevelStatement, - $scope->currentlyAssignedExpressions, - $scope->currentlyAllowedUndefinedExpressions, - $scope->inFunctionCallsStack, - $scope->afterExtractCall, - $scope->parentScope, - $scope->nativeTypesPromoted, - ); - } - - /** - * @param ConditionalExpressionHolder[] $conditionalExpressionHolders - */ - public function addConditionalExpressions(string $exprString, array $conditionalExpressionHolders): self - { - $conditionalExpressions = $this->conditionalExpressions; - $conditionalExpressions[$exprString] = $conditionalExpressionHolders; - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - $conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $this->inFunctionCallsStack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - public function exitFirstLevelStatements(): self - { - if (!$this->inFirstLevelStatement) { - return $this; - } - - if ($this->scopeOutOfFirstLevelStatement !== null) { - return $this->scopeOutOfFirstLevelStatement; - } - - $scope = $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->expressionTypes, - $this->nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - false, - $this->currentlyAssignedExpressions, - $this->currentlyAllowedUndefinedExpressions, - $this->inFunctionCallsStack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - $scope->resolvedTypes = $this->resolvedTypes; - $scope->truthyScopes = $this->truthyScopes; - $scope->falseyScopes = $this->falseyScopes; - $this->scopeOutOfFirstLevelStatement = $scope; - - return $scope; - } - - /** @api */ - public function isInFirstLevelStatement(): bool - { - return $this->inFirstLevelStatement; - } - - public function mergeWith(?self $otherScope): self - { - if ($otherScope === null) { - return $this; - } - $ourExpressionTypes = $this->expressionTypes; - $theirExpressionTypes = $otherScope->expressionTypes; - - $mergedExpressionTypes = $this->mergeVariableHolders($ourExpressionTypes, $theirExpressionTypes); - $conditionalExpressions = $this->intersectConditionalExpressions($otherScope->conditionalExpressions); - $conditionalExpressions = $this->createConditionalExpressions( - $conditionalExpressions, - $ourExpressionTypes, - $theirExpressionTypes, - $mergedExpressionTypes, - ); - $conditionalExpressions = $this->createConditionalExpressions( - $conditionalExpressions, - $theirExpressionTypes, - $ourExpressionTypes, - $mergedExpressionTypes, - ); - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $mergedExpressionTypes, - $this->mergeVariableHolders($this->nativeExpressionTypes, $otherScope->nativeExpressionTypes), - $conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - [], - [], - [], - $this->afterExtractCall && $otherScope->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - /** - * @param array $otherConditionalExpressions - * @return array - */ - private function intersectConditionalExpressions(array $otherConditionalExpressions): array - { - $newConditionalExpressions = []; - foreach ($this->conditionalExpressions as $exprString => $holders) { - if (!array_key_exists($exprString, $otherConditionalExpressions)) { - continue; - } - - $otherHolders = $otherConditionalExpressions[$exprString]; - foreach (array_keys($holders) as $key) { - if (!array_key_exists($key, $otherHolders)) { - continue 2; - } - } - - $newConditionalExpressions[$exprString] = $holders; - } - - return $newConditionalExpressions; - } - - /** - * @param array $conditionalExpressions - * @param array $ourExpressionTypes - * @param array $theirExpressionTypes - * @param array $mergedExpressionTypes - * @return array - */ - private function createConditionalExpressions( - array $conditionalExpressions, - array $ourExpressionTypes, - array $theirExpressionTypes, - array $mergedExpressionTypes, - ): array - { - $newVariableTypes = $ourExpressionTypes; - foreach ($theirExpressionTypes as $exprString => $holder) { - if (!array_key_exists($exprString, $mergedExpressionTypes)) { - continue; - } - - if (!$mergedExpressionTypes[$exprString]->getType()->equals($holder->getType())) { - continue; - } - - unset($newVariableTypes[$exprString]); - } - - $typeGuards = []; - foreach ($newVariableTypes as $exprString => $holder) { - if (!$holder->getCertainty()->yes()) { - continue; - } - if (!array_key_exists($exprString, $mergedExpressionTypes)) { - continue; - } - if ($mergedExpressionTypes[$exprString]->getType()->equals($holder->getType())) { - continue; - } - - $typeGuards[$exprString] = $holder; - } - - if (count($typeGuards) === 0) { - return $conditionalExpressions; - } - - foreach ($newVariableTypes as $exprString => $holder) { - if ( - array_key_exists($exprString, $mergedExpressionTypes) - && $mergedExpressionTypes[$exprString]->equals($holder) - ) { - continue; - } - - $variableTypeGuards = $typeGuards; - unset($variableTypeGuards[$exprString]); - - if (count($variableTypeGuards) === 0) { - continue; - } - - $conditionalExpression = new ConditionalExpressionHolder($variableTypeGuards, $holder); - $conditionalExpressions[$exprString][$conditionalExpression->getKey()] = $conditionalExpression; - } - - foreach ($mergedExpressionTypes as $exprString => $mergedExprTypeHolder) { - if (array_key_exists($exprString, $ourExpressionTypes)) { - continue; - } - - $conditionalExpression = new ConditionalExpressionHolder($typeGuards, new ExpressionTypeHolder($mergedExprTypeHolder->getExpr(), new ErrorType(), TrinaryLogic::createNo())); - $conditionalExpressions[$exprString][$conditionalExpression->getKey()] = $conditionalExpression; - } - - return $conditionalExpressions; - } - - /** - * @param array $ourVariableTypeHolders - * @param array $theirVariableTypeHolders - * @return array - */ - private function mergeVariableHolders(array $ourVariableTypeHolders, array $theirVariableTypeHolders): array - { - $intersectedVariableTypeHolders = []; - foreach ($ourVariableTypeHolders as $exprString => $variableTypeHolder) { - if (isset($theirVariableTypeHolders[$exprString])) { - $intersectedVariableTypeHolders[$exprString] = $variableTypeHolder->and($theirVariableTypeHolders[$exprString]); - } else { - $intersectedVariableTypeHolders[$exprString] = ExpressionTypeHolder::createMaybe($variableTypeHolder->getExpr(), $variableTypeHolder->getType()); - } - } - - foreach ($theirVariableTypeHolders as $exprString => $variableTypeHolder) { - if (isset($intersectedVariableTypeHolders[$exprString])) { - continue; - } - - $intersectedVariableTypeHolders[$exprString] = ExpressionTypeHolder::createMaybe($variableTypeHolder->getExpr(), $variableTypeHolder->getType()); - } - - return $intersectedVariableTypeHolders; - } - - public function processFinallyScope(self $finallyScope, self $originalFinallyScope): self - { - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $this->processFinallyScopeVariableTypeHolders( - $this->expressionTypes, - $finallyScope->expressionTypes, - $originalFinallyScope->expressionTypes, - ), - $this->processFinallyScopeVariableTypeHolders( - $this->nativeExpressionTypes, - $finallyScope->nativeExpressionTypes, - $originalFinallyScope->nativeExpressionTypes, - ), - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - [], - [], - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - /** - * @param array $ourVariableTypeHolders - * @param array $finallyVariableTypeHolders - * @param array $originalVariableTypeHolders - * @return array - */ - private function processFinallyScopeVariableTypeHolders( - array $ourVariableTypeHolders, - array $finallyVariableTypeHolders, - array $originalVariableTypeHolders, - ): array - { - foreach ($finallyVariableTypeHolders as $exprString => $variableTypeHolder) { - if ( - isset($originalVariableTypeHolders[$exprString]) - && !$originalVariableTypeHolders[$exprString]->getType()->equals($variableTypeHolder->getType()) - ) { - $ourVariableTypeHolders[$exprString] = $variableTypeHolder; - continue; - } - - if (isset($originalVariableTypeHolders[$exprString])) { - continue; - } - - $ourVariableTypeHolders[$exprString] = $variableTypeHolder; - } - - return $ourVariableTypeHolders; - } - - /** - * @param Expr\ClosureUse[] $byRefUses - */ - public function processClosureScope( - self $closureScope, - ?self $prevScope, - array $byRefUses, - ): self - { - $nativeExpressionTypes = $this->nativeExpressionTypes; - $expressionTypes = $this->expressionTypes; - if (count($byRefUses) === 0) { - return $this; - } - - foreach ($byRefUses as $use) { - if (!is_string($use->var->name)) { - throw new ShouldNotHappenException(); - } - - $variableName = $use->var->name; - $variableExprString = '$' . $variableName; - - if (!$closureScope->hasVariableType($variableName)->yes()) { - $holder = ExpressionTypeHolder::createYes($use->var, new NullType()); - $expressionTypes[$variableExprString] = $holder; - $nativeExpressionTypes[$variableExprString] = $holder; - continue; - } - - $variableType = $closureScope->getVariableType($variableName); - - if ($prevScope !== null) { - $prevVariableType = $prevScope->getVariableType($variableName); - if (!$variableType->equals($prevVariableType)) { - $variableType = TypeCombinator::union($variableType, $prevVariableType); - $variableType = self::generalizeType($variableType, $prevVariableType, 0); - } - } - - $expressionTypes[$variableExprString] = ExpressionTypeHolder::createYes($use->var, $variableType); - $nativeExpressionTypes[$variableExprString] = ExpressionTypeHolder::createYes($use->var, $variableType); - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $nativeExpressionTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - [], - [], - $this->inFunctionCallsStack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - public function processAlwaysIterableForeachScopeWithoutPollute(self $finalScope): self - { - $expressionTypes = $this->expressionTypes; - foreach ($finalScope->expressionTypes as $variableExprString => $variableTypeHolder) { - if (!isset($expressionTypes[$variableExprString])) { - $expressionTypes[$variableExprString] = ExpressionTypeHolder::createMaybe($variableTypeHolder->getExpr(), $variableTypeHolder->getType()); - continue; - } - - $expressionTypes[$variableExprString] = new ExpressionTypeHolder( - $variableTypeHolder->getExpr(), - $variableTypeHolder->getType(), - $variableTypeHolder->getCertainty()->and($expressionTypes[$variableExprString]->getCertainty()), - ); - } - $nativeTypes = $this->nativeExpressionTypes; - foreach ($finalScope->nativeExpressionTypes as $variableExprString => $variableTypeHolder) { - if (!isset($nativeTypes[$variableExprString])) { - $nativeTypes[$variableExprString] = ExpressionTypeHolder::createMaybe($variableTypeHolder->getExpr(), $variableTypeHolder->getType()); - continue; - } - - $nativeTypes[$variableExprString] = new ExpressionTypeHolder( - $variableTypeHolder->getExpr(), - $variableTypeHolder->getType(), - $variableTypeHolder->getCertainty()->and($nativeTypes[$variableExprString]->getCertainty()), - ); - } - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $expressionTypes, - $nativeTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - [], - [], - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - public function generalizeWith(self $otherScope): self - { - $variableTypeHolders = $this->generalizeVariableTypeHolders( - $this->expressionTypes, - $otherScope->expressionTypes, - ); - $nativeTypes = $this->generalizeVariableTypeHolders( - $this->nativeExpressionTypes, - $otherScope->nativeExpressionTypes, - ); - - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - $this->getFunction(), - $this->getNamespace(), - $variableTypeHolders, - $nativeTypes, - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - [], - [], - [], - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - - /** - * @param array $variableTypeHolders - * @param array $otherVariableTypeHolders - * @return array - */ - private function generalizeVariableTypeHolders( - array $variableTypeHolders, - array $otherVariableTypeHolders, - ): array - { - foreach ($variableTypeHolders as $variableExprString => $variableTypeHolder) { - if (!isset($otherVariableTypeHolders[$variableExprString])) { - continue; - } - - $variableTypeHolders[$variableExprString] = new ExpressionTypeHolder( - $variableTypeHolder->getExpr(), - self::generalizeType($variableTypeHolder->getType(), $otherVariableTypeHolders[$variableExprString]->getType(), 0), - $variableTypeHolder->getCertainty(), - ); - } - - return $variableTypeHolders; - } - - private static function generalizeType(Type $a, Type $b, int $depth): Type - { - if ($a->equals($b)) { - return $a; - } - - $constantIntegers = ['a' => [], 'b' => []]; - $constantFloats = ['a' => [], 'b' => []]; - $constantBooleans = ['a' => [], 'b' => []]; - $constantStrings = ['a' => [], 'b' => []]; - $constantArrays = ['a' => [], 'b' => []]; - $generalArrays = ['a' => [], 'b' => []]; - $integerRanges = ['a' => [], 'b' => []]; - $otherTypes = []; - - foreach ([ - 'a' => TypeUtils::flattenTypes($a), - 'b' => TypeUtils::flattenTypes($b), - ] as $key => $types) { - foreach ($types as $type) { - if ($type instanceof ConstantIntegerType) { - $constantIntegers[$key][] = $type; - continue; - } - if ($type instanceof ConstantFloatType) { - $constantFloats[$key][] = $type; - continue; - } - if ($type instanceof ConstantBooleanType) { - $constantBooleans[$key][] = $type; - continue; - } - if ($type instanceof ConstantStringType) { - $constantStrings[$key][] = $type; - continue; - } - if ($type->isConstantArray()->yes()) { - $constantArrays[$key][] = $type; - continue; - } - if ($type->isArray()->yes()) { - $generalArrays[$key][] = $type; - continue; - } - if ($type instanceof IntegerRangeType) { - $integerRanges[$key][] = $type; - continue; - } - - $otherTypes[] = $type; - } - } - - $resultTypes = []; - foreach ([ - $constantFloats, - $constantBooleans, - $constantStrings, - ] as $constantTypes) { - if (count($constantTypes['a']) === 0) { - if (count($constantTypes['b']) > 0) { - $resultTypes[] = TypeCombinator::union(...$constantTypes['b']); - } - continue; - } elseif (count($constantTypes['b']) === 0) { - $resultTypes[] = TypeCombinator::union(...$constantTypes['a']); - continue; - } - - $aTypes = TypeCombinator::union(...$constantTypes['a']); - $bTypes = TypeCombinator::union(...$constantTypes['b']); - if ($aTypes->equals($bTypes)) { - $resultTypes[] = $aTypes; - continue; - } - - $resultTypes[] = TypeCombinator::union(...$constantTypes['a'], ...$constantTypes['b'])->generalize(GeneralizePrecision::moreSpecific()); - } - - if (count($constantArrays['a']) > 0) { - if (count($constantArrays['b']) === 0) { - $resultTypes[] = TypeCombinator::union(...$constantArrays['a']); - } else { - $constantArraysA = TypeCombinator::union(...$constantArrays['a']); - $constantArraysB = TypeCombinator::union(...$constantArrays['b']); - if ($constantArraysA->getIterableKeyType()->equals($constantArraysB->getIterableKeyType())) { - $resultArrayBuilder = ConstantArrayTypeBuilder::createEmpty(); - foreach (TypeUtils::flattenTypes($constantArraysA->getIterableKeyType()) as $keyType) { - $resultArrayBuilder->setOffsetValueType( - $keyType, - self::generalizeType( - $constantArraysA->getOffsetValueType($keyType), - $constantArraysB->getOffsetValueType($keyType), - $depth + 1, - ), - !$constantArraysA->hasOffsetValueType($keyType)->and($constantArraysB->hasOffsetValueType($keyType))->negate()->no(), - ); - } - - $resultTypes[] = $resultArrayBuilder->getArray(); - } else { - $resultType = new ArrayType( - TypeCombinator::union(self::generalizeType($constantArraysA->getIterableKeyType(), $constantArraysB->getIterableKeyType(), $depth + 1)), - TypeCombinator::union(self::generalizeType($constantArraysA->getIterableValueType(), $constantArraysB->getIterableValueType(), $depth + 1)), - ); - if ($constantArraysA->isIterableAtLeastOnce()->yes() && $constantArraysB->isIterableAtLeastOnce()->yes()) { - $resultType = TypeCombinator::intersect($resultType, new NonEmptyArrayType()); - } - if ($constantArraysA->isList()->yes() && $constantArraysB->isList()->yes()) { - $resultType = AccessoryArrayListType::intersectWith($resultType); - } - $resultTypes[] = $resultType; - } - } - } elseif (count($constantArrays['b']) > 0) { - $resultTypes[] = TypeCombinator::union(...$constantArrays['b']); - } - - if (count($generalArrays['a']) > 0) { - if (count($generalArrays['b']) === 0) { - $resultTypes[] = TypeCombinator::union(...$generalArrays['a']); - } else { - $generalArraysA = TypeCombinator::union(...$generalArrays['a']); - $generalArraysB = TypeCombinator::union(...$generalArrays['b']); - - $aValueType = $generalArraysA->getIterableValueType(); - $bValueType = $generalArraysB->getIterableValueType(); - if ( - $aValueType->isArray()->yes() - && $aValueType->isConstantArray()->no() - && $bValueType->isArray()->yes() - && $bValueType->isConstantArray()->no() - ) { - $aDepth = self::getArrayDepth($aValueType) + $depth; - $bDepth = self::getArrayDepth($bValueType) + $depth; - if ( - ($aDepth > 2 || $bDepth > 2) - && abs($aDepth - $bDepth) > 0 - ) { - $aValueType = new MixedType(); - $bValueType = new MixedType(); - } - } - - $resultType = new ArrayType( - TypeCombinator::union(self::generalizeType($generalArraysA->getIterableKeyType(), $generalArraysB->getIterableKeyType(), $depth + 1)), - TypeCombinator::union(self::generalizeType($aValueType, $bValueType, $depth + 1)), - ); - if ($generalArraysA->isIterableAtLeastOnce()->yes() && $generalArraysB->isIterableAtLeastOnce()->yes()) { - $resultType = TypeCombinator::intersect($resultType, new NonEmptyArrayType()); - } - if ($generalArraysA->isList()->yes() && $generalArraysB->isList()->yes()) { - $resultType = AccessoryArrayListType::intersectWith($resultType); - } - if ($generalArraysA->isOversizedArray()->yes() && $generalArraysB->isOversizedArray()->yes()) { - $resultType = TypeCombinator::intersect($resultType, new OversizedArrayType()); - } - $resultTypes[] = $resultType; - } - } elseif (count($generalArrays['b']) > 0) { - $resultTypes[] = TypeCombinator::union(...$generalArrays['b']); - } - - if (count($constantIntegers['a']) > 0) { - if (count($constantIntegers['b']) === 0) { - $resultTypes[] = TypeCombinator::union(...$constantIntegers['a']); - } else { - $constantIntegersA = TypeCombinator::union(...$constantIntegers['a']); - $constantIntegersB = TypeCombinator::union(...$constantIntegers['b']); - - if ($constantIntegersA->equals($constantIntegersB)) { - $resultTypes[] = $constantIntegersA; - } else { - $min = null; - $max = null; - foreach ($constantIntegers['a'] as $int) { - if ($min === null || $int->getValue() < $min) { - $min = $int->getValue(); - } - if ($max !== null && $int->getValue() <= $max) { - continue; - } - - $max = $int->getValue(); - } - - $gotGreater = false; - $gotSmaller = false; - foreach ($constantIntegers['b'] as $int) { - if ($int->getValue() > $max) { - $gotGreater = true; - } - if ($int->getValue() >= $min) { - continue; - } - - $gotSmaller = true; - } - - if ($gotGreater && $gotSmaller) { - $resultTypes[] = new IntegerType(); - } elseif ($gotGreater) { - $resultTypes[] = IntegerRangeType::fromInterval($min, null); - } elseif ($gotSmaller) { - $resultTypes[] = IntegerRangeType::fromInterval(null, $max); - } else { - $resultTypes[] = TypeCombinator::union($constantIntegersA, $constantIntegersB); - } - } - } - } elseif (count($constantIntegers['b']) > 0) { - $resultTypes[] = TypeCombinator::union(...$constantIntegers['b']); - } - - if (count($integerRanges['a']) > 0) { - if (count($integerRanges['b']) === 0) { - $resultTypes[] = TypeCombinator::union(...$integerRanges['a']); - } else { - $integerRangesA = TypeCombinator::union(...$integerRanges['a']); - $integerRangesB = TypeCombinator::union(...$integerRanges['b']); - - if ($integerRangesA->equals($integerRangesB)) { - $resultTypes[] = $integerRangesA; - } else { - $min = null; - $max = null; - foreach ($integerRanges['a'] as $range) { - if ($range->getMin() === null) { - $rangeMin = PHP_INT_MIN; - } else { - $rangeMin = $range->getMin(); - } - if ($range->getMax() === null) { - $rangeMax = PHP_INT_MAX; - } else { - $rangeMax = $range->getMax(); - } - - if ($min === null || $rangeMin < $min) { - $min = $rangeMin; - } - if ($max !== null && $rangeMax <= $max) { - continue; - } - - $max = $rangeMax; - } - - $gotGreater = false; - $gotSmaller = false; - foreach ($integerRanges['b'] as $range) { - if ($range->getMin() === null) { - $rangeMin = PHP_INT_MIN; - } else { - $rangeMin = $range->getMin(); - } - if ($range->getMax() === null) { - $rangeMax = PHP_INT_MAX; - } else { - $rangeMax = $range->getMax(); - } - - if ($rangeMax > $max) { - $gotGreater = true; - } - if ($rangeMin >= $min) { - continue; - } - - $gotSmaller = true; - } - - if ($min === PHP_INT_MIN) { - $min = null; - } - if ($max === PHP_INT_MAX) { - $max = null; - } - - if ($gotGreater && $gotSmaller) { - $resultTypes[] = new IntegerType(); - } elseif ($gotGreater) { - $resultTypes[] = IntegerRangeType::fromInterval($min, null); - } elseif ($gotSmaller) { - $resultTypes[] = IntegerRangeType::fromInterval(null, $max); - } else { - $resultTypes[] = TypeCombinator::union($integerRangesA, $integerRangesB); - } - } - } - } elseif (count($integerRanges['b']) > 0) { - $resultTypes[] = TypeCombinator::union(...$integerRanges['b']); - } - - $accessoryTypes = array_map( - static fn (Type $type): Type => $type->generalize(GeneralizePrecision::moreSpecific()), - TypeUtils::getAccessoryTypes($a), - ); - - return TypeCombinator::intersect( - TypeCombinator::union(...$resultTypes, ...$otherTypes), - ...$accessoryTypes, - ); - } - - private static function getArrayDepth(Type $type): int - { - $depth = 0; - $arrays = TypeUtils::getAnyArrays($type); - while (count($arrays) > 0) { - $temp = $type->getIterableValueType(); - $type = $temp; - $arrays = TypeUtils::getAnyArrays($type); - $depth++; - } - - return $depth; - } - - public function equals(self $otherScope): bool - { - if (!$this->context->equals($otherScope->context)) { - return false; - } - - if (!$this->compareVariableTypeHolders($this->expressionTypes, $otherScope->expressionTypes)) { - return false; - } - return $this->compareVariableTypeHolders($this->nativeExpressionTypes, $otherScope->nativeExpressionTypes); - } - - /** - * @param array $variableTypeHolders - * @param array $otherVariableTypeHolders - */ - private function compareVariableTypeHolders(array $variableTypeHolders, array $otherVariableTypeHolders): bool - { - if (count($variableTypeHolders) !== count($otherVariableTypeHolders)) { - return false; - } - foreach ($variableTypeHolders as $variableExprString => $variableTypeHolder) { - if (!isset($otherVariableTypeHolders[$variableExprString])) { - return false; - } - - if (!$variableTypeHolder->getCertainty()->equals($otherVariableTypeHolders[$variableExprString]->getCertainty())) { - return false; - } - - if (!$variableTypeHolder->getType()->equals($otherVariableTypeHolders[$variableExprString]->getType())) { - return false; - } - - unset($otherVariableTypeHolders[$variableExprString]); - } - - return true; - } - - /** @api */ - public function canAccessProperty(PropertyReflection $propertyReflection): bool - { - return $this->canAccessClassMember($propertyReflection); - } - - /** @api */ - public function canCallMethod(MethodReflection $methodReflection): bool - { - if ($this->canAccessClassMember($methodReflection)) { - return true; - } - - return $this->canAccessClassMember($methodReflection->getPrototype()); - } - - /** @api */ - public function canAccessConstant(ConstantReflection $constantReflection): bool - { - return $this->canAccessClassMember($constantReflection); - } - - private function canAccessClassMember(ClassMemberReflection $classMemberReflection): bool - { - if ($classMemberReflection->isPublic()) { - return true; - } - - $classReflectionName = $classMemberReflection->getDeclaringClass()->getName(); - $canAccessClassMember = static function (ClassReflection $classReflection) use ($classMemberReflection, $classReflectionName) { - if ($classMemberReflection->isPrivate()) { - return $classReflection->getName() === $classReflectionName; - } - - // protected - - if ( - $classReflection->getName() === $classReflectionName - || $classReflection->isSubclassOf($classReflectionName) - ) { - return true; - } - - return $classMemberReflection->getDeclaringClass()->isSubclassOf($classReflection->getName()); - }; - - foreach ($this->inClosureBindScopeClasses as $inClosureBindScopeClass) { - if (!$this->reflectionProvider->hasClass($inClosureBindScopeClass)) { - continue; - } - - if ($canAccessClassMember($this->reflectionProvider->getClass($inClosureBindScopeClass))) { - return true; - } - } - - if ($this->isInClass()) { - return $canAccessClassMember($this->getClassReflection()); - } - - return false; - } - - /** - * @return string[] - */ - public function debug(): array - { - $descriptions = []; - foreach ($this->expressionTypes as $name => $variableTypeHolder) { - $key = sprintf('%s (%s)', $name, $variableTypeHolder->getCertainty()->describe()); - $descriptions[$key] = $variableTypeHolder->getType()->describe(VerbosityLevel::precise()); - } - foreach ($this->nativeExpressionTypes as $exprString => $nativeTypeHolder) { - $key = sprintf('native %s', $exprString); - $descriptions[$key] = $nativeTypeHolder->getType()->describe(VerbosityLevel::precise()); - } - - return $descriptions; - } - - private function exactInstantiation(New_ $node, string $className): ?Type - { - $resolvedClassName = $this->resolveExactName(new Name($className)); - if ($resolvedClassName === null) { - return null; - } - - if (!$this->reflectionProvider->hasClass($resolvedClassName)) { - return null; - } - - $classReflection = $this->reflectionProvider->getClass($resolvedClassName); - if ($classReflection->hasConstructor()) { - $constructorMethod = $classReflection->getConstructor(); - } else { - $constructorMethod = new DummyConstructorReflection($classReflection); - } - - $resolvedTypes = []; - $methodCall = new Expr\StaticCall( - new Name($resolvedClassName), - new Node\Identifier($constructorMethod->getName()), - $node->getArgs(), - ); - - $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( - $this, - $methodCall->getArgs(), - $constructorMethod->getVariants(), - ); - $normalizedMethodCall = ArgumentsNormalizer::reorderStaticCallArguments($parametersAcceptor, $methodCall); - - if ($normalizedMethodCall !== null) { - foreach ($this->dynamicReturnTypeExtensionRegistry->getDynamicStaticMethodReturnTypeExtensionsForClass($classReflection->getName()) as $dynamicStaticMethodReturnTypeExtension) { - if (!$dynamicStaticMethodReturnTypeExtension->isStaticMethodSupported($constructorMethod)) { - continue; - } - - $resolvedType = $dynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall( - $constructorMethod, - $normalizedMethodCall, - $this, - ); - if ($resolvedType === null) { - continue; - } - - $resolvedTypes[] = $resolvedType; - } - } - - if (count($resolvedTypes) > 0) { - return TypeCombinator::union(...$resolvedTypes); - } - - $methodResult = $this->getType($methodCall); - if ($methodResult instanceof NeverType && $methodResult->isExplicit()) { - return $methodResult; - } - - $objectType = new ObjectType($resolvedClassName); - if (!$classReflection->isGeneric()) { - return $objectType; - } - - $assignedToProperty = $node->getAttribute(NewAssignedToPropertyVisitor::ATTRIBUTE_NAME); - if ($assignedToProperty !== null) { - $constructorVariant = ParametersAcceptorSelector::selectSingle($constructorMethod->getVariants()); - $classTemplateTypes = $classReflection->getTemplateTypeMap()->getTypes(); - $originalClassTemplateTypes = $classTemplateTypes; - foreach ($constructorVariant->getParameters() as $parameter) { - TypeTraverser::map($parameter->getType(), static function (Type $type, callable $traverse) use (&$classTemplateTypes): Type { - if ($type instanceof TemplateType && array_key_exists($type->getName(), $classTemplateTypes)) { - $classTemplateType = $classTemplateTypes[$type->getName()]; - if ($classTemplateType instanceof TemplateType && $classTemplateType->getScope()->equals($type->getScope())) { - unset($classTemplateTypes[$type->getName()]); - } - return $type; - } - - return $traverse($type); - }); - } - - if (count($classTemplateTypes) === count($originalClassTemplateTypes)) { - $propertyType = TypeCombinator::removeNull($this->getType($assignedToProperty)); - if ($objectType->isSuperTypeOf($propertyType)->yes()) { - return $propertyType; - } - } - } - - if ($constructorMethod instanceof DummyConstructorReflection || $constructorMethod->getDeclaringClass()->getName() !== $classReflection->getName()) { - return new GenericObjectType( - $resolvedClassName, - $classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()), - ); - } - - $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( - $this, - $methodCall->getArgs(), - $constructorMethod->getVariants(), - ); - - if ($this->explicitMixedInUnknownGenericNew) { - return new GenericObjectType( - $resolvedClassName, - $classReflection->typeMapToList($parametersAcceptor->getResolvedTemplateTypeMap()), - ); - } - - $resolvedPhpDoc = $classReflection->getResolvedPhpDoc(); - if ($resolvedPhpDoc === null) { - return $objectType; - } - - $list = []; - $typeMap = $parametersAcceptor->getResolvedTemplateTypeMap(); - foreach ($resolvedPhpDoc->getTemplateTags() as $tag) { - $templateType = $typeMap->getType($tag->getName()); - if ($templateType !== null) { - $list[] = $templateType; - continue; - } - $bound = $tag->getBound(); - if ($bound instanceof MixedType && $bound->isExplicitMixed()) { - $bound = new MixedType(false); - } - $list[] = $bound; - } - - return new GenericObjectType( - $resolvedClassName, - $list, - ); - } - - private function filterTypeWithMethod(Type $typeWithMethod, string $methodName): ?Type - { - if ($typeWithMethod instanceof UnionType) { - $newTypes = []; - foreach ($typeWithMethod->getTypes() as $innerType) { - if (!$innerType->hasMethod($methodName)->yes()) { - continue; - } - - $newTypes[] = $innerType; - } - if (count($newTypes) === 0) { - return null; - } - $typeWithMethod = TypeCombinator::union(...$newTypes); - } - - if (!$typeWithMethod->hasMethod($methodName)->yes()) { - return null; - } - - return $typeWithMethod; - } - - /** @api */ - public function getMethodReflection(Type $typeWithMethod, string $methodName): ?ExtendedMethodReflection - { - $type = $this->filterTypeWithMethod($typeWithMethod, $methodName); - if ($type === null) { - return null; - } - - return $type->getMethod($methodName, $this); - } - - /** - * @param MethodCall|Node\Expr\StaticCall $methodCall - */ - private function methodCallReturnType(Type $typeWithMethod, string $methodName, Expr $methodCall): ?Type - { - $typeWithMethod = $this->filterTypeWithMethod($typeWithMethod, $methodName); - if ($typeWithMethod === null) { - return null; - } - - $methodReflection = $typeWithMethod->getMethod($methodName, $this); - $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( - $this, - $methodCall->getArgs(), - $methodReflection->getVariants(), - ); - if ($methodCall instanceof MethodCall) { - $normalizedMethodCall = ArgumentsNormalizer::reorderMethodArguments($parametersAcceptor, $methodCall); - } else { - $normalizedMethodCall = ArgumentsNormalizer::reorderStaticCallArguments($parametersAcceptor, $methodCall); - } - if ($normalizedMethodCall === null) { - return $parametersAcceptor->getReturnType(); - } - - $resolvedTypes = []; - foreach ($typeWithMethod->getObjectClassNames() as $className) { - if ($normalizedMethodCall instanceof MethodCall) { - foreach ($this->dynamicReturnTypeExtensionRegistry->getDynamicMethodReturnTypeExtensionsForClass($className) as $dynamicMethodReturnTypeExtension) { - if (!$dynamicMethodReturnTypeExtension->isMethodSupported($methodReflection)) { - continue; - } - - $resolvedType = $dynamicMethodReturnTypeExtension->getTypeFromMethodCall($methodReflection, $normalizedMethodCall, $this); - if ($resolvedType === null) { - continue; - } - - $resolvedTypes[] = $resolvedType; - } - } else { - foreach ($this->dynamicReturnTypeExtensionRegistry->getDynamicStaticMethodReturnTypeExtensionsForClass($className) as $dynamicStaticMethodReturnTypeExtension) { - if (!$dynamicStaticMethodReturnTypeExtension->isStaticMethodSupported($methodReflection)) { - continue; - } - - $resolvedType = $dynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall( - $methodReflection, - $normalizedMethodCall, - $this, - ); - if ($resolvedType === null) { - continue; - } - - $resolvedTypes[] = $resolvedType; - } - } - } - - if (count($resolvedTypes) > 0) { - return TypeCombinator::union(...$resolvedTypes); - } - - return $parametersAcceptor->getReturnType(); - } - - /** @api */ - public function getPropertyReflection(Type $typeWithProperty, string $propertyName): ?PropertyReflection - { - if ($typeWithProperty instanceof UnionType) { - $newTypes = []; - foreach ($typeWithProperty->getTypes() as $innerType) { - if (!$innerType->hasProperty($propertyName)->yes()) { - continue; - } - - $newTypes[] = $innerType; - } - if (count($newTypes) === 0) { - return null; - } - $typeWithProperty = TypeCombinator::union(...$newTypes); - } - if (!$typeWithProperty->hasProperty($propertyName)->yes()) { - return null; - } - - return $typeWithProperty->getProperty($propertyName, $this); - } - - /** - * @param PropertyFetch|Node\Expr\StaticPropertyFetch $propertyFetch - */ - private function propertyFetchType(Type $fetchedOnType, string $propertyName, Expr $propertyFetch): ?Type - { - $propertyReflection = $this->getPropertyReflection($fetchedOnType, $propertyName); - if ($propertyReflection === null) { - return null; - } - - if ($this->isInExpressionAssign($propertyFetch)) { - return $propertyReflection->getWritableType(); - } - - return $propertyReflection->getReadableType(); - } - - public function getConstantReflection(Type $typeWithConstant, string $constantName): ?ConstantReflection - { - if ($typeWithConstant instanceof UnionType) { - $newTypes = []; - foreach ($typeWithConstant->getTypes() as $innerType) { - if (!$innerType->hasConstant($constantName)->yes()) { - continue; - } - - $newTypes[] = $innerType; - } - if (count($newTypes) === 0) { - return null; - } - $typeWithConstant = TypeCombinator::union(...$newTypes); - } - if (!$typeWithConstant->hasConstant($constantName)->yes()) { - return null; - } - - return $typeWithConstant->getConstant($constantName); - } - - /** - * @return array - */ - private function getConstantTypes(): array - { - $constantTypes = []; - foreach ($this->expressionTypes as $exprString => $typeHolder) { - $expr = $typeHolder->getExpr(); - if (!$expr instanceof ConstFetch) { - continue; - } - $constantTypes[$exprString] = $typeHolder; - } - return $constantTypes; - } - - /** - * @return array - */ - private function getNativeConstantTypes(): array - { - $constantTypes = []; - foreach ($this->nativeExpressionTypes as $exprString => $typeHolder) { - $expr = $typeHolder->getExpr(); - if (!$expr instanceof ConstFetch) { - continue; - } - $constantTypes[$exprString] = $typeHolder; - } - return $constantTypes; - } - -} diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/reflection/carbon.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/reflection/carbon.test deleted file mode 100644 index a3e8fb33e7..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/reflection/carbon.test +++ /dev/null @@ -1,839 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon; - -use Carbon\Traits\Date; -use DateTime; -use DateTimeInterface; - -/** - * A simple API extension for DateTime. - * - * - * - * @property string $localeDayOfWeek the day of week in current locale - * @property string $shortLocaleDayOfWeek the abbreviated day of week in current locale - * @property string $localeMonth the month in current locale - * @property string $shortLocaleMonth the abbreviated month in current locale - * @property int $year - * @property int $yearIso - * @property int $month - * @property int $day - * @property int $hour - * @property int $minute - * @property int $second - * @property int $micro - * @property int $microsecond - * @property int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) - * @property int|float|string $timestamp seconds since the Unix Epoch - * @property string $englishDayOfWeek the day of week in English - * @property string $shortEnglishDayOfWeek the abbreviated day of week in English - * @property string $englishMonth the month in English - * @property string $shortEnglishMonth the abbreviated month in English - * @property int $milliseconds - * @property int $millisecond - * @property int $milli - * @property int $week 1 through 53 - * @property int $isoWeek 1 through 53 - * @property int $weekYear year according to week format - * @property int $isoWeekYear year according to ISO week format - * @property int $age does a diffInYears() with default parameters - * @property int $offset the timezone offset in seconds from UTC - * @property int $offsetMinutes the timezone offset in minutes from UTC - * @property int $offsetHours the timezone offset in hours from UTC - * @property CarbonTimeZone $timezone the current timezone - * @property CarbonTimeZone $tz alias of $timezone - * @property int $centuryOfMillennium The value of the century starting from the beginning of the current millennium - * @property int $dayOfCentury The value of the day starting from the beginning of the current century - * @property int $dayOfDecade The value of the day starting from the beginning of the current decade - * @property int $dayOfMillennium The value of the day starting from the beginning of the current millennium - * @property int $dayOfMonth The value of the day starting from the beginning of the current month - * @property int $dayOfQuarter The value of the day starting from the beginning of the current quarter - * @property int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) - * @property int $dayOfYear 1 through 366 - * @property int $decadeOfCentury The value of the decade starting from the beginning of the current century - * @property int $decadeOfMillennium The value of the decade starting from the beginning of the current millennium - * @property int $hourOfCentury The value of the hour starting from the beginning of the current century - * @property int $hourOfDay The value of the hour starting from the beginning of the current day - * @property int $hourOfDecade The value of the hour starting from the beginning of the current decade - * @property int $hourOfMillennium The value of the hour starting from the beginning of the current millennium - * @property int $hourOfMonth The value of the hour starting from the beginning of the current month - * @property int $hourOfQuarter The value of the hour starting from the beginning of the current quarter - * @property int $hourOfWeek The value of the hour starting from the beginning of the current week - * @property int $hourOfYear The value of the hour starting from the beginning of the current year - * @property int $microsecondOfCentury The value of the microsecond starting from the beginning of the current century - * @property int $microsecondOfDay The value of the microsecond starting from the beginning of the current day - * @property int $microsecondOfDecade The value of the microsecond starting from the beginning of the current decade - * @property int $microsecondOfHour The value of the microsecond starting from the beginning of the current hour - * @property int $microsecondOfMillennium The value of the microsecond starting from the beginning of the current millennium - * @property int $microsecondOfMillisecond The value of the microsecond starting from the beginning of the current millisecond - * @property int $microsecondOfMinute The value of the microsecond starting from the beginning of the current minute - * @property int $microsecondOfMonth The value of the microsecond starting from the beginning of the current month - * @property int $microsecondOfQuarter The value of the microsecond starting from the beginning of the current quarter - * @property int $microsecondOfSecond The value of the microsecond starting from the beginning of the current second - * @property int $microsecondOfWeek The value of the microsecond starting from the beginning of the current week - * @property int $microsecondOfYear The value of the microsecond starting from the beginning of the current year - * @property int $millisecondOfCentury The value of the millisecond starting from the beginning of the current century - * @property int $millisecondOfDay The value of the millisecond starting from the beginning of the current day - * @property int $millisecondOfDecade The value of the millisecond starting from the beginning of the current decade - * @property int $millisecondOfHour The value of the millisecond starting from the beginning of the current hour - * @property int $millisecondOfMillennium The value of the millisecond starting from the beginning of the current millennium - * @property int $millisecondOfMinute The value of the millisecond starting from the beginning of the current minute - * @property int $millisecondOfMonth The value of the millisecond starting from the beginning of the current month - * @property int $millisecondOfQuarter The value of the millisecond starting from the beginning of the current quarter - * @property int $millisecondOfSecond The value of the millisecond starting from the beginning of the current second - * @property int $millisecondOfWeek The value of the millisecond starting from the beginning of the current week - * @property int $millisecondOfYear The value of the millisecond starting from the beginning of the current year - * @property int $minuteOfCentury The value of the minute starting from the beginning of the current century - * @property int $minuteOfDay The value of the minute starting from the beginning of the current day - * @property int $minuteOfDecade The value of the minute starting from the beginning of the current decade - * @property int $minuteOfHour The value of the minute starting from the beginning of the current hour - * @property int $minuteOfMillennium The value of the minute starting from the beginning of the current millennium - * @property int $minuteOfMonth The value of the minute starting from the beginning of the current month - * @property int $minuteOfQuarter The value of the minute starting from the beginning of the current quarter - * @property int $minuteOfWeek The value of the minute starting from the beginning of the current week - * @property int $minuteOfYear The value of the minute starting from the beginning of the current year - * @property int $monthOfCentury The value of the month starting from the beginning of the current century - * @property int $monthOfDecade The value of the month starting from the beginning of the current decade - * @property int $monthOfMillennium The value of the month starting from the beginning of the current millennium - * @property int $monthOfQuarter The value of the month starting from the beginning of the current quarter - * @property int $monthOfYear The value of the month starting from the beginning of the current year - * @property int $quarterOfCentury The value of the quarter starting from the beginning of the current century - * @property int $quarterOfDecade The value of the quarter starting from the beginning of the current decade - * @property int $quarterOfMillennium The value of the quarter starting from the beginning of the current millennium - * @property int $quarterOfYear The value of the quarter starting from the beginning of the current year - * @property int $secondOfCentury The value of the second starting from the beginning of the current century - * @property int $secondOfDay The value of the second starting from the beginning of the current day - * @property int $secondOfDecade The value of the second starting from the beginning of the current decade - * @property int $secondOfHour The value of the second starting from the beginning of the current hour - * @property int $secondOfMillennium The value of the second starting from the beginning of the current millennium - * @property int $secondOfMinute The value of the second starting from the beginning of the current minute - * @property int $secondOfMonth The value of the second starting from the beginning of the current month - * @property int $secondOfQuarter The value of the second starting from the beginning of the current quarter - * @property int $secondOfWeek The value of the second starting from the beginning of the current week - * @property int $secondOfYear The value of the second starting from the beginning of the current year - * @property int $weekOfCentury The value of the week starting from the beginning of the current century - * @property int $weekOfDecade The value of the week starting from the beginning of the current decade - * @property int $weekOfMillennium The value of the week starting from the beginning of the current millennium - * @property int $weekOfMonth 1 through 5 - * @property int $weekOfQuarter The value of the week starting from the beginning of the current quarter - * @property int $weekOfYear ISO-8601 week number of year, weeks starting on Monday - * @property int $yearOfCentury The value of the year starting from the beginning of the current century - * @property int $yearOfDecade The value of the year starting from the beginning of the current decade - * @property int $yearOfMillennium The value of the year starting from the beginning of the current millennium - * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) - * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) - * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name - * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName - * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language - * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language - * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language - * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language - * @property-read int $noZeroHour current hour from 1 to 24 - * @property-read int $isoWeeksInYear 51 through 53 - * @property-read int $weekNumberInMonth 1 through 5 - * @property-read int $firstWeekDay 0 through 6 - * @property-read int $lastWeekDay 0 through 6 - * @property-read int $quarter the quarter of this instance, 1 - 4 - * @property-read int $decade the decade of this instance - * @property-read int $century the century of this instance - * @property-read int $millennium the millennium of this instance - * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise - * @property-read bool $local checks if the timezone is local, true if local, false otherwise - * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise - * @property-read string $timezoneName the current timezone name - * @property-read string $tzName alias of $timezoneName - * @property-read string $locale locale of the current instance - * @property-read int $centuriesInMillennium The number of centuries contained in the current millennium - * @property-read int $daysInCentury The number of days contained in the current century - * @property-read int $daysInDecade The number of days contained in the current decade - * @property-read int $daysInMillennium The number of days contained in the current millennium - * @property-read int $daysInMonth number of days in the given month - * @property-read int $daysInQuarter The number of days contained in the current quarter - * @property-read int $daysInWeek The number of days contained in the current week - * @property-read int $daysInYear 365 or 366 - * @property-read int $decadesInCentury The number of decades contained in the current century - * @property-read int $decadesInMillennium The number of decades contained in the current millennium - * @property-read int $hoursInCentury The number of hours contained in the current century - * @property-read int $hoursInDay The number of hours contained in the current day - * @property-read int $hoursInDecade The number of hours contained in the current decade - * @property-read int $hoursInMillennium The number of hours contained in the current millennium - * @property-read int $hoursInMonth The number of hours contained in the current month - * @property-read int $hoursInQuarter The number of hours contained in the current quarter - * @property-read int $hoursInWeek The number of hours contained in the current week - * @property-read int $hoursInYear The number of hours contained in the current year - * @property-read int $microsecondsInCentury The number of microseconds contained in the current century - * @property-read int $microsecondsInDay The number of microseconds contained in the current day - * @property-read int $microsecondsInDecade The number of microseconds contained in the current decade - * @property-read int $microsecondsInHour The number of microseconds contained in the current hour - * @property-read int $microsecondsInMillennium The number of microseconds contained in the current millennium - * @property-read int $microsecondsInMillisecond The number of microseconds contained in the current millisecond - * @property-read int $microsecondsInMinute The number of microseconds contained in the current minute - * @property-read int $microsecondsInMonth The number of microseconds contained in the current month - * @property-read int $microsecondsInQuarter The number of microseconds contained in the current quarter - * @property-read int $microsecondsInSecond The number of microseconds contained in the current second - * @property-read int $microsecondsInWeek The number of microseconds contained in the current week - * @property-read int $microsecondsInYear The number of microseconds contained in the current year - * @property-read int $millisecondsInCentury The number of milliseconds contained in the current century - * @property-read int $millisecondsInDay The number of milliseconds contained in the current day - * @property-read int $millisecondsInDecade The number of milliseconds contained in the current decade - * @property-read int $millisecondsInHour The number of milliseconds contained in the current hour - * @property-read int $millisecondsInMillennium The number of milliseconds contained in the current millennium - * @property-read int $millisecondsInMinute The number of milliseconds contained in the current minute - * @property-read int $millisecondsInMonth The number of milliseconds contained in the current month - * @property-read int $millisecondsInQuarter The number of milliseconds contained in the current quarter - * @property-read int $millisecondsInSecond The number of milliseconds contained in the current second - * @property-read int $millisecondsInWeek The number of milliseconds contained in the current week - * @property-read int $millisecondsInYear The number of milliseconds contained in the current year - * @property-read int $minutesInCentury The number of minutes contained in the current century - * @property-read int $minutesInDay The number of minutes contained in the current day - * @property-read int $minutesInDecade The number of minutes contained in the current decade - * @property-read int $minutesInHour The number of minutes contained in the current hour - * @property-read int $minutesInMillennium The number of minutes contained in the current millennium - * @property-read int $minutesInMonth The number of minutes contained in the current month - * @property-read int $minutesInQuarter The number of minutes contained in the current quarter - * @property-read int $minutesInWeek The number of minutes contained in the current week - * @property-read int $minutesInYear The number of minutes contained in the current year - * @property-read int $monthsInCentury The number of months contained in the current century - * @property-read int $monthsInDecade The number of months contained in the current decade - * @property-read int $monthsInMillennium The number of months contained in the current millennium - * @property-read int $monthsInQuarter The number of months contained in the current quarter - * @property-read int $monthsInYear The number of months contained in the current year - * @property-read int $quartersInCentury The number of quarters contained in the current century - * @property-read int $quartersInDecade The number of quarters contained in the current decade - * @property-read int $quartersInMillennium The number of quarters contained in the current millennium - * @property-read int $quartersInYear The number of quarters contained in the current year - * @property-read int $secondsInCentury The number of seconds contained in the current century - * @property-read int $secondsInDay The number of seconds contained in the current day - * @property-read int $secondsInDecade The number of seconds contained in the current decade - * @property-read int $secondsInHour The number of seconds contained in the current hour - * @property-read int $secondsInMillennium The number of seconds contained in the current millennium - * @property-read int $secondsInMinute The number of seconds contained in the current minute - * @property-read int $secondsInMonth The number of seconds contained in the current month - * @property-read int $secondsInQuarter The number of seconds contained in the current quarter - * @property-read int $secondsInWeek The number of seconds contained in the current week - * @property-read int $secondsInYear The number of seconds contained in the current year - * @property-read int $weeksInCentury The number of weeks contained in the current century - * @property-read int $weeksInDecade The number of weeks contained in the current decade - * @property-read int $weeksInMillennium The number of weeks contained in the current millennium - * @property-read int $weeksInMonth The number of weeks contained in the current month - * @property-read int $weeksInQuarter The number of weeks contained in the current quarter - * @property-read int $weeksInYear 51 through 53 - * @property-read int $yearsInCentury The number of years contained in the current century - * @property-read int $yearsInDecade The number of years contained in the current decade - * @property-read int $yearsInMillennium The number of years contained in the current millennium - * - * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) - * @method bool isLocal() Check if the current instance has non-UTC timezone. - * @method bool isValid() Check if the current instance is a valid date. - * @method bool isDST() Check if the current instance is in a daylight saving time. - * @method bool isSunday() Checks if the instance day is sunday. - * @method bool isMonday() Checks if the instance day is monday. - * @method bool isTuesday() Checks if the instance day is tuesday. - * @method bool isWednesday() Checks if the instance day is wednesday. - * @method bool isThursday() Checks if the instance day is thursday. - * @method bool isFriday() Checks if the instance day is friday. - * @method bool isSaturday() Checks if the instance day is saturday. - * @method bool isSameYear(DateTimeInterface|string $date) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. - * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. - * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. - * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. - * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. - * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. - * @method bool isSameWeek(DateTimeInterface|string $date) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. - * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. - * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. - * @method bool isSameDay(DateTimeInterface|string $date) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. - * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. - * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. - * @method bool isSameHour(DateTimeInterface|string $date) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. - * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. - * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. - * @method bool isSameMinute(DateTimeInterface|string $date) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. - * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. - * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. - * @method bool isSameSecond(DateTimeInterface|string $date) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. - * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. - * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. - * @method bool isSameMilli(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMilli() Checks if the instance is in the same millisecond as the current moment. - * @method bool isNextMilli() Checks if the instance is in the same millisecond as the current moment next millisecond. - * @method bool isLastMilli() Checks if the instance is in the same millisecond as the current moment last millisecond. - * @method bool isSameMillisecond(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMillisecond() Checks if the instance is in the same millisecond as the current moment. - * @method bool isNextMillisecond() Checks if the instance is in the same millisecond as the current moment next millisecond. - * @method bool isLastMillisecond() Checks if the instance is in the same millisecond as the current moment last millisecond. - * @method bool isSameMicro(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. - * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. - * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isSameMicrosecond(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. - * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. - * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isSameDecade(DateTimeInterface|string $date) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. - * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. - * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. - * @method bool isSameCentury(DateTimeInterface|string $date) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. - * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. - * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. - * @method bool isSameMillennium(DateTimeInterface|string $date) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. - * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. - * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. - * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. - * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. - * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. - * @method $this years(int $value) Set current instance year to the given value. - * @method $this year(int $value) Set current instance year to the given value. - * @method $this setYears(int $value) Set current instance year to the given value. - * @method $this setYear(int $value) Set current instance year to the given value. - * @method $this months(Month|int $value) Set current instance month to the given value. - * @method $this month(Month|int $value) Set current instance month to the given value. - * @method $this setMonths(Month|int $value) Set current instance month to the given value. - * @method $this setMonth(Month|int $value) Set current instance month to the given value. - * @method $this days(int $value) Set current instance day to the given value. - * @method $this day(int $value) Set current instance day to the given value. - * @method $this setDays(int $value) Set current instance day to the given value. - * @method $this setDay(int $value) Set current instance day to the given value. - * @method $this hours(int $value) Set current instance hour to the given value. - * @method $this hour(int $value) Set current instance hour to the given value. - * @method $this setHours(int $value) Set current instance hour to the given value. - * @method $this setHour(int $value) Set current instance hour to the given value. - * @method $this minutes(int $value) Set current instance minute to the given value. - * @method $this minute(int $value) Set current instance minute to the given value. - * @method $this setMinutes(int $value) Set current instance minute to the given value. - * @method $this setMinute(int $value) Set current instance minute to the given value. - * @method $this seconds(int $value) Set current instance second to the given value. - * @method $this second(int $value) Set current instance second to the given value. - * @method $this setSeconds(int $value) Set current instance second to the given value. - * @method $this setSecond(int $value) Set current instance second to the given value. - * @method $this millis(int $value) Set current instance millisecond to the given value. - * @method $this milli(int $value) Set current instance millisecond to the given value. - * @method $this setMillis(int $value) Set current instance millisecond to the given value. - * @method $this setMilli(int $value) Set current instance millisecond to the given value. - * @method $this milliseconds(int $value) Set current instance millisecond to the given value. - * @method $this millisecond(int $value) Set current instance millisecond to the given value. - * @method $this setMilliseconds(int $value) Set current instance millisecond to the given value. - * @method $this setMillisecond(int $value) Set current instance millisecond to the given value. - * @method $this micros(int $value) Set current instance microsecond to the given value. - * @method $this micro(int $value) Set current instance microsecond to the given value. - * @method $this setMicros(int $value) Set current instance microsecond to the given value. - * @method $this setMicro(int $value) Set current instance microsecond to the given value. - * @method $this microseconds(int $value) Set current instance microsecond to the given value. - * @method $this microsecond(int $value) Set current instance microsecond to the given value. - * @method $this setMicroseconds(int $value) Set current instance microsecond to the given value. - * @method $this setMicrosecond(int $value) Set current instance microsecond to the given value. - * @method $this addYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval). - * @method $this addYear() Add one year to the instance (using date interval). - * @method $this subYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval). - * @method $this subYear() Sub one year to the instance (using date interval). - * @method $this addYearsWithOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. - * @method $this subYearsWithOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. - * @method $this addYearsWithoutOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearsWithoutOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearsWithNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearsWithNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearsNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearsNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval). - * @method $this addMonth() Add one month to the instance (using date interval). - * @method $this subMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval). - * @method $this subMonth() Sub one month to the instance (using date interval). - * @method $this addMonthsWithOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. - * @method $this subMonthsWithOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. - * @method $this addMonthsWithoutOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthsWithoutOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthsWithNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthsWithNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthsNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthsNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using date interval). - * @method $this addDay() Add one day to the instance (using date interval). - * @method $this subDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using date interval). - * @method $this subDay() Sub one day to the instance (using date interval). - * @method $this addHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using date interval). - * @method $this addHour() Add one hour to the instance (using date interval). - * @method $this subHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). - * @method $this subHour() Sub one hour to the instance (using date interval). - * @method $this addMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). - * @method $this addMinute() Add one minute to the instance (using date interval). - * @method $this subMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). - * @method $this subMinute() Sub one minute to the instance (using date interval). - * @method $this addSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). - * @method $this addSecond() Add one second to the instance (using date interval). - * @method $this subSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). - * @method $this subSecond() Sub one second to the instance (using date interval). - * @method $this addMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). - * @method $this addMilli() Add one millisecond to the instance (using date interval). - * @method $this subMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). - * @method $this subMilli() Sub one millisecond to the instance (using date interval). - * @method $this addMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). - * @method $this addMillisecond() Add one millisecond to the instance (using date interval). - * @method $this subMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). - * @method $this subMillisecond() Sub one millisecond to the instance (using date interval). - * @method $this addMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). - * @method $this addMicro() Add one microsecond to the instance (using date interval). - * @method $this subMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). - * @method $this subMicro() Sub one microsecond to the instance (using date interval). - * @method $this addMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). - * @method $this addMicrosecond() Add one microsecond to the instance (using date interval). - * @method $this subMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). - * @method $this subMicrosecond() Sub one microsecond to the instance (using date interval). - * @method $this addMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). - * @method $this addMillennium() Add one millennium to the instance (using date interval). - * @method $this subMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). - * @method $this subMillennium() Sub one millennium to the instance (using date interval). - * @method $this addMillenniaWithOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method $this subMillenniaWithOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method $this addMillenniaWithoutOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniaWithoutOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniaWithNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniaWithNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniaNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniaNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). - * @method $this addCentury() Add one century to the instance (using date interval). - * @method $this subCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). - * @method $this subCentury() Sub one century to the instance (using date interval). - * @method $this addCenturiesWithOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. - * @method $this subCenturiesWithOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. - * @method $this addCenturiesWithoutOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturiesWithoutOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturiesWithNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturiesWithNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturiesNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturiesNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval). - * @method $this addDecade() Add one decade to the instance (using date interval). - * @method $this subDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). - * @method $this subDecade() Sub one decade to the instance (using date interval). - * @method $this addDecadesWithOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. - * @method $this subDecadesWithOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. - * @method $this addDecadesWithoutOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadesWithoutOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadesWithNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadesWithNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadesNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadesNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). - * @method $this addQuarter() Add one quarter to the instance (using date interval). - * @method $this subQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). - * @method $this subQuarter() Sub one quarter to the instance (using date interval). - * @method $this addQuartersWithOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method $this subQuartersWithOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method $this addQuartersWithoutOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuartersWithoutOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuartersWithNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuartersWithNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuartersNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuartersNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). - * @method $this addWeek() Add one week to the instance (using date interval). - * @method $this subWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). - * @method $this subWeek() Sub one week to the instance (using date interval). - * @method $this addWeekdays(int|float $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). - * @method $this addWeekday() Add one weekday to the instance (using date interval). - * @method $this subWeekdays(int|float $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). - * @method $this subWeekday() Sub one weekday to the instance (using date interval). - * @method $this addUTCMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCMicro() Add one microsecond to the instance (using timestamp). - * @method $this subUTCMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCMicro() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method float diffInUTCMicros(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. - * @method $this addUTCMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCMicrosecond() Add one microsecond to the instance (using timestamp). - * @method $this subUTCMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCMicrosecond() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method float diffInUTCMicroseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. - * @method $this addUTCMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCMilli() Add one millisecond to the instance (using timestamp). - * @method $this subUTCMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCMilli() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method float diffInUTCMillis(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. - * @method $this addUTCMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCMillisecond() Add one millisecond to the instance (using timestamp). - * @method $this subUTCMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCMillisecond() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method float diffInUTCMilliseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. - * @method $this addUTCSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCSecond() Add one second to the instance (using timestamp). - * @method $this subUTCSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCSecond() Sub one second to the instance (using timestamp). - * @method CarbonPeriod secondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. - * @method float diffInUTCSeconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of seconds. - * @method $this addUTCMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCMinute() Add one minute to the instance (using timestamp). - * @method $this subUTCMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCMinute() Sub one minute to the instance (using timestamp). - * @method CarbonPeriod minutesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. - * @method float diffInUTCMinutes(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of minutes. - * @method $this addUTCHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCHour() Add one hour to the instance (using timestamp). - * @method $this subUTCHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCHour() Sub one hour to the instance (using timestamp). - * @method CarbonPeriod hoursUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. - * @method float diffInUTCHours(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of hours. - * @method $this addUTCDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCDay() Add one day to the instance (using timestamp). - * @method $this subUTCDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCDay() Sub one day to the instance (using timestamp). - * @method CarbonPeriod daysUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. - * @method float diffInUTCDays(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of days. - * @method $this addUTCWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCWeek() Add one week to the instance (using timestamp). - * @method $this subUTCWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCWeek() Sub one week to the instance (using timestamp). - * @method CarbonPeriod weeksUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. - * @method float diffInUTCWeeks(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of weeks. - * @method $this addUTCMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCMonth() Add one month to the instance (using timestamp). - * @method $this subUTCMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCMonth() Sub one month to the instance (using timestamp). - * @method CarbonPeriod monthsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. - * @method float diffInUTCMonths(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of months. - * @method $this addUTCQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCQuarter() Add one quarter to the instance (using timestamp). - * @method $this subUTCQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCQuarter() Sub one quarter to the instance (using timestamp). - * @method CarbonPeriod quartersUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. - * @method float diffInUTCQuarters(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of quarters. - * @method $this addUTCYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCYear() Add one year to the instance (using timestamp). - * @method $this subUTCYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCYear() Sub one year to the instance (using timestamp). - * @method CarbonPeriod yearsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. - * @method float diffInUTCYears(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of years. - * @method $this addUTCDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCDecade() Add one decade to the instance (using timestamp). - * @method $this subUTCDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCDecade() Sub one decade to the instance (using timestamp). - * @method CarbonPeriod decadesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. - * @method float diffInUTCDecades(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of decades. - * @method $this addUTCCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCCentury() Add one century to the instance (using timestamp). - * @method $this subUTCCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCCentury() Sub one century to the instance (using timestamp). - * @method CarbonPeriod centuriesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. - * @method float diffInUTCCenturies(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of centuries. - * @method $this addUTCMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). - * @method $this addUTCMillennium() Add one millennium to the instance (using timestamp). - * @method $this subUTCMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). - * @method $this subUTCMillennium() Sub one millennium to the instance (using timestamp). - * @method CarbonPeriod millenniaUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. - * @method float diffInUTCMillennia(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of millennia. - * @method $this roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. - * @method $this roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. - * @method $this floorYear(float $precision = 1) Truncate the current instance year with given precision. - * @method $this floorYears(float $precision = 1) Truncate the current instance year with given precision. - * @method $this ceilYear(float $precision = 1) Ceil the current instance year with given precision. - * @method $this ceilYears(float $precision = 1) Ceil the current instance year with given precision. - * @method $this roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. - * @method $this roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. - * @method $this floorMonth(float $precision = 1) Truncate the current instance month with given precision. - * @method $this floorMonths(float $precision = 1) Truncate the current instance month with given precision. - * @method $this ceilMonth(float $precision = 1) Ceil the current instance month with given precision. - * @method $this ceilMonths(float $precision = 1) Ceil the current instance month with given precision. - * @method $this roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. - * @method $this roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. - * @method $this floorDay(float $precision = 1) Truncate the current instance day with given precision. - * @method $this floorDays(float $precision = 1) Truncate the current instance day with given precision. - * @method $this ceilDay(float $precision = 1) Ceil the current instance day with given precision. - * @method $this ceilDays(float $precision = 1) Ceil the current instance day with given precision. - * @method $this roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. - * @method $this roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. - * @method $this floorHour(float $precision = 1) Truncate the current instance hour with given precision. - * @method $this floorHours(float $precision = 1) Truncate the current instance hour with given precision. - * @method $this ceilHour(float $precision = 1) Ceil the current instance hour with given precision. - * @method $this ceilHours(float $precision = 1) Ceil the current instance hour with given precision. - * @method $this roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. - * @method $this roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. - * @method $this floorMinute(float $precision = 1) Truncate the current instance minute with given precision. - * @method $this floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. - * @method $this ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. - * @method $this ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. - * @method $this roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. - * @method $this roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. - * @method $this floorSecond(float $precision = 1) Truncate the current instance second with given precision. - * @method $this floorSeconds(float $precision = 1) Truncate the current instance second with given precision. - * @method $this ceilSecond(float $precision = 1) Ceil the current instance second with given precision. - * @method $this ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. - * @method $this roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. - * @method $this roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. - * @method $this floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. - * @method $this floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. - * @method $this ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. - * @method $this ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. - * @method $this roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. - * @method $this roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. - * @method $this floorCentury(float $precision = 1) Truncate the current instance century with given precision. - * @method $this floorCenturies(float $precision = 1) Truncate the current instance century with given precision. - * @method $this ceilCentury(float $precision = 1) Ceil the current instance century with given precision. - * @method $this ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. - * @method $this roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. - * @method $this roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. - * @method $this floorDecade(float $precision = 1) Truncate the current instance decade with given precision. - * @method $this floorDecades(float $precision = 1) Truncate the current instance decade with given precision. - * @method $this ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. - * @method $this ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. - * @method $this roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. - * @method $this roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. - * @method $this floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. - * @method $this floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. - * @method $this ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. - * @method $this ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. - * @method $this roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. - * @method $this roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. - * @method $this floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. - * @method $this floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. - * @method $this ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. - * @method $this ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. - * @method $this roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. - * @method $this roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. - * @method $this floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. - * @method $this floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. - * @method $this ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. - * @method $this ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. - * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method int centuriesInMillennium() Return the number of centuries contained in the current millennium - * @method int|static centuryOfMillennium(?int $century = null) Return the value of the century starting from the beginning of the current millennium when called with no parameters, change the current century when called with an integer value - * @method int|static dayOfCentury(?int $day = null) Return the value of the day starting from the beginning of the current century when called with no parameters, change the current day when called with an integer value - * @method int|static dayOfDecade(?int $day = null) Return the value of the day starting from the beginning of the current decade when called with no parameters, change the current day when called with an integer value - * @method int|static dayOfMillennium(?int $day = null) Return the value of the day starting from the beginning of the current millennium when called with no parameters, change the current day when called with an integer value - * @method int|static dayOfMonth(?int $day = null) Return the value of the day starting from the beginning of the current month when called with no parameters, change the current day when called with an integer value - * @method int|static dayOfQuarter(?int $day = null) Return the value of the day starting from the beginning of the current quarter when called with no parameters, change the current day when called with an integer value - * @method int|static dayOfWeek(?int $day = null) Return the value of the day starting from the beginning of the current week when called with no parameters, change the current day when called with an integer value - * @method int daysInCentury() Return the number of days contained in the current century - * @method int daysInDecade() Return the number of days contained in the current decade - * @method int daysInMillennium() Return the number of days contained in the current millennium - * @method int daysInMonth() Return the number of days contained in the current month - * @method int daysInQuarter() Return the number of days contained in the current quarter - * @method int daysInWeek() Return the number of days contained in the current week - * @method int daysInYear() Return the number of days contained in the current year - * @method int|static decadeOfCentury(?int $decade = null) Return the value of the decade starting from the beginning of the current century when called with no parameters, change the current decade when called with an integer value - * @method int|static decadeOfMillennium(?int $decade = null) Return the value of the decade starting from the beginning of the current millennium when called with no parameters, change the current decade when called with an integer value - * @method int decadesInCentury() Return the number of decades contained in the current century - * @method int decadesInMillennium() Return the number of decades contained in the current millennium - * @method int|static hourOfCentury(?int $hour = null) Return the value of the hour starting from the beginning of the current century when called with no parameters, change the current hour when called with an integer value - * @method int|static hourOfDay(?int $hour = null) Return the value of the hour starting from the beginning of the current day when called with no parameters, change the current hour when called with an integer value - * @method int|static hourOfDecade(?int $hour = null) Return the value of the hour starting from the beginning of the current decade when called with no parameters, change the current hour when called with an integer value - * @method int|static hourOfMillennium(?int $hour = null) Return the value of the hour starting from the beginning of the current millennium when called with no parameters, change the current hour when called with an integer value - * @method int|static hourOfMonth(?int $hour = null) Return the value of the hour starting from the beginning of the current month when called with no parameters, change the current hour when called with an integer value - * @method int|static hourOfQuarter(?int $hour = null) Return the value of the hour starting from the beginning of the current quarter when called with no parameters, change the current hour when called with an integer value - * @method int|static hourOfWeek(?int $hour = null) Return the value of the hour starting from the beginning of the current week when called with no parameters, change the current hour when called with an integer value - * @method int|static hourOfYear(?int $hour = null) Return the value of the hour starting from the beginning of the current year when called with no parameters, change the current hour when called with an integer value - * @method int hoursInCentury() Return the number of hours contained in the current century - * @method int hoursInDay() Return the number of hours contained in the current day - * @method int hoursInDecade() Return the number of hours contained in the current decade - * @method int hoursInMillennium() Return the number of hours contained in the current millennium - * @method int hoursInMonth() Return the number of hours contained in the current month - * @method int hoursInQuarter() Return the number of hours contained in the current quarter - * @method int hoursInWeek() Return the number of hours contained in the current week - * @method int hoursInYear() Return the number of hours contained in the current year - * @method int|static microsecondOfCentury(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current century when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfDay(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current day when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfDecade(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current decade when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfHour(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current hour when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfMillennium(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millennium when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfMillisecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millisecond when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfMinute(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current minute when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfMonth(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current month when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfQuarter(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current quarter when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfSecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current second when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfWeek(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current week when called with no parameters, change the current microsecond when called with an integer value - * @method int|static microsecondOfYear(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current year when called with no parameters, change the current microsecond when called with an integer value - * @method int microsecondsInCentury() Return the number of microseconds contained in the current century - * @method int microsecondsInDay() Return the number of microseconds contained in the current day - * @method int microsecondsInDecade() Return the number of microseconds contained in the current decade - * @method int microsecondsInHour() Return the number of microseconds contained in the current hour - * @method int microsecondsInMillennium() Return the number of microseconds contained in the current millennium - * @method int microsecondsInMillisecond() Return the number of microseconds contained in the current millisecond - * @method int microsecondsInMinute() Return the number of microseconds contained in the current minute - * @method int microsecondsInMonth() Return the number of microseconds contained in the current month - * @method int microsecondsInQuarter() Return the number of microseconds contained in the current quarter - * @method int microsecondsInSecond() Return the number of microseconds contained in the current second - * @method int microsecondsInWeek() Return the number of microseconds contained in the current week - * @method int microsecondsInYear() Return the number of microseconds contained in the current year - * @method int|static millisecondOfCentury(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current century when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfDay(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current day when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfDecade(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current decade when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfHour(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current hour when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfMillennium(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current millennium when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfMinute(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current minute when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfMonth(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current month when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfQuarter(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current quarter when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfSecond(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current second when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfWeek(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current week when called with no parameters, change the current millisecond when called with an integer value - * @method int|static millisecondOfYear(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current year when called with no parameters, change the current millisecond when called with an integer value - * @method int millisecondsInCentury() Return the number of milliseconds contained in the current century - * @method int millisecondsInDay() Return the number of milliseconds contained in the current day - * @method int millisecondsInDecade() Return the number of milliseconds contained in the current decade - * @method int millisecondsInHour() Return the number of milliseconds contained in the current hour - * @method int millisecondsInMillennium() Return the number of milliseconds contained in the current millennium - * @method int millisecondsInMinute() Return the number of milliseconds contained in the current minute - * @method int millisecondsInMonth() Return the number of milliseconds contained in the current month - * @method int millisecondsInQuarter() Return the number of milliseconds contained in the current quarter - * @method int millisecondsInSecond() Return the number of milliseconds contained in the current second - * @method int millisecondsInWeek() Return the number of milliseconds contained in the current week - * @method int millisecondsInYear() Return the number of milliseconds contained in the current year - * @method int|static minuteOfCentury(?int $minute = null) Return the value of the minute starting from the beginning of the current century when called with no parameters, change the current minute when called with an integer value - * @method int|static minuteOfDay(?int $minute = null) Return the value of the minute starting from the beginning of the current day when called with no parameters, change the current minute when called with an integer value - * @method int|static minuteOfDecade(?int $minute = null) Return the value of the minute starting from the beginning of the current decade when called with no parameters, change the current minute when called with an integer value - * @method int|static minuteOfHour(?int $minute = null) Return the value of the minute starting from the beginning of the current hour when called with no parameters, change the current minute when called with an integer value - * @method int|static minuteOfMillennium(?int $minute = null) Return the value of the minute starting from the beginning of the current millennium when called with no parameters, change the current minute when called with an integer value - * @method int|static minuteOfMonth(?int $minute = null) Return the value of the minute starting from the beginning of the current month when called with no parameters, change the current minute when called with an integer value - * @method int|static minuteOfQuarter(?int $minute = null) Return the value of the minute starting from the beginning of the current quarter when called with no parameters, change the current minute when called with an integer value - * @method int|static minuteOfWeek(?int $minute = null) Return the value of the minute starting from the beginning of the current week when called with no parameters, change the current minute when called with an integer value - * @method int|static minuteOfYear(?int $minute = null) Return the value of the minute starting from the beginning of the current year when called with no parameters, change the current minute when called with an integer value - * @method int minutesInCentury() Return the number of minutes contained in the current century - * @method int minutesInDay() Return the number of minutes contained in the current day - * @method int minutesInDecade() Return the number of minutes contained in the current decade - * @method int minutesInHour() Return the number of minutes contained in the current hour - * @method int minutesInMillennium() Return the number of minutes contained in the current millennium - * @method int minutesInMonth() Return the number of minutes contained in the current month - * @method int minutesInQuarter() Return the number of minutes contained in the current quarter - * @method int minutesInWeek() Return the number of minutes contained in the current week - * @method int minutesInYear() Return the number of minutes contained in the current year - * @method int|static monthOfCentury(?int $month = null) Return the value of the month starting from the beginning of the current century when called with no parameters, change the current month when called with an integer value - * @method int|static monthOfDecade(?int $month = null) Return the value of the month starting from the beginning of the current decade when called with no parameters, change the current month when called with an integer value - * @method int|static monthOfMillennium(?int $month = null) Return the value of the month starting from the beginning of the current millennium when called with no parameters, change the current month when called with an integer value - * @method int|static monthOfQuarter(?int $month = null) Return the value of the month starting from the beginning of the current quarter when called with no parameters, change the current month when called with an integer value - * @method int|static monthOfYear(?int $month = null) Return the value of the month starting from the beginning of the current year when called with no parameters, change the current month when called with an integer value - * @method int monthsInCentury() Return the number of months contained in the current century - * @method int monthsInDecade() Return the number of months contained in the current decade - * @method int monthsInMillennium() Return the number of months contained in the current millennium - * @method int monthsInQuarter() Return the number of months contained in the current quarter - * @method int monthsInYear() Return the number of months contained in the current year - * @method int|static quarterOfCentury(?int $quarter = null) Return the value of the quarter starting from the beginning of the current century when called with no parameters, change the current quarter when called with an integer value - * @method int|static quarterOfDecade(?int $quarter = null) Return the value of the quarter starting from the beginning of the current decade when called with no parameters, change the current quarter when called with an integer value - * @method int|static quarterOfMillennium(?int $quarter = null) Return the value of the quarter starting from the beginning of the current millennium when called with no parameters, change the current quarter when called with an integer value - * @method int|static quarterOfYear(?int $quarter = null) Return the value of the quarter starting from the beginning of the current year when called with no parameters, change the current quarter when called with an integer value - * @method int quartersInCentury() Return the number of quarters contained in the current century - * @method int quartersInDecade() Return the number of quarters contained in the current decade - * @method int quartersInMillennium() Return the number of quarters contained in the current millennium - * @method int quartersInYear() Return the number of quarters contained in the current year - * @method int|static secondOfCentury(?int $second = null) Return the value of the second starting from the beginning of the current century when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfDay(?int $second = null) Return the value of the second starting from the beginning of the current day when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfDecade(?int $second = null) Return the value of the second starting from the beginning of the current decade when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfHour(?int $second = null) Return the value of the second starting from the beginning of the current hour when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfMillennium(?int $second = null) Return the value of the second starting from the beginning of the current millennium when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfMinute(?int $second = null) Return the value of the second starting from the beginning of the current minute when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfMonth(?int $second = null) Return the value of the second starting from the beginning of the current month when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfQuarter(?int $second = null) Return the value of the second starting from the beginning of the current quarter when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfWeek(?int $second = null) Return the value of the second starting from the beginning of the current week when called with no parameters, change the current second when called with an integer value - * @method int|static secondOfYear(?int $second = null) Return the value of the second starting from the beginning of the current year when called with no parameters, change the current second when called with an integer value - * @method int secondsInCentury() Return the number of seconds contained in the current century - * @method int secondsInDay() Return the number of seconds contained in the current day - * @method int secondsInDecade() Return the number of seconds contained in the current decade - * @method int secondsInHour() Return the number of seconds contained in the current hour - * @method int secondsInMillennium() Return the number of seconds contained in the current millennium - * @method int secondsInMinute() Return the number of seconds contained in the current minute - * @method int secondsInMonth() Return the number of seconds contained in the current month - * @method int secondsInQuarter() Return the number of seconds contained in the current quarter - * @method int secondsInWeek() Return the number of seconds contained in the current week - * @method int secondsInYear() Return the number of seconds contained in the current year - * @method int|static weekOfCentury(?int $week = null) Return the value of the week starting from the beginning of the current century when called with no parameters, change the current week when called with an integer value - * @method int|static weekOfDecade(?int $week = null) Return the value of the week starting from the beginning of the current decade when called with no parameters, change the current week when called with an integer value - * @method int|static weekOfMillennium(?int $week = null) Return the value of the week starting from the beginning of the current millennium when called with no parameters, change the current week when called with an integer value - * @method int|static weekOfMonth(?int $week = null) Return the value of the week starting from the beginning of the current month when called with no parameters, change the current week when called with an integer value - * @method int|static weekOfQuarter(?int $week = null) Return the value of the week starting from the beginning of the current quarter when called with no parameters, change the current week when called with an integer value - * @method int|static weekOfYear(?int $week = null) Return the value of the week starting from the beginning of the current year when called with no parameters, change the current week when called with an integer value - * @method int weeksInCentury() Return the number of weeks contained in the current century - * @method int weeksInDecade() Return the number of weeks contained in the current decade - * @method int weeksInMillennium() Return the number of weeks contained in the current millennium - * @method int weeksInMonth() Return the number of weeks contained in the current month - * @method int weeksInQuarter() Return the number of weeks contained in the current quarter - * @method int|static yearOfCentury(?int $year = null) Return the value of the year starting from the beginning of the current century when called with no parameters, change the current year when called with an integer value - * @method int|static yearOfDecade(?int $year = null) Return the value of the year starting from the beginning of the current decade when called with no parameters, change the current year when called with an integer value - * @method int|static yearOfMillennium(?int $year = null) Return the value of the year starting from the beginning of the current millennium when called with no parameters, change the current year when called with an integer value - * @method int yearsInCentury() Return the number of years contained in the current century - * @method int yearsInDecade() Return the number of years contained in the current decade - * @method int yearsInMillennium() Return the number of years contained in the current millennium - * - * - */ -class Carbon extends DateTime -{ -} - diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ActiveRecord.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ActiveRecord.php.test deleted file mode 100644 index 0dbef61960..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ActiveRecord.php.test +++ /dev/null @@ -1,799 +0,0 @@ -name`. - * In this example, Active Record is providing an object-oriented interface for accessing data stored in the database. - * But Active Record provides much more functionality than this. - * - * To declare an ActiveRecord class you need to extend [[\yii\db\ActiveRecord]] and - * implement the `tableName` method: - * - * ```php - * Tip: You may also use the [Gii code generator](guide:start-gii) to generate ActiveRecord classes from your - * > database tables. - * - * Class instances are obtained in one of two ways: - * - * * Using the `new` operator to create a new, empty object - * * Using a method to fetch an existing record (or records) from the database - * - * Below is an example showing some typical usage of ActiveRecord: - * - * ```php - * $user = new User(); - * $user->name = 'Qiang'; - * $user->save(); // a new row is inserted into user table - * - * // the following will retrieve the user 'CeBe' from the database - * $user = User::find()->where(['name' => 'CeBe'])->one(); - * - * // this will get related records from orders table when relation is defined - * $orders = $user->orders; - * ``` - * - * For more details and usage information on ActiveRecord, see the [guide article on ActiveRecord](guide:db-active-record). - * - * @method ActiveQuery hasMany($class, array $link) see [[BaseActiveRecord::hasMany()]] for more info - * @method ActiveQuery hasOne($class, array $link) see [[BaseActiveRecord::hasOne()]] for more info - * - * @author Qiang Xue - * @author Carsten Brandt - * @since 2.0 - */ -class ActiveRecord extends BaseActiveRecord -{ - /** - * The insert operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional. - */ - const OP_INSERT = 0x01; - /** - * The update operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional. - */ - const OP_UPDATE = 0x02; - /** - * The delete operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional. - */ - const OP_DELETE = 0x04; - /** - * All three operations: insert, update, delete. - * This is a shortcut of the expression: OP_INSERT | OP_UPDATE | OP_DELETE. - */ - const OP_ALL = 0x07; - - - /** - * Loads default values from database table schema. - * - * You may call this method to load default values after creating a new instance: - * - * ```php - * // class Customer extends \yii\db\ActiveRecord - * $customer = new Customer(); - * $customer->loadDefaultValues(); - * ``` - * - * @param bool $skipIfSet whether existing value should be preserved. - * This will only set defaults for attributes that are `null`. - * @return $this the model instance itself. - */ - public function loadDefaultValues($skipIfSet = true) - { - foreach (static::getTableSchema()->columns as $column) { - if ($column->defaultValue !== null && (!$skipIfSet || $this->{$column->name} === null)) { - $this->{$column->name} = $column->defaultValue; - } - } - - return $this; - } - - /** - * Returns the database connection used by this AR class. - * By default, the "db" application component is used as the database connection. - * You may override this method if you want to use a different database connection. - * @return Connection the database connection used by this AR class. - */ - public static function getDb() - { - return Yii::$app->getDb(); - } - - /** - * Creates an [[ActiveQuery]] instance with a given SQL statement. - * - * Note that because the SQL statement is already specified, calling additional - * query modification methods (such as `where()`, `order()`) on the created [[ActiveQuery]] - * instance will have no effect. However, calling `with()`, `asArray()` or `indexBy()` is - * still fine. - * - * Below is an example: - * - * ```php - * $customers = Customer::findBySql('SELECT * FROM customer')->all(); - * ``` - * - * @param string $sql the SQL statement to be executed - * @param array $params parameters to be bound to the SQL statement during execution. - * @return ActiveQuery the newly created [[ActiveQuery]] instance - */ - public static function findBySql($sql, $params = []) - { - $query = static::find(); - $query->sql = $sql; - - return $query->params($params); - } - - /** - * Finds ActiveRecord instance(s) by the given condition. - * This method is internally called by [[findOne()]] and [[findAll()]]. - * @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter - * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance. - * @throws InvalidConfigException if there is no primary key defined. - * @internal - */ - protected static function findByCondition($condition) - { - $query = static::find(); - - if (!ArrayHelper::isAssociative($condition) && !$condition instanceof ExpressionInterface) { - // query by primary key - $primaryKey = static::primaryKey(); - if (isset($primaryKey[0])) { - $pk = $primaryKey[0]; - if (!empty($query->join) || !empty($query->joinWith)) { - $pk = static::tableName() . '.' . $pk; - } - // if condition is scalar, search for a single primary key, if it is array, search for multiple primary key values - $condition = [$pk => is_array($condition) ? array_values($condition) : $condition]; - } else { - throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.'); - } - } elseif (is_array($condition)) { - $aliases = static::filterValidAliases($query); - $condition = static::filterCondition($condition, $aliases); - } - - return $query->andWhere($condition); - } - - /** - * Returns table aliases which are not the same as the name of the tables. - * - * @param Query $query - * @return array - * @throws InvalidConfigException - * @since 2.0.17 - * @internal - */ - protected static function filterValidAliases(Query $query) - { - $tables = $query->getTablesUsedInFrom(); - - $aliases = array_diff(array_keys($tables), $tables); - - return array_map(function ($alias) { - return preg_replace('/{{([\w]+)}}/', '$1', $alias); - }, array_values($aliases)); - } - - /** - * Filters array condition before it is assiged to a Query filter. - * - * This method will ensure that an array condition only filters on existing table columns. - * - * @param array $condition condition to filter. - * @param array $aliases - * @return array filtered condition. - * @throws InvalidArgumentException in case array contains unsafe values. - * @throws InvalidConfigException - * @since 2.0.15 - * @internal - */ - protected static function filterCondition(array $condition, array $aliases = []) - { - $result = []; - $db = static::getDb(); - $columnNames = static::filterValidColumnNames($db, $aliases); - - foreach ($condition as $key => $value) { - if (is_string($key) && !in_array($db->quoteSql($key), $columnNames, true)) { - throw new InvalidArgumentException('Key "' . $key . '" is not a column name and can not be used as a filter'); - } - $result[$key] = is_array($value) ? array_values($value) : $value; - } - - return $result; - } - - /** - * Valid column names are table column names or column names prefixed with table name or table alias - * - * @param Connection $db - * @param array $aliases - * @return array - * @throws InvalidConfigException - * @since 2.0.17 - * @internal - */ - protected static function filterValidColumnNames($db, array $aliases) - { - $columnNames = []; - $tableName = static::tableName(); - $quotedTableName = $db->quoteTableName($tableName); - - foreach (static::getTableSchema()->getColumnNames() as $columnName) { - $columnNames[] = $columnName; - $columnNames[] = $db->quoteColumnName($columnName); - $columnNames[] = "$tableName.$columnName"; - $columnNames[] = $db->quoteSql("$quotedTableName.[[$columnName]]"); - foreach ($aliases as $tableAlias) { - $columnNames[] = "$tableAlias.$columnName"; - $quotedTableAlias = $db->quoteTableName($tableAlias); - $columnNames[] = $db->quoteSql("$quotedTableAlias.[[$columnName]]"); - } - } - - return $columnNames; - } - - /** - * {@inheritdoc} - */ - public function refresh() - { - $query = static::find(); - $tableName = key($query->getTablesUsedInFrom()); - $pk = []; - // disambiguate column names in case ActiveQuery adds a JOIN - foreach ($this->getPrimaryKey(true) as $key => $value) { - $pk[$tableName . '.' . $key] = $value; - } - $query->where($pk); - - /* @var $record BaseActiveRecord */ - $record = $query->one(); - return $this->refreshInternal($record); - } - - /** - * Updates the whole table using the provided attribute values and conditions. - * - * For example, to change the status to be 1 for all customers whose status is 2: - * - * ```php - * Customer::updateAll(['status' => 1], 'status = 2'); - * ``` - * - * > Warning: If you do not specify any condition, this method will update **all** rows in the table. - * - * Note that this method will not trigger any events. If you need [[EVENT_BEFORE_UPDATE]] or - * [[EVENT_AFTER_UPDATE]] to be triggered, you need to [[find()|find]] the models first and then - * call [[update()]] on each of them. For example an equivalent of the example above would be: - * - * ```php - * $models = Customer::find()->where('status = 2')->all(); - * foreach ($models as $model) { - * $model->status = 1; - * $model->update(false); // skipping validation as no user input is involved - * } - * ``` - * - * For a large set of models you might consider using [[ActiveQuery::each()]] to keep memory usage within limits. - * - * @param array $attributes attribute values (name-value pairs) to be saved into the table - * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL. - * Please refer to [[Query::where()]] on how to specify this parameter. - * @param array $params the parameters (name => value) to be bound to the query. - * @return int the number of rows updated - */ - public static function updateAll($attributes, $condition = '', $params = []) - { - $command = static::getDb()->createCommand(); - $command->update(static::tableName(), $attributes, $condition, $params); - - return $command->execute(); - } - - /** - * Updates the whole table using the provided counter changes and conditions. - * - * For example, to increment all customers' age by 1, - * - * ```php - * Customer::updateAllCounters(['age' => 1]); - * ``` - * - * Note that this method will not trigger any events. - * - * @param array $counters the counters to be updated (attribute name => increment value). - * Use negative values if you want to decrement the counters. - * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL. - * Please refer to [[Query::where()]] on how to specify this parameter. - * @param array $params the parameters (name => value) to be bound to the query. - * Do not name the parameters as `:bp0`, `:bp1`, etc., because they are used internally by this method. - * @return int the number of rows updated - */ - public static function updateAllCounters($counters, $condition = '', $params = []) - { - $n = 0; - foreach ($counters as $name => $value) { - $counters[$name] = new Expression("[[$name]]+:bp{$n}", [":bp{$n}" => $value]); - $n++; - } - $command = static::getDb()->createCommand(); - $command->update(static::tableName(), $counters, $condition, $params); - - return $command->execute(); - } - - /** - * Deletes rows in the table using the provided conditions. - * - * For example, to delete all customers whose status is 3: - * - * ```php - * Customer::deleteAll('status = 3'); - * ``` - * - * > Warning: If you do not specify any condition, this method will delete **all** rows in the table. - * - * Note that this method will not trigger any events. If you need [[EVENT_BEFORE_DELETE]] or - * [[EVENT_AFTER_DELETE]] to be triggered, you need to [[find()|find]] the models first and then - * call [[delete()]] on each of them. For example an equivalent of the example above would be: - * - * ```php - * $models = Customer::find()->where('status = 3')->all(); - * foreach ($models as $model) { - * $model->delete(); - * } - * ``` - * - * For a large set of models you might consider using [[ActiveQuery::each()]] to keep memory usage within limits. - * - * @param string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL. - * Please refer to [[Query::where()]] on how to specify this parameter. - * @param array $params the parameters (name => value) to be bound to the query. - * @return int the number of rows deleted - */ - public static function deleteAll($condition = null, $params = []) - { - $command = static::getDb()->createCommand(); - $command->delete(static::tableName(), $condition, $params); - - return $command->execute(); - } - - /** - * {@inheritdoc} - * @return ActiveQuery the newly created [[ActiveQuery]] instance. - */ - public static function find() - { - return Yii::createObject(ActiveQuery::className(), [get_called_class()]); - } - - /** - * Declares the name of the database table associated with this AR class. - * By default this method returns the class name as the table name by calling [[Inflector::camel2id()]] - * with prefix [[Connection::tablePrefix]]. For example if [[Connection::tablePrefix]] is `tbl_`, - * `Customer` becomes `tbl_customer`, and `OrderItem` becomes `tbl_order_item`. You may override this method - * if the table is not named after this convention. - * @return string the table name - */ - public static function tableName() - { - return '{{%' . Inflector::camel2id(StringHelper::basename(get_called_class()), '_') . '}}'; - } - - /** - * Returns the schema information of the DB table associated with this AR class. - * @return TableSchema the schema information of the DB table associated with this AR class. - * @throws InvalidConfigException if the table for the AR class does not exist. - */ - public static function getTableSchema() - { - $tableSchema = static::getDb() - ->getSchema() - ->getTableSchema(static::tableName()); - - if ($tableSchema === null) { - throw new InvalidConfigException('The table does not exist: ' . static::tableName()); - } - - return $tableSchema; - } - - /** - * Returns the primary key name(s) for this AR class. - * The default implementation will return the primary key(s) as declared - * in the DB table that is associated with this AR class. - * - * If the DB table does not declare any primary key, you should override - * this method to return the attributes that you want to use as primary keys - * for this AR class. - * - * Note that an array should be returned even for a table with single primary key. - * - * @return string[] the primary keys of the associated database table. - */ - public static function primaryKey() - { - return static::getTableSchema()->primaryKey; - } - - /** - * Returns the list of all attribute names of the model. - * The default implementation will return all column names of the table associated with this AR class. - * @return array list of attribute names. - */ - public function attributes() - { - return array_keys(static::getTableSchema()->columns); - } - - /** - * Declares which DB operations should be performed within a transaction in different scenarios. - * The supported DB operations are: [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]], - * which correspond to the [[insert()]], [[update()]] and [[delete()]] methods, respectively. - * By default, these methods are NOT enclosed in a DB transaction. - * - * In some scenarios, to ensure data consistency, you may want to enclose some or all of them - * in transactions. You can do so by overriding this method and returning the operations - * that need to be transactional. For example, - * - * ```php - * return [ - * 'admin' => self::OP_INSERT, - * 'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE, - * // the above is equivalent to the following: - * // 'api' => self::OP_ALL, - * - * ]; - * ``` - * - * The above declaration specifies that in the "admin" scenario, the insert operation ([[insert()]]) - * should be done in a transaction; and in the "api" scenario, all the operations should be done - * in a transaction. - * - * @return array the declarations of transactional operations. The array keys are scenarios names, - * and the array values are the corresponding transaction operations. - */ - public function transactions() - { - return []; - } - - /** - * {@inheritdoc} - */ - public static function populateRecord($record, $row) - { - $columns = static::getTableSchema()->columns; - foreach ($row as $name => $value) { - if (isset($columns[$name])) { - $row[$name] = $columns[$name]->phpTypecast($value); - } - } - parent::populateRecord($record, $row); - } - - /** - * Inserts a row into the associated database table using the attribute values of this record. - * - * This method performs the following steps in order: - * - * 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]] - * returns `false`, the rest of the steps will be skipped; - * 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation - * failed, the rest of the steps will be skipped; - * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`, - * the rest of the steps will be skipped; - * 4. insert the record into database. If this fails, it will skip the rest of the steps; - * 5. call [[afterSave()]]; - * - * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]], - * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_INSERT]], and [[EVENT_AFTER_INSERT]] - * will be raised by the corresponding methods. - * - * Only the [[dirtyAttributes|changed attribute values]] will be inserted into database. - * - * If the table's primary key is auto-incremental and is `null` during insertion, - * it will be populated with the actual value after insertion. - * - * For example, to insert a customer record: - * - * ```php - * $customer = new Customer; - * $customer->name = $name; - * $customer->email = $email; - * $customer->insert(); - * ``` - * - * @param bool $runValidation whether to perform validation (calling [[validate()]]) - * before saving the record. Defaults to `true`. If the validation fails, the record - * will not be saved to the database and this method will return `false`. - * @param array $attributes list of attributes that need to be saved. Defaults to `null`, - * meaning all attributes that are loaded from DB will be saved. - * @return bool whether the attributes are valid and the record is inserted successfully. - * @throws \Exception|\Throwable in case insert failed. - */ - public function insert($runValidation = true, $attributes = null) - { - if ($runValidation && !$this->validate($attributes)) { - Yii::info('Model not inserted due to validation error.', __METHOD__); - return false; - } - - if (!$this->isTransactional(self::OP_INSERT)) { - return $this->insertInternal($attributes); - } - - $transaction = static::getDb()->beginTransaction(); - try { - $result = $this->insertInternal($attributes); - if ($result === false) { - $transaction->rollBack(); - } else { - $transaction->commit(); - } - - return $result; - } catch (\Exception $e) { - $transaction->rollBack(); - throw $e; - } catch (\Throwable $e) { - $transaction->rollBack(); - throw $e; - } - } - - /** - * Inserts an ActiveRecord into DB without considering transaction. - * @param array $attributes list of attributes that need to be saved. Defaults to `null`, - * meaning all attributes that are loaded from DB will be saved. - * @return bool whether the record is inserted successfully. - */ - protected function insertInternal($attributes = null) - { - if (!$this->beforeSave(true)) { - return false; - } - $values = $this->getDirtyAttributes($attributes); - if (($primaryKeys = static::getDb()->schema->insert(static::tableName(), $values)) === false) { - return false; - } - foreach ($primaryKeys as $name => $value) { - $id = static::getTableSchema()->columns[$name]->phpTypecast($value); - $this->setAttribute($name, $id); - $values[$name] = $id; - } - - $changedAttributes = array_fill_keys(array_keys($values), null); - $this->setOldAttributes($values); - $this->afterSave(true, $changedAttributes); - - return true; - } - - /** - * Saves the changes to this active record into the associated database table. - * - * This method performs the following steps in order: - * - * 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]] - * returns `false`, the rest of the steps will be skipped; - * 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation - * failed, the rest of the steps will be skipped; - * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`, - * the rest of the steps will be skipped; - * 4. save the record into database. If this fails, it will skip the rest of the steps; - * 5. call [[afterSave()]]; - * - * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]], - * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_UPDATE]], and [[EVENT_AFTER_UPDATE]] - * will be raised by the corresponding methods. - * - * Only the [[dirtyAttributes|changed attribute values]] will be saved into database. - * - * For example, to update a customer record: - * - * ```php - * $customer = Customer::findOne($id); - * $customer->name = $name; - * $customer->email = $email; - * $customer->update(); - * ``` - * - * Note that it is possible the update does not affect any row in the table. - * In this case, this method will return 0. For this reason, you should use the following - * code to check if update() is successful or not: - * - * ```php - * if ($customer->update() !== false) { - * // update successful - * } else { - * // update failed - * } - * ``` - * - * @param bool $runValidation whether to perform validation (calling [[validate()]]) - * before saving the record. Defaults to `true`. If the validation fails, the record - * will not be saved to the database and this method will return `false`. - * @param array $attributeNames list of attributes that need to be saved. Defaults to `null`, - * meaning all attributes that are loaded from DB will be saved. - * @return int|false the number of rows affected, or false if validation fails - * or [[beforeSave()]] stops the updating process. - * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data - * being updated is outdated. - * @throws \Exception|\Throwable in case update failed. - */ - public function update($runValidation = true, $attributeNames = null) - { - if ($runValidation && !$this->validate($attributeNames)) { - Yii::info('Model not updated due to validation error.', __METHOD__); - return false; - } - - if (!$this->isTransactional(self::OP_UPDATE)) { - return $this->updateInternal($attributeNames); - } - - $transaction = static::getDb()->beginTransaction(); - try { - $result = $this->updateInternal($attributeNames); - if ($result === false) { - $transaction->rollBack(); - } else { - $transaction->commit(); - } - - return $result; - } catch (\Exception $e) { - $transaction->rollBack(); - throw $e; - } catch (\Throwable $e) { - $transaction->rollBack(); - throw $e; - } - } - - /** - * Deletes the table row corresponding to this active record. - * - * This method performs the following steps in order: - * - * 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the - * rest of the steps; - * 2. delete the record from the database; - * 3. call [[afterDelete()]]. - * - * In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]] - * will be raised by the corresponding methods. - * - * @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason. - * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful. - * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data - * being deleted is outdated. - * @throws \Exception|\Throwable in case delete failed. - */ - public function delete() - { - if (!$this->isTransactional(self::OP_DELETE)) { - return $this->deleteInternal(); - } - - $transaction = static::getDb()->beginTransaction(); - try { - $result = $this->deleteInternal(); - if ($result === false) { - $transaction->rollBack(); - } else { - $transaction->commit(); - } - - return $result; - } catch (\Exception $e) { - $transaction->rollBack(); - throw $e; - } catch (\Throwable $e) { - $transaction->rollBack(); - throw $e; - } - } - - /** - * Deletes an ActiveRecord without considering transaction. - * @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason. - * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful. - * @throws StaleObjectException - */ - protected function deleteInternal() - { - if (!$this->beforeDelete()) { - return false; - } - - // we do not check the return value of deleteAll() because it's possible - // the record is already deleted in the database and thus the method will return 0 - $condition = $this->getOldPrimaryKey(true); - $lock = $this->optimisticLock(); - if ($lock !== null) { - $condition[$lock] = $this->$lock; - } - $result = static::deleteAll($condition); - if ($lock !== null && !$result) { - throw new StaleObjectException('The object being deleted is outdated.'); - } - $this->setOldAttributes(null); - $this->afterDelete(); - - return $result; - } - - /** - * Returns a value indicating whether the given active record is the same as the current one. - * The comparison is made by comparing the table names and the primary key values of the two active records. - * If one of the records [[isNewRecord|is new]] they are also considered not equal. - * @param ActiveRecord $record record to compare to - * @return bool whether the two active records refer to the same row in the same database table. - */ - public function equals($record) - { - if ($this->isNewRecord || $record->isNewRecord) { - return false; - } - - return static::tableName() === $record->tableName() && $this->getPrimaryKey() === $record->getPrimaryKey(); - } - - /** - * Returns a value indicating whether the specified operation is transactional in the current [[$scenario]]. - * @param int $operation the operation to check. Possible values are [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]]. - * @return bool whether the specified operation is transactional in the current [[scenario]]. - */ - public function isTransactional($operation) - { - $scenario = $this->getScenario(); - $transactions = $this->transactions(); - - return isset($transactions[$scenario]) && ($transactions[$scenario] & $operation); - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ActiveRecordInterface.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ActiveRecordInterface.php.test deleted file mode 100644 index 846b40c1eb..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ActiveRecordInterface.php.test +++ /dev/null @@ -1,472 +0,0 @@ - - * @author Carsten Brandt - * @since 2.0 - */ -interface ActiveRecordInterface extends StaticInstanceInterface -{ - /** - * Returns the primary key **name(s)** for this AR class. - * - * Note that an array should be returned even when the record only has a single primary key. - * - * For the primary key **value** see [[getPrimaryKey()]] instead. - * - * @return string[] the primary key name(s) for this AR class. - */ - public static function primaryKey(); - - /** - * Returns the list of all attribute names of the record. - * @return array list of attribute names. - */ - public function attributes(); - - /** - * Returns the named attribute value. - * If this record is the result of a query and the attribute is not loaded, - * `null` will be returned. - * @param string $name the attribute name - * @return mixed the attribute value. `null` if the attribute is not set or does not exist. - * @see hasAttribute() - */ - public function getAttribute($name); - - /** - * Sets the named attribute value. - * @param string $name the attribute name. - * @param mixed $value the attribute value. - * @see hasAttribute() - */ - public function setAttribute($name, $value); - - /** - * Returns a value indicating whether the record has an attribute with the specified name. - * @param string $name the name of the attribute - * @return bool whether the record has an attribute with the specified name. - */ - public function hasAttribute($name); - - /** - * Returns the primary key value(s). - * @param bool $asArray whether to return the primary key value as an array. If true, - * the return value will be an array with attribute names as keys and attribute values as values. - * Note that for composite primary keys, an array will always be returned regardless of this parameter value. - * @return mixed the primary key value. An array (attribute name => attribute value) is returned if the primary key - * is composite or `$asArray` is true. A string is returned otherwise (`null` will be returned if - * the key value is `null`). - */ - public function getPrimaryKey($asArray = false); - - /** - * Returns the old primary key value(s). - * This refers to the primary key value that is populated into the record - * after executing a find method (e.g. find(), findOne()). - * The value remains unchanged even if the primary key attribute is manually assigned with a different value. - * @param bool $asArray whether to return the primary key value as an array. If true, - * the return value will be an array with column name as key and column value as value. - * If this is `false` (default), a scalar value will be returned for non-composite primary key. - * @property mixed The old primary key value. An array (column name => column value) is - * returned if the primary key is composite. A string is returned otherwise (`null` will be - * returned if the key value is `null`). - * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key - * is composite or `$asArray` is true. A string is returned otherwise (`null` will be returned if - * the key value is `null`). - */ - public function getOldPrimaryKey($asArray = false); - - /** - * Returns a value indicating whether the given set of attributes represents the primary key for this model. - * @param array $keys the set of attributes to check - * @return bool whether the given set of attributes represents the primary key for this model - */ - public static function isPrimaryKey($keys); - - /** - * Creates an [[ActiveQueryInterface]] instance for query purpose. - * - * The returned [[ActiveQueryInterface]] instance can be further customized by calling - * methods defined in [[ActiveQueryInterface]] before `one()` or `all()` is called to return - * populated ActiveRecord instances. For example, - * - * ```php - * // find the customer whose ID is 1 - * $customer = Customer::find()->where(['id' => 1])->one(); - * - * // find all active customers and order them by their age: - * $customers = Customer::find() - * ->where(['status' => 1]) - * ->orderBy('age') - * ->all(); - * ``` - * - * This method is also called by [[BaseActiveRecord::hasOne()]] and [[BaseActiveRecord::hasMany()]] to - * create a relational query. - * - * You may override this method to return a customized query. For example, - * - * ```php - * class Customer extends ActiveRecord - * { - * public static function find() - * { - * // use CustomerQuery instead of the default ActiveQuery - * return new CustomerQuery(get_called_class()); - * } - * } - * ``` - * - * The following code shows how to apply a default condition for all queries: - * - * ```php - * class Customer extends ActiveRecord - * { - * public static function find() - * { - * return parent::find()->where(['deleted' => false]); - * } - * } - * - * // Use andWhere()/orWhere() to apply the default condition - * // SELECT FROM customer WHERE `deleted`=:deleted AND age>30 - * $customers = Customer::find()->andWhere('age>30')->all(); - * - * // Use where() to ignore the default condition - * // SELECT FROM customer WHERE age>30 - * $customers = Customer::find()->where('age>30')->all(); - * - * @return ActiveQueryInterface the newly created [[ActiveQueryInterface]] instance. - */ - public static function find(); - - /** - * Returns a single active record model instance by a primary key or an array of column values. - * - * The method accepts: - * - * - a scalar value (integer or string): query by a single primary key value and return the - * corresponding record (or `null` if not found). - * - a non-associative array: query by a list of primary key values and return the - * first record (or `null` if not found). - * - an associative array of name-value pairs: query by a set of attribute values and return a single record - * matching all of them (or `null` if not found). Note that `['id' => 1, 2]` is treated as a non-associative array. - * Column names are limited to current records table columns for SQL DBMS, or filtered otherwise to be limited to simple filter conditions. - * - a yii\db\Expression: The expression will be used directly. (@since 2.0.37) - * - * That this method will automatically call the `one()` method and return an [[ActiveRecordInterface|ActiveRecord]] - * instance. - * - * > Note: As this is a short-hand method only, using more complex conditions, like ['!=', 'id', 1] will not work. - * > If you need to specify more complex conditions, use [[find()]] in combination with [[ActiveQuery::where()|where()]] instead. - * - * See the following code for usage examples: - * - * ```php - * // find a single customer whose primary key value is 10 - * $customer = Customer::findOne(10); - * - * // the above code is equivalent to: - * $customer = Customer::find()->where(['id' => 10])->one(); - * - * // find the customers whose primary key value is 10, 11 or 12. - * $customers = Customer::findOne([10, 11, 12]); - * - * // the above code is equivalent to: - * $customers = Customer::find()->where(['id' => [10, 11, 12]])->one(); - * - * // find the first customer whose age is 30 and whose status is 1 - * $customer = Customer::findOne(['age' => 30, 'status' => 1]); - * - * // the above code is equivalent to: - * $customer = Customer::find()->where(['age' => 30, 'status' => 1])->one(); - * ``` - * - * If you need to pass user input to this method, make sure the input value is scalar or in case of - * array condition, make sure the array structure can not be changed from the outside: - * - * ```php - * // yii\web\Controller ensures that $id is scalar - * public function actionView($id) - * { - * $model = Post::findOne($id); - * // ... - * } - * - * // explicitly specifying the colum to search, passing a scalar or array here will always result in finding a single record - * $model = Post::findOne(['id' => Yii::$app->request->get('id')]); - * - * // do NOT use the following code! it is possible to inject an array condition to filter by arbitrary column values! - * $model = Post::findOne(Yii::$app->request->get('id')); - * ``` - * - * @param mixed $condition primary key value or a set of column values - * @return static|null ActiveRecord instance matching the condition, or `null` if nothing matches. - */ - public static function findOne($condition); - - /** - * Returns a list of active record models that match the specified primary key value(s) or a set of column values. - * - * The method accepts: - * - * - a scalar value (integer or string): query by a single primary key value and return an array containing the - * corresponding record (or an empty array if not found). - * - a non-associative array: query by a list of primary key values and return the - * corresponding records (or an empty array if none was found). - * Note that an empty condition will result in an empty result as it will be interpreted as a search for - * primary keys and not an empty `WHERE` condition. - * - an associative array of name-value pairs: query by a set of attribute values and return an array of records - * matching all of them (or an empty array if none was found). Note that `['id' => 1, 2]` is treated as - * a non-associative array. - * Column names are limited to current records table columns for SQL DBMS, or filtered otherwise to be limted to simple filter conditions. - * - a yii\db\Expression: The expression will be used directly. (@since 2.0.37) - * - * This method will automatically call the `all()` method and return an array of [[ActiveRecordInterface|ActiveRecord]] - * instances. - * - * > Note: As this is a short-hand method only, using more complex conditions, like ['!=', 'id', 1] will not work. - * > If you need to specify more complex conditions, use [[find()]] in combination with [[ActiveQuery::where()|where()]] instead. - * - * See the following code for usage examples: - * - * ```php - * // find the customers whose primary key value is 10 - * $customers = Customer::findAll(10); - * - * // the above code is equivalent to: - * $customers = Customer::find()->where(['id' => 10])->all(); - * - * // find the customers whose primary key value is 10, 11 or 12. - * $customers = Customer::findAll([10, 11, 12]); - * - * // the above code is equivalent to: - * $customers = Customer::find()->where(['id' => [10, 11, 12]])->all(); - * - * // find customers whose age is 30 and whose status is 1 - * $customers = Customer::findAll(['age' => 30, 'status' => 1]); - * - * // the above code is equivalent to: - * $customers = Customer::find()->where(['age' => 30, 'status' => 1])->all(); - * ``` - * - * If you need to pass user input to this method, make sure the input value is scalar or in case of - * array condition, make sure the array structure can not be changed from the outside: - * - * ```php - * // yii\web\Controller ensures that $id is scalar - * public function actionView($id) - * { - * $model = Post::findOne($id); - * // ... - * } - * - * // explicitly specifying the colum to search, passing a scalar or array here will always result in finding a single record - * $model = Post::findOne(['id' => Yii::$app->request->get('id')]); - * - * // do NOT use the following code! it is possible to inject an array condition to filter by arbitrary column values! - * $model = Post::findOne(Yii::$app->request->get('id')); - * ``` - * - * @param mixed $condition primary key value or a set of column values - * @return array an array of ActiveRecord instance, or an empty array if nothing matches. - */ - public static function findAll($condition); - - /** - * Updates records using the provided attribute values and conditions. - * - * For example, to change the status to be 1 for all customers whose status is 2: - * - * ```php - * Customer::updateAll(['status' => 1], ['status' => '2']); - * ``` - * - * @param array $attributes attribute values (name-value pairs) to be saved for the record. - * Unlike [[update()]] these are not going to be validated. - * @param array $condition the condition that matches the records that should get updated. - * Please refer to [[QueryInterface::where()]] on how to specify this parameter. - * An empty condition will match all records. - * @return int the number of rows updated - */ - public static function updateAll($attributes, $condition = null); - - /** - * Deletes records using the provided conditions. - * WARNING: If you do not specify any condition, this method will delete ALL rows in the table. - * - * For example, to delete all customers whose status is 3: - * - * ```php - * Customer::deleteAll([status = 3]); - * ``` - * - * @param array $condition the condition that matches the records that should get deleted. - * Please refer to [[QueryInterface::where()]] on how to specify this parameter. - * An empty condition will match all records. - * @return int the number of rows deleted - */ - public static function deleteAll($condition = null); - - /** - * Saves the current record. - * - * This method will call [[insert()]] when [[getIsNewRecord()|isNewRecord]] is true, or [[update()]] - * when [[getIsNewRecord()|isNewRecord]] is false. - * - * For example, to save a customer record: - * - * ```php - * $customer = new Customer; // or $customer = Customer::findOne($id); - * $customer->name = $name; - * $customer->email = $email; - * $customer->save(); - * ``` - * - * @param bool $runValidation whether to perform validation (calling [[\yii\base\Model::validate()|validate()]]) - * before saving the record. Defaults to `true`. If the validation fails, the record - * will not be saved to the database and this method will return `false`. - * @param array $attributeNames list of attribute names that need to be saved. Defaults to `null`, - * meaning all attributes that are loaded from DB will be saved. - * @return bool whether the saving succeeded (i.e. no validation errors occurred). - */ - public function save($runValidation = true, $attributeNames = null); - - /** - * Inserts the record into the database using the attribute values of this record. - * - * Usage example: - * - * ```php - * $customer = new Customer; - * $customer->name = $name; - * $customer->email = $email; - * $customer->insert(); - * ``` - * - * @param bool $runValidation whether to perform validation (calling [[\yii\base\Model::validate()|validate()]]) - * before saving the record. Defaults to `true`. If the validation fails, the record - * will not be saved to the database and this method will return `false`. - * @param array $attributes list of attributes that need to be saved. Defaults to `null`, - * meaning all attributes that are loaded from DB will be saved. - * @return bool whether the attributes are valid and the record is inserted successfully. - */ - public function insert($runValidation = true, $attributes = null); - - /** - * Saves the changes to this active record into the database. - * - * Usage example: - * - * ```php - * $customer = Customer::findOne($id); - * $customer->name = $name; - * $customer->email = $email; - * $customer->update(); - * ``` - * - * @param bool $runValidation whether to perform validation (calling [[\yii\base\Model::validate()|validate()]]) - * before saving the record. Defaults to `true`. If the validation fails, the record - * will not be saved to the database and this method will return `false`. - * @param array $attributeNames list of attributes that need to be saved. Defaults to `null`, - * meaning all attributes that are loaded from DB will be saved. - * @return int|bool the number of rows affected, or `false` if validation fails - * or updating process is stopped for other reasons. - * Note that it is possible that the number of rows affected is 0, even though the - * update execution is successful. - */ - public function update($runValidation = true, $attributeNames = null); - - /** - * Deletes the record from the database. - * - * @return int|bool the number of rows deleted, or `false` if the deletion is unsuccessful for some reason. - * Note that it is possible that the number of rows deleted is 0, even though the deletion execution is successful. - */ - public function delete(); - - /** - * Returns a value indicating whether the current record is new (not saved in the database). - * @return bool whether the record is new and should be inserted when calling [[save()]]. - */ - public function getIsNewRecord(); - - /** - * Returns a value indicating whether the given active record is the same as the current one. - * Two [[getIsNewRecord()|new]] records are considered to be not equal. - * @param static $record record to compare to - * @return bool whether the two active records refer to the same row in the same database table. - */ - public function equals($record); - - /** - * Returns the relation object with the specified name. - * A relation is defined by a getter method which returns an object implementing the [[ActiveQueryInterface]] - * (normally this would be a relational [[ActiveQuery]] object). - * It can be declared in either the ActiveRecord class itself or one of its behaviors. - * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive). - * @param bool $throwException whether to throw exception if the relation does not exist. - * @return ActiveQueryInterface the relational query object - */ - public function getRelation($name, $throwException = true); - - /** - * Populates the named relation with the related records. - * Note that this method does not check if the relation exists or not. - * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive). - * @param ActiveRecordInterface|array|null $records the related records to be populated into the relation. - * @since 2.0.8 - */ - public function populateRelation($name, $records); - - /** - * Establishes the relationship between two records. - * - * The relationship is established by setting the foreign key value(s) in one record - * to be the corresponding primary key value(s) in the other record. - * The record with the foreign key will be saved into database without performing validation. - * - * If the relationship involves a junction table, a new row will be inserted into the - * junction table which contains the primary key values from both records. - * - * This method requires that the primary key value is not `null`. - * - * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method. - * @param static $model the record to be linked with the current one. - * @param array $extraColumns additional column values to be saved into the junction table. - * This parameter is only meaningful for a relationship involving a junction table - * (i.e., a relation set with [[ActiveQueryInterface::via()]]). - */ - public function link($name, $model, $extraColumns = []); - - /** - * Destroys the relationship between two records. - * - * The record with the foreign key of the relationship will be deleted if `$delete` is true. - * Otherwise, the foreign key will be set `null` and the record will be saved without validation. - * - * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method. - * @param static $model the model to be unlinked from the current one. - * @param bool $delete whether to delete the model that contains the foreign key. - * If false, the model's foreign key will be set `null` and saved. - * If true, the model containing the foreign key will be deleted. - */ - public function unlink($name, $model, $delete = false); - - /** - * Returns the connection used by this AR class. - * @return mixed the database connection used by this AR class. - */ - public static function getDb(); -} - diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Arrayable.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Arrayable.php.test deleted file mode 100644 index f2146f38bd..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Arrayable.php.test +++ /dev/null @@ -1,93 +0,0 @@ - - * @since 2.0 - */ -interface Arrayable -{ - /** - * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified. - * - * A field is a named element in the returned array by [[toArray()]]. - * - * This method should return an array of field names or field definitions. - * If the former, the field name will be treated as an object property name whose value will be used - * as the field value. If the latter, the array key should be the field name while the array value should be - * the corresponding field definition which can be either an object property name or a PHP callable - * returning the corresponding field value. The signature of the callable should be: - * - * ```php - * function ($model, $field) { - * // return field value - * } - * ``` - * - * For example, the following code declares four fields: - * - * - `email`: the field name is the same as the property name `email`; - * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their - * values are obtained from the `first_name` and `last_name` properties; - * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name` - * and `last_name`. - * - * ```php - * return [ - * 'email', - * 'firstName' => 'first_name', - * 'lastName' => 'last_name', - * 'fullName' => function ($model) { - * return $model->first_name . ' ' . $model->last_name; - * }, - * ]; - * ``` - * - * @return array the list of field names or field definitions. - * @see toArray() - */ - public function fields(); - - /** - * Returns the list of additional fields that can be returned by [[toArray()]] in addition to those listed in [[fields()]]. - * - * This method is similar to [[fields()]] except that the list of fields declared - * by this method are not returned by default by [[toArray()]]. Only when a field in the list - * is explicitly requested, will it be included in the result of [[toArray()]]. - * - * @return array the list of expandable field names or field definitions. Please refer - * to [[fields()]] on the format of the return value. - * @see toArray() - * @see fields() - */ - public function extraFields(); - - /** - * Converts the object into an array. - * - * @param array $fields the fields that the output array should contain. Fields not specified - * in [[fields()]] will be ignored. If this parameter is empty, all fields as specified in [[fields()]] will be returned. - * @param array $expand the additional fields that the output array should contain. - * Fields not specified in [[extraFields()]] will be ignored. If this parameter is empty, no extra fields - * will be returned. - * @param bool $recursive whether to recursively return array representation of embedded objects. - * @return array the array representation of the object - */ - public function toArray(array $fields = [], array $expand = [], $recursive = true); -} - diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ArrayableTrait.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ArrayableTrait.php.test deleted file mode 100644 index 93b7175b8b..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/ArrayableTrait.php.test +++ /dev/null @@ -1,247 +0,0 @@ - - * @since 2.0 - */ -trait ArrayableTrait -{ - /** - * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified. - * - * A field is a named element in the returned array by [[toArray()]]. - * - * This method should return an array of field names or field definitions. - * If the former, the field name will be treated as an object property name whose value will be used - * as the field value. If the latter, the array key should be the field name while the array value should be - * the corresponding field definition which can be either an object property name or a PHP callable - * returning the corresponding field value. The signature of the callable should be: - * - * ```php - * function ($model, $field) { - * // return field value - * } - * ``` - * - * For example, the following code declares four fields: - * - * - `email`: the field name is the same as the property name `email`; - * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their - * values are obtained from the `first_name` and `last_name` properties; - * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name` - * and `last_name`. - * - * ```php - * return [ - * 'email', - * 'firstName' => 'first_name', - * 'lastName' => 'last_name', - * 'fullName' => function () { - * return $this->first_name . ' ' . $this->last_name; - * }, - * ]; - * ``` - * - * In this method, you may also want to return different lists of fields based on some context - * information. For example, depending on the privilege of the current application user, - * you may return different sets of visible fields or filter out some fields. - * - * The default implementation of this method returns the public object member variables indexed by themselves. - * - * @return array the list of field names or field definitions. - * @see toArray() - */ - public function fields() - { - $fields = array_keys(Yii::getObjectVars($this)); - return array_combine($fields, $fields); - } - - /** - * Returns the list of fields that can be expanded further and returned by [[toArray()]]. - * - * This method is similar to [[fields()]] except that the list of fields returned - * by this method are not returned by default by [[toArray()]]. Only when field names - * to be expanded are explicitly specified when calling [[toArray()]], will their values - * be exported. - * - * The default implementation returns an empty array. - * - * You may override this method to return a list of expandable fields based on some context information - * (e.g. the current application user). - * - * @return array the list of expandable field names or field definitions. Please refer - * to [[fields()]] on the format of the return value. - * @see toArray() - * @see fields() - */ - public function extraFields() - { - return []; - } - - /** - * Converts the model into an array. - * - * This method will first identify which fields to be included in the resulting array by calling [[resolveFields()]]. - * It will then turn the model into an array with these fields. If `$recursive` is true, - * any embedded objects will also be converted into arrays. - * When embeded objects are [[Arrayable]], their respective nested fields will be extracted and passed to [[toArray()]]. - * - * If the model implements the [[Linkable]] interface, the resulting array will also have a `_link` element - * which refers to a list of links as specified by the interface. - * - * @param array $fields the fields being requested. - * If empty or if it contains '*', all fields as specified by [[fields()]] will be returned. - * Fields can be nested, separated with dots (.). e.g.: item.field.sub-field - * `$recursive` must be true for nested fields to be extracted. If `$recursive` is false, only the root fields will be extracted. - * @param array $expand the additional fields being requested for exporting. Only fields declared in [[extraFields()]] - * will be considered. - * Expand can also be nested, separated with dots (.). e.g.: item.expand1.expand2 - * `$recursive` must be true for nested expands to be extracted. If `$recursive` is false, only the root expands will be extracted. - * @param bool $recursive whether to recursively return array representation of embedded objects. - * @return array the array representation of the object - */ - public function toArray(array $fields = [], array $expand = [], $recursive = true) - { - $data = []; - foreach ($this->resolveFields($fields, $expand) as $field => $definition) { - $attribute = is_string($definition) ? $this->$definition : $definition($this, $field); - - if ($recursive) { - $nestedFields = $this->extractFieldsFor($fields, $field); - $nestedExpand = $this->extractFieldsFor($expand, $field); - if ($attribute instanceof JsonSerializable) { - $attribute = $attribute->jsonSerialize(); - } elseif ($attribute instanceof Arrayable) { - $attribute = $attribute->toArray($nestedFields, $nestedExpand); - } elseif (is_array($attribute)) { - $attribute = array_map( - function ($item) use ($nestedFields, $nestedExpand) { - if ($item instanceof JsonSerializable) { - return $item->jsonSerialize(); - } elseif ($item instanceof Arrayable) { - return $item->toArray($nestedFields, $nestedExpand); - } - return $item; - }, - $attribute - ); - } - } - $data[$field] = $attribute; - } - - if ($this instanceof Linkable) { - $data['_links'] = Link::serialize($this->getLinks()); - } - - return $recursive ? ArrayHelper::toArray($data) : $data; - } - - /** - * Extracts the root field names from nested fields. - * Nested fields are separated with dots (.). e.g: "item.id" - * The previous example would extract "item". - * - * @param array $fields The fields requested for extraction - * @return array root fields extracted from the given nested fields - * @since 2.0.14 - */ - protected function extractRootFields(array $fields) - { - $result = []; - - foreach ($fields as $field) { - $result[] = current(explode('.', $field, 2)); - } - - if (in_array('*', $result, true)) { - $result = []; - } - - return array_unique($result); - } - - /** - * Extract nested fields from a fields collection for a given root field - * Nested fields are separated with dots (.). e.g: "item.id" - * The previous example would extract "id". - * - * @param array $fields The fields requested for extraction - * @param string $rootField The root field for which we want to extract the nested fields - * @return array nested fields extracted for the given field - * @since 2.0.14 - */ - protected function extractFieldsFor(array $fields, $rootField) - { - $result = []; - - foreach ($fields as $field) { - if (0 === strpos($field, "{$rootField}.")) { - $result[] = preg_replace('/^' . preg_quote($rootField, '/') . '\./i', '', $field); - } - } - - return array_unique($result); - } - - /** - * Determines which fields can be returned by [[toArray()]]. - * This method will first extract the root fields from the given fields. - * Then it will check the requested root fields against those declared in [[fields()]] and [[extraFields()]] - * to determine which fields can be returned. - * @param array $fields the fields being requested for exporting - * @param array $expand the additional fields being requested for exporting - * @return array the list of fields to be exported. The array keys are the field names, and the array values - * are the corresponding object property names or PHP callables returning the field values. - */ - protected function resolveFields(array $fields, array $expand) - { - $fields = $this->extractRootFields($fields); - $expand = $this->extractRootFields($expand); - $result = []; - - foreach ($this->fields() as $field => $definition) { - if (is_int($field)) { - $field = $definition; - } - if (empty($fields) || in_array($field, $fields, true)) { - $result[$field] = $definition; - } - } - - if (empty($expand)) { - return $result; - } - - foreach ($this->extraFields() as $field => $definition) { - if (is_int($field)) { - $field = $definition; - } - if (in_array($field, $expand, true)) { - $result[$field] = $definition; - } - } - - return $result; - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/BaseActiveRecord.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/BaseActiveRecord.php.test deleted file mode 100644 index 536762be81..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/BaseActiveRecord.php.test +++ /dev/null @@ -1,1755 +0,0 @@ - column value) is - * returned if the primary key is composite. A string is returned otherwise (null will be returned if the key - * value is null). This property is read-only. - * @property mixed $primaryKey The primary key value. An array (column name => column value) is returned if - * the primary key is composite. A string is returned otherwise (null will be returned if the key value is null). - * This property is read-only. - * @property array $relatedRecords An array of related records indexed by relation names. This property is - * read-only. - * - * @author Qiang Xue - * @author Carsten Brandt - * @since 2.0 - */ -abstract class BaseActiveRecord extends Model implements ActiveRecordInterface -{ - /** - * @event Event an event that is triggered when the record is initialized via [[init()]]. - */ - const EVENT_INIT = 'init'; - /** - * @event Event an event that is triggered after the record is created and populated with query result. - */ - const EVENT_AFTER_FIND = 'afterFind'; - /** - * @event ModelEvent an event that is triggered before inserting a record. - * You may set [[ModelEvent::isValid]] to be `false` to stop the insertion. - */ - const EVENT_BEFORE_INSERT = 'beforeInsert'; - /** - * @event AfterSaveEvent an event that is triggered after a record is inserted. - */ - const EVENT_AFTER_INSERT = 'afterInsert'; - /** - * @event ModelEvent an event that is triggered before updating a record. - * You may set [[ModelEvent::isValid]] to be `false` to stop the update. - */ - const EVENT_BEFORE_UPDATE = 'beforeUpdate'; - /** - * @event AfterSaveEvent an event that is triggered after a record is updated. - */ - const EVENT_AFTER_UPDATE = 'afterUpdate'; - /** - * @event ModelEvent an event that is triggered before deleting a record. - * You may set [[ModelEvent::isValid]] to be `false` to stop the deletion. - */ - const EVENT_BEFORE_DELETE = 'beforeDelete'; - /** - * @event Event an event that is triggered after a record is deleted. - */ - const EVENT_AFTER_DELETE = 'afterDelete'; - /** - * @event Event an event that is triggered after a record is refreshed. - * @since 2.0.8 - */ - const EVENT_AFTER_REFRESH = 'afterRefresh'; - - /** - * @var array attribute values indexed by attribute names - */ - private $_attributes = []; - /** - * @var array|null old attribute values indexed by attribute names. - * This is `null` if the record [[isNewRecord|is new]]. - */ - private $_oldAttributes; - /** - * @var array related models indexed by the relation names - */ - private $_related = []; - /** - * @var array relation names indexed by their link attributes - */ - private $_relationsDependencies = []; - - - /** - * {@inheritdoc} - * @return static|null ActiveRecord instance matching the condition, or `null` if nothing matches. - */ - public static function findOne($condition) - { - return static::findByCondition($condition)->one(); - } - - /** - * {@inheritdoc} - * @return static[] an array of ActiveRecord instances, or an empty array if nothing matches. - */ - public static function findAll($condition) - { - return static::findByCondition($condition)->all(); - } - - /** - * Finds ActiveRecord instance(s) by the given condition. - * This method is internally called by [[findOne()]] and [[findAll()]]. - * @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter - * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance. - * @throws InvalidConfigException if there is no primary key defined - * @internal - */ - protected static function findByCondition($condition) - { - $query = static::find(); - - if (!ArrayHelper::isAssociative($condition) && !$condition instanceof ExpressionInterface) { - // query by primary key - $primaryKey = static::primaryKey(); - if (isset($primaryKey[0])) { - // if condition is scalar, search for a single primary key, if it is array, search for multiple primary key values - $condition = [$primaryKey[0] => is_array($condition) ? array_values($condition) : $condition]; - } else { - throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.'); - } - } - - return $query->andWhere($condition); - } - - /** - * Updates the whole table using the provided attribute values and conditions. - * - * For example, to change the status to be 1 for all customers whose status is 2: - * - * ```php - * Customer::updateAll(['status' => 1], 'status = 2'); - * ``` - * - * @param array $attributes attribute values (name-value pairs) to be saved into the table - * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL. - * Please refer to [[Query::where()]] on how to specify this parameter. - * @return int the number of rows updated - * @throws NotSupportedException if not overridden - */ - public static function updateAll($attributes, $condition = '') - { - throw new NotSupportedException(__METHOD__ . ' is not supported.'); - } - - /** - * Updates the whole table using the provided counter changes and conditions. - * - * For example, to increment all customers' age by 1, - * - * ```php - * Customer::updateAllCounters(['age' => 1]); - * ``` - * - * @param array $counters the counters to be updated (attribute name => increment value). - * Use negative values if you want to decrement the counters. - * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL. - * Please refer to [[Query::where()]] on how to specify this parameter. - * @return int the number of rows updated - * @throws NotSupportedException if not overrided - */ - public static function updateAllCounters($counters, $condition = '') - { - throw new NotSupportedException(__METHOD__ . ' is not supported.'); - } - - /** - * Deletes rows in the table using the provided conditions. - * WARNING: If you do not specify any condition, this method will delete ALL rows in the table. - * - * For example, to delete all customers whose status is 3: - * - * ```php - * Customer::deleteAll('status = 3'); - * ``` - * - * @param string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL. - * Please refer to [[Query::where()]] on how to specify this parameter. - * @return int the number of rows deleted - * @throws NotSupportedException if not overridden. - */ - public static function deleteAll($condition = null) - { - throw new NotSupportedException(__METHOD__ . ' is not supported.'); - } - - /** - * Returns the name of the column that stores the lock version for implementing optimistic locking. - * - * Optimistic locking allows multiple users to access the same record for edits and avoids - * potential conflicts. In case when a user attempts to save the record upon some staled data - * (because another user has modified the data), a [[StaleObjectException]] exception will be thrown, - * and the update or deletion is skipped. - * - * Optimistic locking is only supported by [[update()]] and [[delete()]]. - * - * To use Optimistic locking: - * - * 1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`. - * Override this method to return the name of this column. - * 2. Ensure the version value is submitted and loaded to your model before any update or delete. - * Or add [[\yii\behaviors\OptimisticLockBehavior|OptimisticLockBehavior]] to your model - * class in order to automate the process. - * 3. In the Web form that collects the user input, add a hidden field that stores - * the lock version of the recording being updated. - * 4. In the controller action that does the data updating, try to catch the [[StaleObjectException]] - * and implement necessary business logic (e.g. merging the changes, prompting stated data) - * to resolve the conflict. - * - * @return string the column name that stores the lock version of a table row. - * If `null` is returned (default implemented), optimistic locking will not be supported. - */ - public function optimisticLock() - { - return null; - } - - /** - * {@inheritdoc} - */ - public function canGetProperty($name, $checkVars = true, $checkBehaviors = true) - { - if (parent::canGetProperty($name, $checkVars, $checkBehaviors)) { - return true; - } - - try { - return $this->hasAttribute($name); - } catch (\Exception $e) { - // `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used - return false; - } - } - - /** - * {@inheritdoc} - */ - public function canSetProperty($name, $checkVars = true, $checkBehaviors = true) - { - if (parent::canSetProperty($name, $checkVars, $checkBehaviors)) { - return true; - } - - try { - return $this->hasAttribute($name); - } catch (\Exception $e) { - // `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used - return false; - } - } - - /** - * PHP getter magic method. - * This method is overridden so that attributes and related objects can be accessed like properties. - * - * @param string $name property name - * @throws InvalidArgumentException if relation name is wrong - * @return mixed property value - * @see getAttribute() - */ - public function __get($name) - { - if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) { - return $this->_attributes[$name]; - } - - if ($this->hasAttribute($name)) { - return null; - } - - if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) { - return $this->_related[$name]; - } - $value = parent::__get($name); - if ($value instanceof ActiveQueryInterface) { - $this->setRelationDependencies($name, $value); - return $this->_related[$name] = $value->findFor($name, $this); - } - - return $value; - } - - /** - * PHP setter magic method. - * This method is overridden so that AR attributes can be accessed like properties. - * @param string $name property name - * @param mixed $value property value - */ - public function __set($name, $value) - { - if ($this->hasAttribute($name)) { - if ( - !empty($this->_relationsDependencies[$name]) - && (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value) - ) { - $this->resetDependentRelations($name); - } - $this->_attributes[$name] = $value; - } else { - parent::__set($name, $value); - } - } - - /** - * Checks if a property value is null. - * This method overrides the parent implementation by checking if the named attribute is `null` or not. - * @param string $name the property name or the event name - * @return bool whether the property value is null - */ - public function __isset($name) - { - try { - return $this->__get($name) !== null; - } catch (\Throwable $t) { - return false; - } catch (\Exception $e) { - return false; - } - } - - /** - * Sets a component property to be null. - * This method overrides the parent implementation by clearing - * the specified attribute value. - * @param string $name the property name or the event name - */ - public function __unset($name) - { - if ($this->hasAttribute($name)) { - unset($this->_attributes[$name]); - if (!empty($this->_relationsDependencies[$name])) { - $this->resetDependentRelations($name); - } - } elseif (array_key_exists($name, $this->_related)) { - unset($this->_related[$name]); - } elseif ($this->getRelation($name, false) === null) { - parent::__unset($name); - } - } - - /** - * Declares a `has-one` relation. - * The declaration is returned in terms of a relational [[ActiveQuery]] instance - * through which the related record can be queried and retrieved back. - * - * A `has-one` relation means that there is at most one related record matching - * the criteria set by this relation, e.g., a customer has one country. - * - * For example, to declare the `country` relation for `Customer` class, we can write - * the following code in the `Customer` class: - * - * ```php - * public function getCountry() - * { - * return $this->hasOne(Country::className(), ['id' => 'country_id']); - * } - * ``` - * - * Note that in the above, the 'id' key in the `$link` parameter refers to an attribute name - * in the related class `Country`, while the 'country_id' value refers to an attribute name - * in the current AR class. - * - * Call methods declared in [[ActiveQuery]] to further customize the relation. - * - * @param string $class the class name of the related record - * @param array $link the primary-foreign key constraint. The keys of the array refer to - * the attributes of the record associated with the `$class` model, while the values of the - * array refer to the corresponding attributes in **this** AR class. - * @return ActiveQueryInterface the relational query object. - */ - public function hasOne($class, $link) - { - return $this->createRelationQuery($class, $link, false); - } - - /** - * Declares a `has-many` relation. - * The declaration is returned in terms of a relational [[ActiveQuery]] instance - * through which the related record can be queried and retrieved back. - * - * A `has-many` relation means that there are multiple related records matching - * the criteria set by this relation, e.g., a customer has many orders. - * - * For example, to declare the `orders` relation for `Customer` class, we can write - * the following code in the `Customer` class: - * - * ```php - * public function getOrders() - * { - * return $this->hasMany(Order::className(), ['customer_id' => 'id']); - * } - * ``` - * - * Note that in the above, the 'customer_id' key in the `$link` parameter refers to - * an attribute name in the related class `Order`, while the 'id' value refers to - * an attribute name in the current AR class. - * - * Call methods declared in [[ActiveQuery]] to further customize the relation. - * - * @param string $class the class name of the related record - * @param array $link the primary-foreign key constraint. The keys of the array refer to - * the attributes of the record associated with the `$class` model, while the values of the - * array refer to the corresponding attributes in **this** AR class. - * @return ActiveQueryInterface the relational query object. - */ - public function hasMany($class, $link) - { - return $this->createRelationQuery($class, $link, true); - } - - /** - * Creates a query instance for `has-one` or `has-many` relation. - * @param string $class the class name of the related record. - * @param array $link the primary-foreign key constraint. - * @param bool $multiple whether this query represents a relation to more than one record. - * @return ActiveQueryInterface the relational query object. - * @since 2.0.12 - * @see hasOne() - * @see hasMany() - */ - protected function createRelationQuery($class, $link, $multiple) - { - /* @var $class ActiveRecordInterface */ - /* @var $query ActiveQuery */ - $query = $class::find(); - $query->primaryModel = $this; - $query->link = $link; - $query->multiple = $multiple; - return $query; - } - - /** - * Populates the named relation with the related records. - * Note that this method does not check if the relation exists or not. - * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive). - * @param ActiveRecordInterface|array|null $records the related records to be populated into the relation. - * @see getRelation() - */ - public function populateRelation($name, $records) - { - foreach ($this->_relationsDependencies as &$relationNames) { - unset($relationNames[$name]); - } - - $this->_related[$name] = $records; - } - - /** - * Check whether the named relation has been populated with records. - * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive). - * @return bool whether relation has been populated with records. - * @see getRelation() - */ - public function isRelationPopulated($name) - { - return array_key_exists($name, $this->_related); - } - - /** - * Returns all populated related records. - * @return array an array of related records indexed by relation names. - * @see getRelation() - */ - public function getRelatedRecords() - { - return $this->_related; - } - - /** - * Returns a value indicating whether the model has an attribute with the specified name. - * @param string $name the name of the attribute - * @return bool whether the model has an attribute with the specified name. - */ - public function hasAttribute($name) - { - return isset($this->_attributes[$name]) || in_array($name, $this->attributes(), true); - } - - /** - * Returns the named attribute value. - * If this record is the result of a query and the attribute is not loaded, - * `null` will be returned. - * @param string $name the attribute name - * @return mixed the attribute value. `null` if the attribute is not set or does not exist. - * @see hasAttribute() - */ - public function getAttribute($name) - { - return isset($this->_attributes[$name]) ? $this->_attributes[$name] : null; - } - - /** - * Sets the named attribute value. - * @param string $name the attribute name - * @param mixed $value the attribute value. - * @throws InvalidArgumentException if the named attribute does not exist. - * @see hasAttribute() - */ - public function setAttribute($name, $value) - { - if ($this->hasAttribute($name)) { - if ( - !empty($this->_relationsDependencies[$name]) - && (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value) - ) { - $this->resetDependentRelations($name); - } - $this->_attributes[$name] = $value; - } else { - throw new InvalidArgumentException(get_class($this) . ' has no attribute named "' . $name . '".'); - } - } - - /** - * Returns the old attribute values. - * @return array the old attribute values (name-value pairs) - */ - public function getOldAttributes() - { - return $this->_oldAttributes === null ? [] : $this->_oldAttributes; - } - - /** - * Sets the old attribute values. - * All existing old attribute values will be discarded. - * @param array|null $values old attribute values to be set. - * If set to `null` this record is considered to be [[isNewRecord|new]]. - */ - public function setOldAttributes($values) - { - $this->_oldAttributes = $values; - } - - /** - * Returns the old value of the named attribute. - * If this record is the result of a query and the attribute is not loaded, - * `null` will be returned. - * @param string $name the attribute name - * @return mixed the old attribute value. `null` if the attribute is not loaded before - * or does not exist. - * @see hasAttribute() - */ - public function getOldAttribute($name) - { - return isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null; - } - - /** - * Sets the old value of the named attribute. - * @param string $name the attribute name - * @param mixed $value the old attribute value. - * @throws InvalidArgumentException if the named attribute does not exist. - * @see hasAttribute() - */ - public function setOldAttribute($name, $value) - { - if (isset($this->_oldAttributes[$name]) || $this->hasAttribute($name)) { - $this->_oldAttributes[$name] = $value; - } else { - throw new InvalidArgumentException(get_class($this) . ' has no attribute named "' . $name . '".'); - } - } - - /** - * Marks an attribute dirty. - * This method may be called to force updating a record when calling [[update()]], - * even if there is no change being made to the record. - * @param string $name the attribute name - */ - public function markAttributeDirty($name) - { - unset($this->_oldAttributes[$name]); - } - - /** - * Returns a value indicating whether the named attribute has been changed. - * @param string $name the name of the attribute. - * @param bool $identical whether the comparison of new and old value is made for - * identical values using `===`, defaults to `true`. Otherwise `==` is used for comparison. - * This parameter is available since version 2.0.4. - * @return bool whether the attribute has been changed - */ - public function isAttributeChanged($name, $identical = true) - { - if (isset($this->_attributes[$name], $this->_oldAttributes[$name])) { - if ($identical) { - return $this->_attributes[$name] !== $this->_oldAttributes[$name]; - } - - return $this->_attributes[$name] != $this->_oldAttributes[$name]; - } - - return isset($this->_attributes[$name]) || isset($this->_oldAttributes[$name]); - } - - /** - * Returns the attribute values that have been modified since they are loaded or saved most recently. - * - * The comparison of new and old values is made for identical values using `===`. - * - * @param string[]|null $names the names of the attributes whose values may be returned if they are - * changed recently. If null, [[attributes()]] will be used. - * @return array the changed attribute values (name-value pairs) - */ - public function getDirtyAttributes($names = null) - { - if ($names === null) { - $names = $this->attributes(); - } - $names = array_flip($names); - $attributes = []; - if ($this->_oldAttributes === null) { - foreach ($this->_attributes as $name => $value) { - if (isset($names[$name])) { - $attributes[$name] = $value; - } - } - } else { - foreach ($this->_attributes as $name => $value) { - if (isset($names[$name]) && (!array_key_exists($name, $this->_oldAttributes) || $value !== $this->_oldAttributes[$name])) { - $attributes[$name] = $value; - } - } - } - - return $attributes; - } - - /** - * Saves the current record. - * - * This method will call [[insert()]] when [[isNewRecord]] is `true`, or [[update()]] - * when [[isNewRecord]] is `false`. - * - * For example, to save a customer record: - * - * ```php - * $customer = new Customer; // or $customer = Customer::findOne($id); - * $customer->name = $name; - * $customer->email = $email; - * $customer->save(); - * ``` - * - * @param bool $runValidation whether to perform validation (calling [[validate()]]) - * before saving the record. Defaults to `true`. If the validation fails, the record - * will not be saved to the database and this method will return `false`. - * @param array $attributeNames list of attribute names that need to be saved. Defaults to null, - * meaning all attributes that are loaded from DB will be saved. - * @return bool whether the saving succeeded (i.e. no validation errors occurred). - */ - public function save($runValidation = true, $attributeNames = null) - { - if ($this->getIsNewRecord()) { - return $this->insert($runValidation, $attributeNames); - } - - return $this->update($runValidation, $attributeNames) !== false; - } - - /** - * Saves the changes to this active record into the associated database table. - * - * This method performs the following steps in order: - * - * 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]] - * returns `false`, the rest of the steps will be skipped; - * 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation - * failed, the rest of the steps will be skipped; - * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`, - * the rest of the steps will be skipped; - * 4. save the record into database. If this fails, it will skip the rest of the steps; - * 5. call [[afterSave()]]; - * - * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]], - * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_UPDATE]], and [[EVENT_AFTER_UPDATE]] - * will be raised by the corresponding methods. - * - * Only the [[dirtyAttributes|changed attribute values]] will be saved into database. - * - * For example, to update a customer record: - * - * ```php - * $customer = Customer::findOne($id); - * $customer->name = $name; - * $customer->email = $email; - * $customer->update(); - * ``` - * - * Note that it is possible the update does not affect any row in the table. - * In this case, this method will return 0. For this reason, you should use the following - * code to check if update() is successful or not: - * - * ```php - * if ($customer->update() !== false) { - * // update successful - * } else { - * // update failed - * } - * ``` - * - * @param bool $runValidation whether to perform validation (calling [[validate()]]) - * before saving the record. Defaults to `true`. If the validation fails, the record - * will not be saved to the database and this method will return `false`. - * @param array $attributeNames list of attribute names that need to be saved. Defaults to null, - * meaning all attributes that are loaded from DB will be saved. - * @return int|false the number of rows affected, or `false` if validation fails - * or [[beforeSave()]] stops the updating process. - * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data - * being updated is outdated. - * @throws Exception in case update failed. - */ - public function update($runValidation = true, $attributeNames = null) - { - if ($runValidation && !$this->validate($attributeNames)) { - return false; - } - - return $this->updateInternal($attributeNames); - } - - /** - * Updates the specified attributes. - * - * This method is a shortcut to [[update()]] when data validation is not needed - * and only a small set attributes need to be updated. - * - * You may specify the attributes to be updated as name list or name-value pairs. - * If the latter, the corresponding attribute values will be modified accordingly. - * The method will then save the specified attributes into database. - * - * Note that this method will **not** perform data validation and will **not** trigger events. - * - * @param array $attributes the attributes (names or name-value pairs) to be updated - * @return int the number of rows affected. - */ - public function updateAttributes($attributes) - { - $attrs = []; - foreach ($attributes as $name => $value) { - if (is_int($name)) { - $attrs[] = $value; - } else { - $this->$name = $value; - $attrs[] = $name; - } - } - - $values = $this->getDirtyAttributes($attrs); - if (empty($values) || $this->getIsNewRecord()) { - return 0; - } - - $rows = static::updateAll($values, $this->getOldPrimaryKey(true)); - - foreach ($values as $name => $value) { - $this->_oldAttributes[$name] = $this->_attributes[$name]; - } - - return $rows; - } - - /** - * @see update() - * @param array $attributes attributes to update - * @return int|false the number of rows affected, or false if [[beforeSave()]] stops the updating process. - * @throws StaleObjectException - */ - protected function updateInternal($attributes = null) - { - if (!$this->beforeSave(false)) { - return false; - } - $values = $this->getDirtyAttributes($attributes); - if (empty($values)) { - $this->afterSave(false, $values); - return 0; - } - $condition = $this->getOldPrimaryKey(true); - $lock = $this->optimisticLock(); - if ($lock !== null) { - $values[$lock] = $this->$lock + 1; - $condition[$lock] = $this->$lock; - } - // We do not check the return value of updateAll() because it's possible - // that the UPDATE statement doesn't change anything and thus returns 0. - $rows = static::updateAll($values, $condition); - - if ($lock !== null && !$rows) { - throw new StaleObjectException('The object being updated is outdated.'); - } - - if (isset($values[$lock])) { - $this->$lock = $values[$lock]; - } - - $changedAttributes = []; - foreach ($values as $name => $value) { - $changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null; - $this->_oldAttributes[$name] = $value; - } - $this->afterSave(false, $changedAttributes); - - return $rows; - } - - /** - * Updates one or several counter columns for the current AR object. - * Note that this method differs from [[updateAllCounters()]] in that it only - * saves counters for the current AR object. - * - * An example usage is as follows: - * - * ```php - * $post = Post::findOne($id); - * $post->updateCounters(['view_count' => 1]); - * ``` - * - * @param array $counters the counters to be updated (attribute name => increment value) - * Use negative values if you want to decrement the counters. - * @return bool whether the saving is successful - * @see updateAllCounters() - */ - public function updateCounters($counters) - { - if (static::updateAllCounters($counters, $this->getOldPrimaryKey(true)) > 0) { - foreach ($counters as $name => $value) { - if (!isset($this->_attributes[$name])) { - $this->_attributes[$name] = $value; - } else { - $this->_attributes[$name] += $value; - } - $this->_oldAttributes[$name] = $this->_attributes[$name]; - } - - return true; - } - - return false; - } - - /** - * Deletes the table row corresponding to this active record. - * - * This method performs the following steps in order: - * - * 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the - * rest of the steps; - * 2. delete the record from the database; - * 3. call [[afterDelete()]]. - * - * In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]] - * will be raised by the corresponding methods. - * - * @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason. - * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful. - * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data - * being deleted is outdated. - * @throws Exception in case delete failed. - */ - public function delete() - { - $result = false; - if ($this->beforeDelete()) { - // we do not check the return value of deleteAll() because it's possible - // the record is already deleted in the database and thus the method will return 0 - $condition = $this->getOldPrimaryKey(true); - $lock = $this->optimisticLock(); - if ($lock !== null) { - $condition[$lock] = $this->$lock; - } - $result = static::deleteAll($condition); - if ($lock !== null && !$result) { - throw new StaleObjectException('The object being deleted is outdated.'); - } - $this->_oldAttributes = null; - $this->afterDelete(); - } - - return $result; - } - - /** - * Returns a value indicating whether the current record is new. - * @return bool whether the record is new and should be inserted when calling [[save()]]. - */ - public function getIsNewRecord() - { - return $this->_oldAttributes === null; - } - - /** - * Sets the value indicating whether the record is new. - * @param bool $value whether the record is new and should be inserted when calling [[save()]]. - * @see getIsNewRecord() - */ - public function setIsNewRecord($value) - { - $this->_oldAttributes = $value ? null : $this->_attributes; - } - - /** - * Initializes the object. - * This method is called at the end of the constructor. - * The default implementation will trigger an [[EVENT_INIT]] event. - */ - public function init() - { - parent::init(); - $this->trigger(self::EVENT_INIT); - } - - /** - * This method is called when the AR object is created and populated with the query result. - * The default implementation will trigger an [[EVENT_AFTER_FIND]] event. - * When overriding this method, make sure you call the parent implementation to ensure the - * event is triggered. - */ - public function afterFind() - { - $this->trigger(self::EVENT_AFTER_FIND); - } - - /** - * This method is called at the beginning of inserting or updating a record. - * - * The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is `true`, - * or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is `false`. - * When overriding this method, make sure you call the parent implementation like the following: - * - * ```php - * public function beforeSave($insert) - * { - * if (!parent::beforeSave($insert)) { - * return false; - * } - * - * // ...custom code here... - * return true; - * } - * ``` - * - * @param bool $insert whether this method called while inserting a record. - * If `false`, it means the method is called while updating a record. - * @return bool whether the insertion or updating should continue. - * If `false`, the insertion or updating will be cancelled. - */ - public function beforeSave($insert) - { - $event = new ModelEvent(); - $this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event); - - return $event->isValid; - } - - /** - * This method is called at the end of inserting or updating a record. - * The default implementation will trigger an [[EVENT_AFTER_INSERT]] event when `$insert` is `true`, - * or an [[EVENT_AFTER_UPDATE]] event if `$insert` is `false`. The event class used is [[AfterSaveEvent]]. - * When overriding this method, make sure you call the parent implementation so that - * the event is triggered. - * @param bool $insert whether this method called while inserting a record. - * If `false`, it means the method is called while updating a record. - * @param array $changedAttributes The old values of attributes that had changed and were saved. - * You can use this parameter to take action based on the changes made for example send an email - * when the password had changed or implement audit trail that tracks all the changes. - * `$changedAttributes` gives you the old attribute values while the active record (`$this`) has - * already the new, updated values. - * - * Note that no automatic type conversion performed by default. You may use - * [[\yii\behaviors\AttributeTypecastBehavior]] to facilitate attribute typecasting. - * See http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#attributes-typecasting. - */ - public function afterSave($insert, $changedAttributes) - { - $this->trigger($insert ? self::EVENT_AFTER_INSERT : self::EVENT_AFTER_UPDATE, new AfterSaveEvent([ - 'changedAttributes' => $changedAttributes, - ])); - } - - /** - * This method is invoked before deleting a record. - * - * The default implementation raises the [[EVENT_BEFORE_DELETE]] event. - * When overriding this method, make sure you call the parent implementation like the following: - * - * ```php - * public function beforeDelete() - * { - * if (!parent::beforeDelete()) { - * return false; - * } - * - * // ...custom code here... - * return true; - * } - * ``` - * - * @return bool whether the record should be deleted. Defaults to `true`. - */ - public function beforeDelete() - { - $event = new ModelEvent(); - $this->trigger(self::EVENT_BEFORE_DELETE, $event); - - return $event->isValid; - } - - /** - * This method is invoked after deleting a record. - * The default implementation raises the [[EVENT_AFTER_DELETE]] event. - * You may override this method to do postprocessing after the record is deleted. - * Make sure you call the parent implementation so that the event is raised properly. - */ - public function afterDelete() - { - $this->trigger(self::EVENT_AFTER_DELETE); - } - - /** - * Repopulates this active record with the latest data. - * - * If the refresh is successful, an [[EVENT_AFTER_REFRESH]] event will be triggered. - * This event is available since version 2.0.8. - * - * @return bool whether the row still exists in the database. If `true`, the latest data - * will be populated to this active record. Otherwise, this record will remain unchanged. - */ - public function refresh() - { - /* @var $record BaseActiveRecord */ - $record = static::findOne($this->getPrimaryKey(true)); - return $this->refreshInternal($record); - } - - /** - * Repopulates this active record with the latest data from a newly fetched instance. - * @param BaseActiveRecord $record the record to take attributes from. - * @return bool whether refresh was successful. - * @see refresh() - * @since 2.0.13 - */ - protected function refreshInternal($record) - { - if ($record === null) { - return false; - } - foreach ($this->attributes() as $name) { - $this->_attributes[$name] = isset($record->_attributes[$name]) ? $record->_attributes[$name] : null; - } - $this->_oldAttributes = $record->_oldAttributes; - $this->_related = []; - $this->_relationsDependencies = []; - $this->afterRefresh(); - - return true; - } - - /** - * This method is called when the AR object is refreshed. - * The default implementation will trigger an [[EVENT_AFTER_REFRESH]] event. - * When overriding this method, make sure you call the parent implementation to ensure the - * event is triggered. - * @since 2.0.8 - */ - public function afterRefresh() - { - $this->trigger(self::EVENT_AFTER_REFRESH); - } - - /** - * Returns a value indicating whether the given active record is the same as the current one. - * The comparison is made by comparing the table names and the primary key values of the two active records. - * If one of the records [[isNewRecord|is new]] they are also considered not equal. - * @param ActiveRecordInterface $record record to compare to - * @return bool whether the two active records refer to the same row in the same database table. - */ - public function equals($record) - { - if ($this->getIsNewRecord() || $record->getIsNewRecord()) { - return false; - } - - return get_class($this) === get_class($record) && $this->getPrimaryKey() === $record->getPrimaryKey(); - } - - /** - * Returns the primary key value(s). - * @param bool $asArray whether to return the primary key value as an array. If `true`, - * the return value will be an array with column names as keys and column values as values. - * Note that for composite primary keys, an array will always be returned regardless of this parameter value. - * @property mixed The primary key value. An array (column name => column value) is returned if - * the primary key is composite. A string is returned otherwise (null will be returned if - * the key value is null). - * @return mixed the primary key value. An array (column name => column value) is returned if the primary key - * is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if - * the key value is null). - */ - public function getPrimaryKey($asArray = false) - { - $keys = $this->primaryKey(); - if (!$asArray && count($keys) === 1) { - return isset($this->_attributes[$keys[0]]) ? $this->_attributes[$keys[0]] : null; - } - - $values = []; - foreach ($keys as $name) { - $values[$name] = isset($this->_attributes[$name]) ? $this->_attributes[$name] : null; - } - - return $values; - } - - /** - * Returns the old primary key value(s). - * This refers to the primary key value that is populated into the record - * after executing a find method (e.g. find(), findOne()). - * The value remains unchanged even if the primary key attribute is manually assigned with a different value. - * @param bool $asArray whether to return the primary key value as an array. If `true`, - * the return value will be an array with column name as key and column value as value. - * If this is `false` (default), a scalar value will be returned for non-composite primary key. - * @property mixed The old primary key value. An array (column name => column value) is - * returned if the primary key is composite. A string is returned otherwise (null will be - * returned if the key value is null). - * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key - * is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if - * the key value is null). - * @throws Exception if the AR model does not have a primary key - */ - public function getOldPrimaryKey($asArray = false) - { - $keys = $this->primaryKey(); - if (empty($keys)) { - throw new Exception(get_class($this) . ' does not have a primary key. You should either define a primary key for the corresponding table or override the primaryKey() method.'); - } - if (!$asArray && count($keys) === 1) { - return isset($this->_oldAttributes[$keys[0]]) ? $this->_oldAttributes[$keys[0]] : null; - } - - $values = []; - foreach ($keys as $name) { - $values[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null; - } - - return $values; - } - - /** - * Populates an active record object using a row of data from the database/storage. - * - * This is an internal method meant to be called to create active record objects after - * fetching data from the database. It is mainly used by [[ActiveQuery]] to populate - * the query results into active records. - * - * When calling this method manually you should call [[afterFind()]] on the created - * record to trigger the [[EVENT_AFTER_FIND|afterFind Event]]. - * - * @param BaseActiveRecord $record the record to be populated. In most cases this will be an instance - * created by [[instantiate()]] beforehand. - * @param array $row attribute values (name => value) - */ - public static function populateRecord($record, $row) - { - $columns = array_flip($record->attributes()); - foreach ($row as $name => $value) { - if (isset($columns[$name])) { - $record->_attributes[$name] = $value; - } elseif ($record->canSetProperty($name)) { - $record->$name = $value; - } - } - $record->_oldAttributes = $record->_attributes; - $record->_related = []; - $record->_relationsDependencies = []; - } - - /** - * Creates an active record instance. - * - * This method is called together with [[populateRecord()]] by [[ActiveQuery]]. - * It is not meant to be used for creating new records directly. - * - * You may override this method if the instance being created - * depends on the row data to be populated into the record. - * For example, by creating a record based on the value of a column, - * you may implement the so-called single-table inheritance mapping. - * @param array $row row data to be populated into the record. - * @return static the newly created active record - */ - public static function instantiate($row) - { - return new static(); - } - - /** - * Returns whether there is an element at the specified offset. - * This method is required by the interface [[\ArrayAccess]]. - * @param mixed $offset the offset to check on - * @return bool whether there is an element at the specified offset. - */ - public function offsetExists($offset) - { - return $this->__isset($offset); - } - - /** - * Returns the relation object with the specified name. - * A relation is defined by a getter method which returns an [[ActiveQueryInterface]] object. - * It can be declared in either the Active Record class itself or one of its behaviors. - * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive). - * @param bool $throwException whether to throw exception if the relation does not exist. - * @return ActiveQueryInterface|ActiveQuery the relational query object. If the relation does not exist - * and `$throwException` is `false`, `null` will be returned. - * @throws InvalidArgumentException if the named relation does not exist. - */ - public function getRelation($name, $throwException = true) - { - $getter = 'get' . $name; - try { - // the relation could be defined in a behavior - $relation = $this->$getter(); - } catch (UnknownMethodException $e) { - if ($throwException) { - throw new InvalidArgumentException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e); - } - - return null; - } - if (!$relation instanceof ActiveQueryInterface) { - if ($throwException) { - throw new InvalidArgumentException(get_class($this) . ' has no relation named "' . $name . '".'); - } - - return null; - } - - if (method_exists($this, $getter)) { - // relation name is case sensitive, trying to validate it when the relation is defined within this class - $method = new \ReflectionMethod($this, $getter); - $realName = lcfirst(substr($method->getName(), 3)); - if ($realName !== $name) { - if ($throwException) { - throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\"."); - } - - return null; - } - } - - return $relation; - } - - /** - * Establishes the relationship between two models. - * - * The relationship is established by setting the foreign key value(s) in one model - * to be the corresponding primary key value(s) in the other model. - * The model with the foreign key will be saved into database without performing validation. - * - * If the relationship involves a junction table, a new row will be inserted into the - * junction table which contains the primary key values from both models. - * - * Note that this method requires that the primary key value is not null. - * - * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method. - * @param ActiveRecordInterface $model the model to be linked with the current one. - * @param array $extraColumns additional column values to be saved into the junction table. - * This parameter is only meaningful for a relationship involving a junction table - * (i.e., a relation set with [[ActiveRelationTrait::via()]] or [[ActiveQuery::viaTable()]].) - * @throws InvalidCallException if the method is unable to link two models. - */ - public function link($name, $model, $extraColumns = []) - { - $relation = $this->getRelation($name); - - if ($relation->via !== null) { - if ($this->getIsNewRecord() || $model->getIsNewRecord()) { - throw new InvalidCallException('Unable to link models: the models being linked cannot be newly created.'); - } - if (is_array($relation->via)) { - /* @var $viaRelation ActiveQuery */ - list($viaName, $viaRelation) = $relation->via; - $viaClass = $viaRelation->modelClass; - // unset $viaName so that it can be reloaded to reflect the change - unset($this->_related[$viaName]); - } else { - $viaRelation = $relation->via; - $viaTable = reset($relation->via->from); - } - $columns = []; - foreach ($viaRelation->link as $a => $b) { - $columns[$a] = $this->$b; - } - foreach ($relation->link as $a => $b) { - $columns[$b] = $model->$a; - } - foreach ($extraColumns as $k => $v) { - $columns[$k] = $v; - } - if (is_array($relation->via)) { - /* @var $viaClass ActiveRecordInterface */ - /* @var $record ActiveRecordInterface */ - $record = Yii::createObject($viaClass); - foreach ($columns as $column => $value) { - $record->$column = $value; - } - $record->insert(false); - } else { - /* @var $viaTable string */ - static::getDb()->createCommand() - ->insert($viaTable, $columns)->execute(); - } - } else { - $p1 = $model->isPrimaryKey(array_keys($relation->link)); - $p2 = static::isPrimaryKey(array_values($relation->link)); - if ($p1 && $p2) { - if ($this->getIsNewRecord() && $model->getIsNewRecord()) { - throw new InvalidCallException('Unable to link models: at most one model can be newly created.'); - } elseif ($this->getIsNewRecord()) { - $this->bindModels(array_flip($relation->link), $this, $model); - } else { - $this->bindModels($relation->link, $model, $this); - } - } elseif ($p1) { - $this->bindModels(array_flip($relation->link), $this, $model); - } elseif ($p2) { - $this->bindModels($relation->link, $model, $this); - } else { - throw new InvalidCallException('Unable to link models: the link defining the relation does not involve any primary key.'); - } - } - - // update lazily loaded related objects - if (!$relation->multiple) { - $this->_related[$name] = $model; - } elseif (isset($this->_related[$name])) { - if ($relation->indexBy !== null) { - if ($relation->indexBy instanceof \Closure) { - $index = call_user_func($relation->indexBy, $model); - } else { - $index = $model->{$relation->indexBy}; - } - $this->_related[$name][$index] = $model; - } else { - $this->_related[$name][] = $model; - } - } - } - - /** - * Destroys the relationship between two models. - * - * The model with the foreign key of the relationship will be deleted if `$delete` is `true`. - * Otherwise, the foreign key will be set `null` and the model will be saved without validation. - * - * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method. - * @param ActiveRecordInterface $model the model to be unlinked from the current one. - * You have to make sure that the model is really related with the current model as this method - * does not check this. - * @param bool $delete whether to delete the model that contains the foreign key. - * If `false`, the model's foreign key will be set `null` and saved. - * If `true`, the model containing the foreign key will be deleted. - * @throws InvalidCallException if the models cannot be unlinked - */ - public function unlink($name, $model, $delete = false) - { - $relation = $this->getRelation($name); - - if ($relation->via !== null) { - if (is_array($relation->via)) { - /* @var $viaRelation ActiveQuery */ - list($viaName, $viaRelation) = $relation->via; - $viaClass = $viaRelation->modelClass; - unset($this->_related[$viaName]); - } else { - $viaRelation = $relation->via; - $viaTable = reset($relation->via->from); - } - $columns = []; - foreach ($viaRelation->link as $a => $b) { - $columns[$a] = $this->$b; - } - foreach ($relation->link as $a => $b) { - $columns[$b] = $model->$a; - } - $nulls = []; - foreach (array_keys($columns) as $a) { - $nulls[$a] = null; - } - if (is_array($relation->via)) { - /* @var $viaClass ActiveRecordInterface */ - if ($delete) { - $viaClass::deleteAll($columns); - } else { - $viaClass::updateAll($nulls, $columns); - } - } else { - /* @var $viaTable string */ - /* @var $command Command */ - $command = static::getDb()->createCommand(); - if ($delete) { - $command->delete($viaTable, $columns)->execute(); - } else { - $command->update($viaTable, $nulls, $columns)->execute(); - } - } - } else { - $p1 = $model->isPrimaryKey(array_keys($relation->link)); - $p2 = static::isPrimaryKey(array_values($relation->link)); - if ($p2) { - if ($delete) { - $model->delete(); - } else { - foreach ($relation->link as $a => $b) { - $model->$a = null; - } - $model->save(false); - } - } elseif ($p1) { - foreach ($relation->link as $a => $b) { - if (is_array($this->$b)) { // relation via array valued attribute - if (($key = array_search($model->$a, $this->$b, false)) !== false) { - $values = $this->$b; - unset($values[$key]); - $this->$b = array_values($values); - } - } else { - $this->$b = null; - } - } - $delete ? $this->delete() : $this->save(false); - } else { - throw new InvalidCallException('Unable to unlink models: the link does not involve any primary key.'); - } - } - - if (!$relation->multiple) { - unset($this->_related[$name]); - } elseif (isset($this->_related[$name])) { - /* @var $b ActiveRecordInterface */ - foreach ($this->_related[$name] as $a => $b) { - if ($model->getPrimaryKey() === $b->getPrimaryKey()) { - unset($this->_related[$name][$a]); - } - } - } - } - - /** - * Destroys the relationship in current model. - * - * The model with the foreign key of the relationship will be deleted if `$delete` is `true`. - * Otherwise, the foreign key will be set `null` and the model will be saved without validation. - * - * Note that to destroy the relationship without removing records make sure your keys can be set to null - * - * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method. - * @param bool $delete whether to delete the model that contains the foreign key. - * - * Note that the deletion will be performed using [[deleteAll()]], which will not trigger any events on the related models. - * If you need [[EVENT_BEFORE_DELETE]] or [[EVENT_AFTER_DELETE]] to be triggered, you need to [[find()|find]] the models first - * and then call [[delete()]] on each of them. - */ - public function unlinkAll($name, $delete = false) - { - $relation = $this->getRelation($name); - - if ($relation->via !== null) { - if (is_array($relation->via)) { - /* @var $viaRelation ActiveQuery */ - list($viaName, $viaRelation) = $relation->via; - $viaClass = $viaRelation->modelClass; - unset($this->_related[$viaName]); - } else { - $viaRelation = $relation->via; - $viaTable = reset($relation->via->from); - } - $condition = []; - $nulls = []; - foreach ($viaRelation->link as $a => $b) { - $nulls[$a] = null; - $condition[$a] = $this->$b; - } - if (!empty($viaRelation->where)) { - $condition = ['and', $condition, $viaRelation->where]; - } - if (!empty($viaRelation->on)) { - $condition = ['and', $condition, $viaRelation->on]; - } - if (is_array($relation->via)) { - /* @var $viaClass ActiveRecordInterface */ - if ($delete) { - $viaClass::deleteAll($condition); - } else { - $viaClass::updateAll($nulls, $condition); - } - } else { - /* @var $viaTable string */ - /* @var $command Command */ - $command = static::getDb()->createCommand(); - if ($delete) { - $command->delete($viaTable, $condition)->execute(); - } else { - $command->update($viaTable, $nulls, $condition)->execute(); - } - } - } else { - /* @var $relatedModel ActiveRecordInterface */ - $relatedModel = $relation->modelClass; - if (!$delete && count($relation->link) === 1 && is_array($this->{$b = reset($relation->link)})) { - // relation via array valued attribute - $this->$b = []; - $this->save(false); - } else { - $nulls = []; - $condition = []; - foreach ($relation->link as $a => $b) { - $nulls[$a] = null; - $condition[$a] = $this->$b; - } - if (!empty($relation->where)) { - $condition = ['and', $condition, $relation->where]; - } - if (!empty($relation->on)) { - $condition = ['and', $condition, $relation->on]; - } - if ($delete) { - $relatedModel::deleteAll($condition); - } else { - $relatedModel::updateAll($nulls, $condition); - } - } - } - - unset($this->_related[$name]); - } - - /** - * @param array $link - * @param ActiveRecordInterface $foreignModel - * @param ActiveRecordInterface $primaryModel - * @throws InvalidCallException - */ - private function bindModels($link, $foreignModel, $primaryModel) - { - foreach ($link as $fk => $pk) { - $value = $primaryModel->$pk; - if ($value === null) { - throw new InvalidCallException('Unable to link models: the primary key of ' . get_class($primaryModel) . ' is null.'); - } - if (is_array($foreignModel->$fk)) { // relation via array valued attribute - $foreignModel->{$fk}[] = $value; - } else { - $foreignModel->{$fk} = $value; - } - } - $foreignModel->save(false); - } - - /** - * Returns a value indicating whether the given set of attributes represents the primary key for this model. - * @param array $keys the set of attributes to check - * @return bool whether the given set of attributes represents the primary key for this model - */ - public static function isPrimaryKey($keys) - { - $pks = static::primaryKey(); - if (count($keys) === count($pks)) { - return count(array_intersect($keys, $pks)) === count($pks); - } - - return false; - } - - /** - * Returns the text label for the specified attribute. - * If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model. - * @param string $attribute the attribute name - * @return string the attribute label - * @see generateAttributeLabel() - * @see attributeLabels() - */ - public function getAttributeLabel($attribute) - { - $labels = $this->attributeLabels(); - if (isset($labels[$attribute])) { - return $labels[$attribute]; - } elseif (strpos($attribute, '.')) { - $attributeParts = explode('.', $attribute); - $neededAttribute = array_pop($attributeParts); - - $relatedModel = $this; - foreach ($attributeParts as $relationName) { - if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) { - $relatedModel = $relatedModel->$relationName; - } else { - try { - $relation = $relatedModel->getRelation($relationName); - } catch (InvalidParamException $e) { - return $this->generateAttributeLabel($attribute); - } - /* @var $modelClass ActiveRecordInterface */ - $modelClass = $relation->modelClass; - $relatedModel = $modelClass::instance(); - } - } - - $labels = $relatedModel->attributeLabels(); - if (isset($labels[$neededAttribute])) { - return $labels[$neededAttribute]; - } - } - - return $this->generateAttributeLabel($attribute); - } - - /** - * Returns the text hint for the specified attribute. - * If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model. - * @param string $attribute the attribute name - * @return string the attribute hint - * @see attributeHints() - * @since 2.0.4 - */ - public function getAttributeHint($attribute) - { - $hints = $this->attributeHints(); - if (isset($hints[$attribute])) { - return $hints[$attribute]; - } elseif (strpos($attribute, '.')) { - $attributeParts = explode('.', $attribute); - $neededAttribute = array_pop($attributeParts); - - $relatedModel = $this; - foreach ($attributeParts as $relationName) { - if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) { - $relatedModel = $relatedModel->$relationName; - } else { - try { - $relation = $relatedModel->getRelation($relationName); - } catch (InvalidParamException $e) { - return ''; - } - /* @var $modelClass ActiveRecordInterface */ - $modelClass = $relation->modelClass; - $relatedModel = $modelClass::instance(); - } - } - - $hints = $relatedModel->attributeHints(); - if (isset($hints[$neededAttribute])) { - return $hints[$neededAttribute]; - } - } - - return ''; - } - - /** - * {@inheritdoc} - * - * The default implementation returns the names of the columns whose values have been populated into this record. - */ - public function fields() - { - $fields = array_keys($this->_attributes); - - return array_combine($fields, $fields); - } - - /** - * {@inheritdoc} - * - * The default implementation returns the names of the relations that have been populated into this record. - */ - public function extraFields() - { - $fields = array_keys($this->getRelatedRecords()); - - return array_combine($fields, $fields); - } - - /** - * Sets the element value at the specified offset to null. - * This method is required by the SPL interface [[\ArrayAccess]]. - * It is implicitly called when you use something like `unset($model[$offset])`. - * @param mixed $offset the offset to unset element - */ - public function offsetUnset($offset) - { - if (property_exists($this, $offset)) { - $this->$offset = null; - } else { - unset($this->$offset); - } - } - - /** - * Resets dependent related models checking if their links contain specific attribute. - * @param string $attribute The changed attribute name. - */ - private function resetDependentRelations($attribute) - { - foreach ($this->_relationsDependencies[$attribute] as $relation) { - unset($this->_related[$relation]); - } - unset($this->_relationsDependencies[$attribute]); - } - - /** - * Sets relation dependencies for a property - * @param string $name property name - * @param ActiveQueryInterface $relation relation instance - * @param string|null $viaRelationName intermediate relation - */ - private function setRelationDependencies($name, $relation, $viaRelationName = null) - { - if (empty($relation->via) && $relation->link) { - foreach ($relation->link as $attribute) { - $this->_relationsDependencies[$attribute][$name] = $name; - if ($viaRelationName !== null) { - $this->_relationsDependencies[$attribute][] = $viaRelationName; - } - } - } elseif ($relation->via instanceof ActiveQueryInterface) { - $this->setRelationDependencies($name, $relation->via); - } elseif (is_array($relation->via)) { - list($viaRelationName, $viaQuery) = $relation->via; - $this->setRelationDependencies($name, $viaQuery, $viaRelationName); - } - } -} - diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/BaseObject.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/BaseObject.php.test deleted file mode 100644 index 7886ff0f85..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/BaseObject.php.test +++ /dev/null @@ -1,295 +0,0 @@ -_label; - * } - * - * public function setLabel($value) - * { - * $this->_label = $value; - * } - * ``` - * - * Property names are *case-insensitive*. - * - * A property can be accessed like a member variable of an object. Reading or writing a property will cause the invocation - * of the corresponding getter or setter method. For example, - * - * ```php - * // equivalent to $label = $object->getLabel(); - * $label = $object->label; - * // equivalent to $object->setLabel('abc'); - * $object->label = 'abc'; - * ``` - * - * If a property has only a getter method and has no setter method, it is considered as *read-only*. In this case, trying - * to modify the property value will cause an exception. - * - * One can call [[hasProperty()]], [[canGetProperty()]] and/or [[canSetProperty()]] to check the existence of a property. - * - * Besides the property feature, BaseObject also introduces an important object initialization life cycle. In particular, - * creating an new instance of BaseObject or its derived class will involve the following life cycles sequentially: - * - * 1. the class constructor is invoked; - * 2. object properties are initialized according to the given configuration; - * 3. the `init()` method is invoked. - * - * In the above, both Step 2 and 3 occur at the end of the class constructor. It is recommended that - * you perform object initialization in the `init()` method because at that stage, the object configuration - * is already applied. - * - * In order to ensure the above life cycles, if a child class of BaseObject needs to override the constructor, - * it should be done like the following: - * - * ```php - * public function __construct($param1, $param2, ..., $config = []) - * { - * ... - * parent::__construct($config); - * } - * ``` - * - * That is, a `$config` parameter (defaults to `[]`) should be declared as the last parameter - * of the constructor, and the parent implementation should be called at the end of the constructor. - * - * @author Qiang Xue - * @since 2.0.13 - */ -class BaseObject implements Configurable -{ - /** - * Returns the fully qualified name of this class. - * @return string the fully qualified name of this class. - * @deprecated since 2.0.14. On PHP >=5.5, use `::class` instead. - */ - public static function className() - { - return get_called_class(); - } - - /** - * Constructor. - * - * The default implementation does two things: - * - * - Initializes the object with the given configuration `$config`. - * - Call [[init()]]. - * - * If this method is overridden in a child class, it is recommended that - * - * - the last parameter of the constructor is a configuration array, like `$config` here. - * - call the parent implementation at the end of the constructor. - * - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($config = []) - { - if (!empty($config)) { - Yii::configure($this, $config); - } - $this->init(); - } - - /** - * Initializes the object. - * This method is invoked at the end of the constructor after the object is initialized with the - * given configuration. - */ - public function init() - { - } - - /** - * Returns the value of an object property. - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when executing `$value = $object->property;`. - * @param string $name the property name - * @return mixed the property value - * @throws UnknownPropertyException if the property is not defined - * @throws InvalidCallException if the property is write-only - * @see __set() - */ - public function __get($name) - { - $getter = 'get' . $name; - if (method_exists($this, $getter)) { - return $this->$getter(); - } elseif (method_exists($this, 'set' . $name)) { - throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name); - } - - throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); - } - - /** - * Sets value of an object property. - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when executing `$object->property = $value;`. - * @param string $name the property name or the event name - * @param mixed $value the property value - * @throws UnknownPropertyException if the property is not defined - * @throws InvalidCallException if the property is read-only - * @see __get() - */ - public function __set($name, $value) - { - $setter = 'set' . $name; - if (method_exists($this, $setter)) { - $this->$setter($value); - } elseif (method_exists($this, 'get' . $name)) { - throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name); - } else { - throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name); - } - } - - /** - * Checks if a property is set, i.e. defined and not null. - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when executing `isset($object->property)`. - * - * Note that if the property is not defined, false will be returned. - * @param string $name the property name or the event name - * @return bool whether the named property is set (not null). - * @see https://secure.php.net/manual/en/function.isset.php - */ - public function __isset($name) - { - $getter = 'get' . $name; - if (method_exists($this, $getter)) { - return $this->$getter() !== null; - } - - return false; - } - - /** - * Sets an object property to null. - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when executing `unset($object->property)`. - * - * Note that if the property is not defined, this method will do nothing. - * If the property is read-only, it will throw an exception. - * @param string $name the property name - * @throws InvalidCallException if the property is read only. - * @see https://secure.php.net/manual/en/function.unset.php - */ - public function __unset($name) - { - $setter = 'set' . $name; - if (method_exists($this, $setter)) { - $this->$setter(null); - } elseif (method_exists($this, 'get' . $name)) { - throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '::' . $name); - } - } - - /** - * Calls the named method which is not a class method. - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when an unknown method is being invoked. - * @param string $name the method name - * @param array $params method parameters - * @throws UnknownMethodException when calling unknown method - * @return mixed the method return value - */ - public function __call($name, $params) - { - throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()"); - } - - /** - * Returns a value indicating whether a property is defined. - * - * A property is defined if: - * - * - the class has a getter or setter method associated with the specified name - * (in this case, property name is case-insensitive); - * - the class has a member variable with the specified name (when `$checkVars` is true); - * - * @param string $name the property name - * @param bool $checkVars whether to treat member variables as properties - * @return bool whether the property is defined - * @see canGetProperty() - * @see canSetProperty() - */ - public function hasProperty($name, $checkVars = true) - { - return $this->canGetProperty($name, $checkVars) || $this->canSetProperty($name, false); - } - - /** - * Returns a value indicating whether a property can be read. - * - * A property is readable if: - * - * - the class has a getter method associated with the specified name - * (in this case, property name is case-insensitive); - * - the class has a member variable with the specified name (when `$checkVars` is true); - * - * @param string $name the property name - * @param bool $checkVars whether to treat member variables as properties - * @return bool whether the property can be read - * @see canSetProperty() - */ - public function canGetProperty($name, $checkVars = true) - { - return method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name); - } - - /** - * Returns a value indicating whether a property can be set. - * - * A property is writable if: - * - * - the class has a setter method associated with the specified name - * (in this case, property name is case-insensitive); - * - the class has a member variable with the specified name (when `$checkVars` is true); - * - * @param string $name the property name - * @param bool $checkVars whether to treat member variables as properties - * @return bool whether the property can be written - * @see canGetProperty() - */ - public function canSetProperty($name, $checkVars = true) - { - return method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name); - } - - /** - * Returns a value indicating whether a method is defined. - * - * The default implementation is a call to php function `method_exists()`. - * You may override this method when you implemented the php magic method `__call()`. - * @param string $name the method name - * @return bool whether the method is defined - */ - public function hasMethod($name) - { - return method_exists($this, $name); - } -} diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Component.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Component.php.test deleted file mode 100644 index 0d4215f38d..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Component.php.test +++ /dev/null @@ -1,766 +0,0 @@ -on('update', function ($event) { - * // send email notification - * }); - * ``` - * - * In the above, an anonymous function is attached to the "update" event of the post. You may attach - * the following types of event handlers: - * - * - anonymous function: `function ($event) { ... }` - * - object method: `[$object, 'handleAdd']` - * - static class method: `['Page', 'handleAdd']` - * - global function: `'handleAdd'` - * - * The signature of an event handler should be like the following: - * - * ```php - * function foo($event) - * ``` - * - * where `$event` is an [[Event]] object which includes parameters associated with the event. - * - * You can also attach a handler to an event when configuring a component with a configuration array. - * The syntax is like the following: - * - * ```php - * [ - * 'on add' => function ($event) { ... } - * ] - * ``` - * - * where `on add` stands for attaching an event to the `add` event. - * - * Sometimes, you may want to associate extra data with an event handler when you attach it to an event - * and then access it when the handler is invoked. You may do so by - * - * ```php - * $post->on('update', function ($event) { - * // the data can be accessed via $event->data - * }, $data); - * ``` - * - * A behavior is an instance of [[Behavior]] or its child class. A component can be attached with one or multiple - * behaviors. When a behavior is attached to a component, its public properties and methods can be accessed via the - * component directly, as if the component owns those properties and methods. - * - * To attach a behavior to a component, declare it in [[behaviors()]], or explicitly call [[attachBehavior]]. Behaviors - * declared in [[behaviors()]] are automatically attached to the corresponding component. - * - * One can also attach a behavior to a component when configuring it with a configuration array. The syntax is like the - * following: - * - * ```php - * [ - * 'as tree' => [ - * 'class' => 'Tree', - * ], - * ] - * ``` - * - * where `as tree` stands for attaching a behavior named `tree`, and the array will be passed to [[\Yii::createObject()]] - * to create the behavior object. - * - * For more details and usage information on Component, see the [guide article on components](guide:concept-components). - * - * @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only. - * - * @author Qiang Xue - * @since 2.0 - */ -class Component extends BaseObject -{ - /** - * @var array the attached event handlers (event name => handlers) - */ - private $_events = []; - /** - * @var array the event handlers attached for wildcard patterns (event name wildcard => handlers) - * @since 2.0.14 - */ - private $_eventWildcards = []; - /** - * @var Behavior[]|null the attached behaviors (behavior name => behavior). This is `null` when not initialized. - */ - private $_behaviors; - - - /** - * Returns the value of a component property. - * - * This method will check in the following order and act accordingly: - * - * - a property defined by a getter: return the getter result - * - a property of a behavior: return the behavior property value - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when executing `$value = $component->property;`. - * @param string $name the property name - * @return mixed the property value or the value of a behavior's property - * @throws UnknownPropertyException if the property is not defined - * @throws InvalidCallException if the property is write-only. - * @see __set() - */ - public function __get($name) - { - $getter = 'get' . $name; - if (method_exists($this, $getter)) { - // read property, e.g. getName() - return $this->$getter(); - } - - // behavior property - $this->ensureBehaviors(); - foreach ($this->_behaviors as $behavior) { - if ($behavior->canGetProperty($name)) { - return $behavior->$name; - } - } - - if (method_exists($this, 'set' . $name)) { - throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name); - } - - throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); - } - - /** - * Sets the value of a component property. - * - * This method will check in the following order and act accordingly: - * - * - a property defined by a setter: set the property value - * - an event in the format of "on xyz": attach the handler to the event "xyz" - * - a behavior in the format of "as xyz": attach the behavior named as "xyz" - * - a property of a behavior: set the behavior property value - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when executing `$component->property = $value;`. - * @param string $name the property name or the event name - * @param mixed $value the property value - * @throws UnknownPropertyException if the property is not defined - * @throws InvalidCallException if the property is read-only. - * @see __get() - */ - public function __set($name, $value) - { - $setter = 'set' . $name; - if (method_exists($this, $setter)) { - // set property - $this->$setter($value); - - return; - } elseif (strncmp($name, 'on ', 3) === 0) { - // on event: attach event handler - $this->on(trim(substr($name, 3)), $value); - - return; - } elseif (strncmp($name, 'as ', 3) === 0) { - // as behavior: attach behavior - $name = trim(substr($name, 3)); - $this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value)); - - return; - } - - // behavior property - $this->ensureBehaviors(); - foreach ($this->_behaviors as $behavior) { - if ($behavior->canSetProperty($name)) { - $behavior->$name = $value; - return; - } - } - - if (method_exists($this, 'get' . $name)) { - throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name); - } - - throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name); - } - - /** - * Checks if a property is set, i.e. defined and not null. - * - * This method will check in the following order and act accordingly: - * - * - a property defined by a setter: return whether the property is set - * - a property of a behavior: return whether the property is set - * - return `false` for non existing properties - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when executing `isset($component->property)`. - * @param string $name the property name or the event name - * @return bool whether the named property is set - * @see https://secure.php.net/manual/en/function.isset.php - */ - public function __isset($name) - { - $getter = 'get' . $name; - if (method_exists($this, $getter)) { - return $this->$getter() !== null; - } - - // behavior property - $this->ensureBehaviors(); - foreach ($this->_behaviors as $behavior) { - if ($behavior->canGetProperty($name)) { - return $behavior->$name !== null; - } - } - - return false; - } - - /** - * Sets a component property to be null. - * - * This method will check in the following order and act accordingly: - * - * - a property defined by a setter: set the property value to be null - * - a property of a behavior: set the property value to be null - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when executing `unset($component->property)`. - * @param string $name the property name - * @throws InvalidCallException if the property is read only. - * @see https://secure.php.net/manual/en/function.unset.php - */ - public function __unset($name) - { - $setter = 'set' . $name; - if (method_exists($this, $setter)) { - $this->$setter(null); - return; - } - - // behavior property - $this->ensureBehaviors(); - foreach ($this->_behaviors as $behavior) { - if ($behavior->canSetProperty($name)) { - $behavior->$name = null; - return; - } - } - - throw new InvalidCallException('Unsetting an unknown or read-only property: ' . get_class($this) . '::' . $name); - } - - /** - * Calls the named method which is not a class method. - * - * This method will check if any attached behavior has - * the named method and will execute it if available. - * - * Do not call this method directly as it is a PHP magic method that - * will be implicitly called when an unknown method is being invoked. - * @param string $name the method name - * @param array $params method parameters - * @return mixed the method return value - * @throws UnknownMethodException when calling unknown method - */ - public function __call($name, $params) - { - $this->ensureBehaviors(); - foreach ($this->_behaviors as $object) { - if ($object->hasMethod($name)) { - return call_user_func_array([$object, $name], $params); - } - } - throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()"); - } - - /** - * This method is called after the object is created by cloning an existing one. - * It removes all behaviors because they are attached to the old object. - */ - public function __clone() - { - $this->_events = []; - $this->_eventWildcards = []; - $this->_behaviors = null; - } - - /** - * Returns a value indicating whether a property is defined for this component. - * - * A property is defined if: - * - * - the class has a getter or setter method associated with the specified name - * (in this case, property name is case-insensitive); - * - the class has a member variable with the specified name (when `$checkVars` is true); - * - an attached behavior has a property of the given name (when `$checkBehaviors` is true). - * - * @param string $name the property name - * @param bool $checkVars whether to treat member variables as properties - * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component - * @return bool whether the property is defined - * @see canGetProperty() - * @see canSetProperty() - */ - public function hasProperty($name, $checkVars = true, $checkBehaviors = true) - { - return $this->canGetProperty($name, $checkVars, $checkBehaviors) || $this->canSetProperty($name, false, $checkBehaviors); - } - - /** - * Returns a value indicating whether a property can be read. - * - * A property can be read if: - * - * - the class has a getter method associated with the specified name - * (in this case, property name is case-insensitive); - * - the class has a member variable with the specified name (when `$checkVars` is true); - * - an attached behavior has a readable property of the given name (when `$checkBehaviors` is true). - * - * @param string $name the property name - * @param bool $checkVars whether to treat member variables as properties - * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component - * @return bool whether the property can be read - * @see canSetProperty() - */ - public function canGetProperty($name, $checkVars = true, $checkBehaviors = true) - { - if (method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name)) { - return true; - } elseif ($checkBehaviors) { - $this->ensureBehaviors(); - foreach ($this->_behaviors as $behavior) { - if ($behavior->canGetProperty($name, $checkVars)) { - return true; - } - } - } - - return false; - } - - /** - * Returns a value indicating whether a property can be set. - * - * A property can be written if: - * - * - the class has a setter method associated with the specified name - * (in this case, property name is case-insensitive); - * - the class has a member variable with the specified name (when `$checkVars` is true); - * - an attached behavior has a writable property of the given name (when `$checkBehaviors` is true). - * - * @param string $name the property name - * @param bool $checkVars whether to treat member variables as properties - * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component - * @return bool whether the property can be written - * @see canGetProperty() - */ - public function canSetProperty($name, $checkVars = true, $checkBehaviors = true) - { - if (method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name)) { - return true; - } elseif ($checkBehaviors) { - $this->ensureBehaviors(); - foreach ($this->_behaviors as $behavior) { - if ($behavior->canSetProperty($name, $checkVars)) { - return true; - } - } - } - - return false; - } - - /** - * Returns a value indicating whether a method is defined. - * - * A method is defined if: - * - * - the class has a method with the specified name - * - an attached behavior has a method with the given name (when `$checkBehaviors` is true). - * - * @param string $name the property name - * @param bool $checkBehaviors whether to treat behaviors' methods as methods of this component - * @return bool whether the method is defined - */ - public function hasMethod($name, $checkBehaviors = true) - { - if (method_exists($this, $name)) { - return true; - } elseif ($checkBehaviors) { - $this->ensureBehaviors(); - foreach ($this->_behaviors as $behavior) { - if ($behavior->hasMethod($name)) { - return true; - } - } - } - - return false; - } - - /** - * Returns a list of behaviors that this component should behave as. - * - * Child classes may override this method to specify the behaviors they want to behave as. - * - * The return value of this method should be an array of behavior objects or configurations - * indexed by behavior names. A behavior configuration can be either a string specifying - * the behavior class or an array of the following structure: - * - * ```php - * 'behaviorName' => [ - * 'class' => 'BehaviorClass', - * 'property1' => 'value1', - * 'property2' => 'value2', - * ] - * ``` - * - * Note that a behavior class must extend from [[Behavior]]. Behaviors can be attached using a name or anonymously. - * When a name is used as the array key, using this name, the behavior can later be retrieved using [[getBehavior()]] - * or be detached using [[detachBehavior()]]. Anonymous behaviors can not be retrieved or detached. - * - * Behaviors declared in this method will be attached to the component automatically (on demand). - * - * @return array the behavior configurations. - */ - public function behaviors() - { - return []; - } - - /** - * Returns a value indicating whether there is any handler attached to the named event. - * @param string $name the event name - * @return bool whether there is any handler attached to the event. - */ - public function hasEventHandlers($name) - { - $this->ensureBehaviors(); - - foreach ($this->_eventWildcards as $wildcard => $handlers) { - if (!empty($handlers) && StringHelper::matchWildcard($wildcard, $name)) { - return true; - } - } - - return !empty($this->_events[$name]) || Event::hasHandlers($this, $name); - } - - /** - * Attaches an event handler to an event. - * - * The event handler must be a valid PHP callback. The following are - * some examples: - * - * ``` - * function ($event) { ... } // anonymous function - * [$object, 'handleClick'] // $object->handleClick() - * ['Page', 'handleClick'] // Page::handleClick() - * 'handleClick' // global function handleClick() - * ``` - * - * The event handler must be defined with the following signature, - * - * ``` - * function ($event) - * ``` - * - * where `$event` is an [[Event]] object which includes parameters associated with the event. - * - * Since 2.0.14 you can specify event name as a wildcard pattern: - * - * ```php - * $component->on('event.group.*', function ($event) { - * Yii::trace($event->name . ' is triggered.'); - * }); - * ``` - * - * @param string $name the event name - * @param callable $handler the event handler - * @param mixed $data the data to be passed to the event handler when the event is triggered. - * When the event handler is invoked, this data can be accessed via [[Event::data]]. - * @param bool $append whether to append new event handler to the end of the existing - * handler list. If false, the new handler will be inserted at the beginning of the existing - * handler list. - * @see off() - */ - public function on($name, $handler, $data = null, $append = true) - { - $this->ensureBehaviors(); - - if (strpos($name, '*') !== false) { - if ($append || empty($this->_eventWildcards[$name])) { - $this->_eventWildcards[$name][] = [$handler, $data]; - } else { - array_unshift($this->_eventWildcards[$name], [$handler, $data]); - } - return; - } - - if ($append || empty($this->_events[$name])) { - $this->_events[$name][] = [$handler, $data]; - } else { - array_unshift($this->_events[$name], [$handler, $data]); - } - } - - /** - * Detaches an existing event handler from this component. - * - * This method is the opposite of [[on()]]. - * - * Note: in case wildcard pattern is passed for event name, only the handlers registered with this - * wildcard will be removed, while handlers registered with plain names matching this wildcard will remain. - * - * @param string $name event name - * @param callable $handler the event handler to be removed. - * If it is null, all handlers attached to the named event will be removed. - * @return bool if a handler is found and detached - * @see on() - */ - public function off($name, $handler = null) - { - $this->ensureBehaviors(); - if (empty($this->_events[$name]) && empty($this->_eventWildcards[$name])) { - return false; - } - if ($handler === null) { - unset($this->_events[$name], $this->_eventWildcards[$name]); - return true; - } - - $removed = false; - // plain event names - if (isset($this->_events[$name])) { - foreach ($this->_events[$name] as $i => $event) { - if ($event[0] === $handler) { - unset($this->_events[$name][$i]); - $removed = true; - } - } - if ($removed) { - $this->_events[$name] = array_values($this->_events[$name]); - return $removed; - } - } - - // wildcard event names - if (isset($this->_eventWildcards[$name])) { - foreach ($this->_eventWildcards[$name] as $i => $event) { - if ($event[0] === $handler) { - unset($this->_eventWildcards[$name][$i]); - $removed = true; - } - } - if ($removed) { - $this->_eventWildcards[$name] = array_values($this->_eventWildcards[$name]); - // remove empty wildcards to save future redundant regex checks: - if (empty($this->_eventWildcards[$name])) { - unset($this->_eventWildcards[$name]); - } - } - } - - return $removed; - } - - /** - * Triggers an event. - * This method represents the happening of an event. It invokes - * all attached handlers for the event including class-level handlers. - * @param string $name the event name - * @param Event $event the event parameter. If not set, a default [[Event]] object will be created. - */ - public function trigger($name, Event $event = null) - { - $this->ensureBehaviors(); - - $eventHandlers = []; - foreach ($this->_eventWildcards as $wildcard => $handlers) { - if (StringHelper::matchWildcard($wildcard, $name)) { - $eventHandlers = array_merge($eventHandlers, $handlers); - } - } - - if (!empty($this->_events[$name])) { - $eventHandlers = array_merge($eventHandlers, $this->_events[$name]); - } - - if (!empty($eventHandlers)) { - if ($event === null) { - $event = new Event(); - } - if ($event->sender === null) { - $event->sender = $this; - } - $event->handled = false; - $event->name = $name; - foreach ($eventHandlers as $handler) { - $event->data = $handler[1]; - call_user_func($handler[0], $event); - // stop further handling if the event is handled - if ($event->handled) { - return; - } - } - } - - // invoke class-level attached handlers - Event::trigger($this, $name, $event); - } - - /** - * Returns the named behavior object. - * @param string $name the behavior name - * @return null|Behavior the behavior object, or null if the behavior does not exist - */ - public function getBehavior($name) - { - $this->ensureBehaviors(); - return isset($this->_behaviors[$name]) ? $this->_behaviors[$name] : null; - } - - /** - * Returns all behaviors attached to this component. - * @return Behavior[] list of behaviors attached to this component - */ - public function getBehaviors() - { - $this->ensureBehaviors(); - return $this->_behaviors; - } - - /** - * Attaches a behavior to this component. - * This method will create the behavior object based on the given - * configuration. After that, the behavior object will be attached to - * this component by calling the [[Behavior::attach()]] method. - * @param string $name the name of the behavior. - * @param string|array|Behavior $behavior the behavior configuration. This can be one of the following: - * - * - a [[Behavior]] object - * - a string specifying the behavior class - * - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object. - * - * @return Behavior the behavior object - * @see detachBehavior() - */ - public function attachBehavior($name, $behavior) - { - $this->ensureBehaviors(); - return $this->attachBehaviorInternal($name, $behavior); - } - - /** - * Attaches a list of behaviors to the component. - * Each behavior is indexed by its name and should be a [[Behavior]] object, - * a string specifying the behavior class, or an configuration array for creating the behavior. - * @param array $behaviors list of behaviors to be attached to the component - * @see attachBehavior() - */ - public function attachBehaviors($behaviors) - { - $this->ensureBehaviors(); - foreach ($behaviors as $name => $behavior) { - $this->attachBehaviorInternal($name, $behavior); - } - } - - /** - * Detaches a behavior from the component. - * The behavior's [[Behavior::detach()]] method will be invoked. - * @param string $name the behavior's name. - * @return null|Behavior the detached behavior. Null if the behavior does not exist. - */ - public function detachBehavior($name) - { - $this->ensureBehaviors(); - if (isset($this->_behaviors[$name])) { - $behavior = $this->_behaviors[$name]; - unset($this->_behaviors[$name]); - $behavior->detach(); - return $behavior; - } - - return null; - } - - /** - * Detaches all behaviors from the component. - */ - public function detachBehaviors() - { - $this->ensureBehaviors(); - foreach ($this->_behaviors as $name => $behavior) { - $this->detachBehavior($name); - } - } - - /** - * Makes sure that the behaviors declared in [[behaviors()]] are attached to this component. - */ - public function ensureBehaviors() - { - if ($this->_behaviors === null) { - $this->_behaviors = []; - foreach ($this->behaviors() as $name => $behavior) { - $this->attachBehaviorInternal($name, $behavior); - } - } - } - - /** - * Attaches a behavior to this component. - * @param string|int $name the name of the behavior. If this is an integer, it means the behavior - * is an anonymous one. Otherwise, the behavior is a named one and any existing behavior with the same name - * will be detached first. - * @param string|array|Behavior $behavior the behavior to be attached - * @return Behavior the attached behavior. - */ - private function attachBehaviorInternal($name, $behavior) - { - if (!($behavior instanceof Behavior)) { - $behavior = Yii::createObject($behavior); - } - if (is_int($name)) { - $behavior->attach($this); - $this->_behaviors[] = $behavior; - } else { - if (isset($this->_behaviors[$name])) { - $this->_behaviors[$name]->detach(); - } - $behavior->attach($this); - $this->_behaviors[$name] = $behavior; - } - - return $behavior; - } -} - diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Model.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Model.php.test deleted file mode 100644 index 701659883f..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Model.php.test +++ /dev/null @@ -1,1052 +0,0 @@ - value). - * @property array $errors An array of errors for all attributes. Empty array is returned if no error. The - * result is a two-dimensional array. See [[getErrors()]] for detailed description. This property is read-only. - * @property array $firstErrors The first errors. The array keys are the attribute names, and the array values - * are the corresponding error messages. An empty array will be returned if there is no error. This property is - * read-only. - * @property ArrayIterator $iterator An iterator for traversing the items in the list. This property is - * read-only. - * @property string $scenario The scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]]. - * @property ArrayObject|\yii\validators\Validator[] $validators All the validators declared in the model. - * This property is read-only. - * - * @author Qiang Xue - * @since 2.0 - */ -class Model extends Component implements StaticInstanceInterface, IteratorAggregate, ArrayAccess, Arrayable -{ - use ArrayableTrait; - use StaticInstanceTrait; - - /** - * The name of the default scenario. - */ - const SCENARIO_DEFAULT = 'default'; - /** - * @event ModelEvent an event raised at the beginning of [[validate()]]. You may set - * [[ModelEvent::isValid]] to be false to stop the validation. - */ - const EVENT_BEFORE_VALIDATE = 'beforeValidate'; - /** - * @event Event an event raised at the end of [[validate()]] - */ - const EVENT_AFTER_VALIDATE = 'afterValidate'; - - /** - * @var array validation errors (attribute name => array of errors) - */ - private $_errors; - /** - * @var ArrayObject list of validators - */ - private $_validators; - /** - * @var string current scenario - */ - private $_scenario = self::SCENARIO_DEFAULT; - - - /** - * Returns the validation rules for attributes. - * - * Validation rules are used by [[validate()]] to check if attribute values are valid. - * Child classes may override this method to declare different validation rules. - * - * Each rule is an array with the following structure: - * - * ```php - * [ - * ['attribute1', 'attribute2'], - * 'validator type', - * 'on' => ['scenario1', 'scenario2'], - * //...other parameters... - * ] - * ``` - * - * where - * - * - attribute list: required, specifies the attributes array to be validated, for single attribute you can pass a string; - * - validator type: required, specifies the validator to be used. It can be a built-in validator name, - * a method name of the model class, an anonymous function, or a validator class name. - * - on: optional, specifies the [[scenario|scenarios]] array in which the validation - * rule can be applied. If this option is not set, the rule will apply to all scenarios. - * - additional name-value pairs can be specified to initialize the corresponding validator properties. - * Please refer to individual validator class API for possible properties. - * - * A validator can be either an object of a class extending [[Validator]], or a model class method - * (called *inline validator*) that has the following signature: - * - * ```php - * // $params refers to validation parameters given in the rule - * function validatorName($attribute, $params) - * ``` - * - * In the above `$attribute` refers to the attribute currently being validated while `$params` contains an array of - * validator configuration options such as `max` in case of `string` validator. The value of the attribute currently being validated - * can be accessed as `$this->$attribute`. Note the `$` before `attribute`; this is taking the value of the variable - * `$attribute` and using it as the name of the property to access. - * - * Yii also provides a set of [[Validator::builtInValidators|built-in validators]]. - * Each one has an alias name which can be used when specifying a validation rule. - * - * Below are some examples: - * - * ```php - * [ - * // built-in "required" validator - * [['username', 'password'], 'required'], - * // built-in "string" validator customized with "min" and "max" properties - * ['username', 'string', 'min' => 3, 'max' => 12], - * // built-in "compare" validator that is used in "register" scenario only - * ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'], - * // an inline validator defined via the "authenticate()" method in the model class - * ['password', 'authenticate', 'on' => 'login'], - * // a validator of class "DateRangeValidator" - * ['dateRange', 'DateRangeValidator'], - * ]; - * ``` - * - * Note, in order to inherit rules defined in the parent class, a child class needs to - * merge the parent rules with child rules using functions such as `array_merge()`. - * - * @return array validation rules - * @see scenarios() - */ - public function rules() - { - return []; - } - - /** - * Returns a list of scenarios and the corresponding active attributes. - * - * An active attribute is one that is subject to validation in the current scenario. - * The returned array should be in the following format: - * - * ```php - * [ - * 'scenario1' => ['attribute11', 'attribute12', ...], - * 'scenario2' => ['attribute21', 'attribute22', ...], - * ... - * ] - * ``` - * - * By default, an active attribute is considered safe and can be massively assigned. - * If an attribute should NOT be massively assigned (thus considered unsafe), - * please prefix the attribute with an exclamation character (e.g. `'!rank'`). - * - * The default implementation of this method will return all scenarios found in the [[rules()]] - * declaration. A special scenario named [[SCENARIO_DEFAULT]] will contain all attributes - * found in the [[rules()]]. Each scenario will be associated with the attributes that - * are being validated by the validation rules that apply to the scenario. - * - * @return array a list of scenarios and the corresponding active attributes. - */ - public function scenarios() - { - $scenarios = [self::SCENARIO_DEFAULT => []]; - foreach ($this->getValidators() as $validator) { - foreach ($validator->on as $scenario) { - $scenarios[$scenario] = []; - } - foreach ($validator->except as $scenario) { - $scenarios[$scenario] = []; - } - } - $names = array_keys($scenarios); - - foreach ($this->getValidators() as $validator) { - if (empty($validator->on) && empty($validator->except)) { - foreach ($names as $name) { - foreach ($validator->attributes as $attribute) { - $scenarios[$name][$attribute] = true; - } - } - } elseif (empty($validator->on)) { - foreach ($names as $name) { - if (!in_array($name, $validator->except, true)) { - foreach ($validator->attributes as $attribute) { - $scenarios[$name][$attribute] = true; - } - } - } - } else { - foreach ($validator->on as $name) { - foreach ($validator->attributes as $attribute) { - $scenarios[$name][$attribute] = true; - } - } - } - } - - foreach ($scenarios as $scenario => $attributes) { - if (!empty($attributes)) { - $scenarios[$scenario] = array_keys($attributes); - } - } - - return $scenarios; - } - - /** - * Returns the form name that this model class should use. - * - * The form name is mainly used by [[\yii\widgets\ActiveForm]] to determine how to name - * the input fields for the attributes in a model. If the form name is "A" and an attribute - * name is "b", then the corresponding input name would be "A[b]". If the form name is - * an empty string, then the input name would be "b". - * - * The purpose of the above naming schema is that for forms which contain multiple different models, - * the attributes of each model are grouped in sub-arrays of the POST-data and it is easier to - * differentiate between them. - * - * By default, this method returns the model class name (without the namespace part) - * as the form name. You may override it when the model is used in different forms. - * - * @return string the form name of this model class. - * @see load() - * @throws InvalidConfigException when form is defined with anonymous class and `formName()` method is - * not overridden. - */ - public function formName() - { - $reflector = new ReflectionClass($this); - if (PHP_VERSION_ID >= 70000 && $reflector->isAnonymous()) { - throw new InvalidConfigException('The "formName()" method should be explicitly defined for anonymous models'); - } - return $reflector->getShortName(); - } - - /** - * Returns the list of attribute names. - * By default, this method returns all public non-static properties of the class. - * You may override this method to change the default behavior. - * @return array list of attribute names. - */ - public function attributes() - { - $class = new ReflectionClass($this); - $names = []; - foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { - if (!$property->isStatic()) { - $names[] = $property->getName(); - } - } - - return $names; - } - - /** - * Returns the attribute labels. - * - * Attribute labels are mainly used for display purpose. For example, given an attribute - * `firstName`, we can declare a label `First Name` which is more user-friendly and can - * be displayed to end users. - * - * By default an attribute label is generated using [[generateAttributeLabel()]]. - * This method allows you to explicitly specify attribute labels. - * - * Note, in order to inherit labels defined in the parent class, a child class needs to - * merge the parent labels with child labels using functions such as `array_merge()`. - * - * @return array attribute labels (name => label) - * @see generateAttributeLabel() - */ - public function attributeLabels() - { - return []; - } - - /** - * Returns the attribute hints. - * - * Attribute hints are mainly used for display purpose. For example, given an attribute - * `isPublic`, we can declare a hint `Whether the post should be visible for not logged in users`, - * which provides user-friendly description of the attribute meaning and can be displayed to end users. - * - * Unlike label hint will not be generated, if its explicit declaration is omitted. - * - * Note, in order to inherit hints defined in the parent class, a child class needs to - * merge the parent hints with child hints using functions such as `array_merge()`. - * - * @return array attribute hints (name => hint) - * @since 2.0.4 - */ - public function attributeHints() - { - return []; - } - - /** - * Performs the data validation. - * - * This method executes the validation rules applicable to the current [[scenario]]. - * The following criteria are used to determine whether a rule is currently applicable: - * - * - the rule must be associated with the attributes relevant to the current scenario; - * - the rules must be effective for the current scenario. - * - * This method will call [[beforeValidate()]] and [[afterValidate()]] before and - * after the actual validation, respectively. If [[beforeValidate()]] returns false, - * the validation will be cancelled and [[afterValidate()]] will not be called. - * - * Errors found during the validation can be retrieved via [[getErrors()]], - * [[getFirstErrors()]] and [[getFirstError()]]. - * - * @param string[]|string $attributeNames attribute name or list of attribute names that should be validated. - * If this parameter is empty, it means any attribute listed in the applicable - * validation rules should be validated. - * @param bool $clearErrors whether to call [[clearErrors()]] before performing validation - * @return bool whether the validation is successful without any error. - * @throws InvalidArgumentException if the current scenario is unknown. - */ - public function validate($attributeNames = null, $clearErrors = true) - { - if ($clearErrors) { - $this->clearErrors(); - } - - if (!$this->beforeValidate()) { - return false; - } - - $scenarios = $this->scenarios(); - $scenario = $this->getScenario(); - if (!isset($scenarios[$scenario])) { - throw new InvalidArgumentException("Unknown scenario: $scenario"); - } - - if ($attributeNames === null) { - $attributeNames = $this->activeAttributes(); - } - - $attributeNames = (array)$attributeNames; - - foreach ($this->getActiveValidators() as $validator) { - $validator->validateAttributes($this, $attributeNames); - } - $this->afterValidate(); - - return !$this->hasErrors(); - } - - /** - * This method is invoked before validation starts. - * The default implementation raises a `beforeValidate` event. - * You may override this method to do preliminary checks before validation. - * Make sure the parent implementation is invoked so that the event can be raised. - * @return bool whether the validation should be executed. Defaults to true. - * If false is returned, the validation will stop and the model is considered invalid. - */ - public function beforeValidate() - { - $event = new ModelEvent(); - $this->trigger(self::EVENT_BEFORE_VALIDATE, $event); - - return $event->isValid; - } - - /** - * This method is invoked after validation ends. - * The default implementation raises an `afterValidate` event. - * You may override this method to do postprocessing after validation. - * Make sure the parent implementation is invoked so that the event can be raised. - */ - public function afterValidate() - { - $this->trigger(self::EVENT_AFTER_VALIDATE); - } - - /** - * Returns all the validators declared in [[rules()]]. - * - * This method differs from [[getActiveValidators()]] in that the latter - * only returns the validators applicable to the current [[scenario]]. - * - * Because this method returns an ArrayObject object, you may - * manipulate it by inserting or removing validators (useful in model behaviors). - * For example, - * - * ```php - * $model->validators[] = $newValidator; - * ``` - * - * @return ArrayObject|\yii\validators\Validator[] all the validators declared in the model. - */ - public function getValidators() - { - if ($this->_validators === null) { - $this->_validators = $this->createValidators(); - } - - return $this->_validators; - } - - /** - * Returns the validators applicable to the current [[scenario]]. - * @param string $attribute the name of the attribute whose applicable validators should be returned. - * If this is null, the validators for ALL attributes in the model will be returned. - * @return \yii\validators\Validator[] the validators applicable to the current [[scenario]]. - */ - public function getActiveValidators($attribute = null) - { - $activeAttributes = $this->activeAttributes(); - if ($attribute !== null && !in_array($attribute, $activeAttributes, true)) { - return []; - } - $scenario = $this->getScenario(); - $validators = []; - foreach ($this->getValidators() as $validator) { - if ($attribute === null) { - $validatorAttributes = $validator->getValidationAttributes($activeAttributes); - $attributeValid = !empty($validatorAttributes); - } else { - $attributeValid = in_array($attribute, $validator->getValidationAttributes($attribute), true); - } - if ($attributeValid && $validator->isActive($scenario)) { - $validators[] = $validator; - } - } - - return $validators; - } - - /** - * Creates validator objects based on the validation rules specified in [[rules()]]. - * Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned. - * @return ArrayObject validators - * @throws InvalidConfigException if any validation rule configuration is invalid - */ - public function createValidators() - { - $validators = new ArrayObject(); - foreach ($this->rules() as $rule) { - if ($rule instanceof Validator) { - $validators->append($rule); - } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type - $validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2)); - $validators->append($validator); - } else { - throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.'); - } - } - - return $validators; - } - - /** - * Returns a value indicating whether the attribute is required. - * This is determined by checking if the attribute is associated with a - * [[\yii\validators\RequiredValidator|required]] validation rule in the - * current [[scenario]]. - * - * Note that when the validator has a conditional validation applied using - * [[\yii\validators\RequiredValidator::$when|$when]] this method will return - * `false` regardless of the `when` condition because it may be called be - * before the model is loaded with data. - * - * @param string $attribute attribute name - * @return bool whether the attribute is required - */ - public function isAttributeRequired($attribute) - { - foreach ($this->getActiveValidators($attribute) as $validator) { - if ($validator instanceof RequiredValidator && $validator->when === null) { - return true; - } - } - - return false; - } - - /** - * Returns a value indicating whether the attribute is safe for massive assignments. - * @param string $attribute attribute name - * @return bool whether the attribute is safe for massive assignments - * @see safeAttributes() - */ - public function isAttributeSafe($attribute) - { - return in_array($attribute, $this->safeAttributes(), true); - } - - /** - * Returns a value indicating whether the attribute is active in the current scenario. - * @param string $attribute attribute name - * @return bool whether the attribute is active in the current scenario - * @see activeAttributes() - */ - public function isAttributeActive($attribute) - { - return in_array($attribute, $this->activeAttributes(), true); - } - - /** - * Returns the text label for the specified attribute. - * @param string $attribute the attribute name - * @return string the attribute label - * @see generateAttributeLabel() - * @see attributeLabels() - */ - public function getAttributeLabel($attribute) - { - $labels = $this->attributeLabels(); - return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute); - } - - /** - * Returns the text hint for the specified attribute. - * @param string $attribute the attribute name - * @return string the attribute hint - * @see attributeHints() - * @since 2.0.4 - */ - public function getAttributeHint($attribute) - { - $hints = $this->attributeHints(); - return isset($hints[$attribute]) ? $hints[$attribute] : ''; - } - - /** - * Returns a value indicating whether there is any validation error. - * @param string|null $attribute attribute name. Use null to check all attributes. - * @return bool whether there is any error. - */ - public function hasErrors($attribute = null) - { - return $attribute === null ? !empty($this->_errors) : isset($this->_errors[$attribute]); - } - - /** - * Returns the errors for all attributes or a single attribute. - * @param string $attribute attribute name. Use null to retrieve errors for all attributes. - * @property array An array of errors for all attributes. Empty array is returned if no error. - * The result is a two-dimensional array. See [[getErrors()]] for detailed description. - * @return array errors for all attributes or the specified attribute. Empty array is returned if no error. - * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following: - * - * ```php - * [ - * 'username' => [ - * 'Username is required.', - * 'Username must contain only word characters.', - * ], - * 'email' => [ - * 'Email address is invalid.', - * ] - * ] - * ``` - * - * @see getFirstErrors() - * @see getFirstError() - */ - public function getErrors($attribute = null) - { - if ($attribute === null) { - return $this->_errors === null ? [] : $this->_errors; - } - - return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : []; - } - - /** - * Returns the first error of every attribute in the model. - * @return array the first errors. The array keys are the attribute names, and the array - * values are the corresponding error messages. An empty array will be returned if there is no error. - * @see getErrors() - * @see getFirstError() - */ - public function getFirstErrors() - { - if (empty($this->_errors)) { - return []; - } - - $errors = []; - foreach ($this->_errors as $name => $es) { - if (!empty($es)) { - $errors[$name] = reset($es); - } - } - - return $errors; - } - - /** - * Returns the first error of the specified attribute. - * @param string $attribute attribute name. - * @return string the error message. Null is returned if no error. - * @see getErrors() - * @see getFirstErrors() - */ - public function getFirstError($attribute) - { - return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null; - } - - /** - * Returns the errors for all attributes as a one-dimensional array. - * @param bool $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise - * only the first error message for each attribute will be shown. - * @return array errors for all attributes as a one-dimensional array. Empty array is returned if no error. - * @see getErrors() - * @see getFirstErrors() - * @since 2.0.14 - */ - public function getErrorSummary($showAllErrors) - { - $lines = []; - $errors = $showAllErrors ? $this->getErrors() : $this->getFirstErrors(); - foreach ($errors as $es) { - $lines = array_merge((array)$es, $lines); - } - return $lines; - } - - /** - * Adds a new error to the specified attribute. - * @param string $attribute attribute name - * @param string $error new error message - */ - public function addError($attribute, $error = '') - { - $this->_errors[$attribute][] = $error; - } - - /** - * Adds a list of errors. - * @param array $items a list of errors. The array keys must be attribute names. - * The array values should be error messages. If an attribute has multiple errors, - * these errors must be given in terms of an array. - * You may use the result of [[getErrors()]] as the value for this parameter. - * @since 2.0.2 - */ - public function addErrors(array $items) - { - foreach ($items as $attribute => $errors) { - if (is_array($errors)) { - foreach ($errors as $error) { - $this->addError($attribute, $error); - } - } else { - $this->addError($attribute, $errors); - } - } - } - - /** - * Removes errors for all attributes or a single attribute. - * @param string $attribute attribute name. Use null to remove errors for all attributes. - */ - public function clearErrors($attribute = null) - { - if ($attribute === null) { - $this->_errors = []; - } else { - unset($this->_errors[$attribute]); - } - } - - /** - * Generates a user friendly attribute label based on the give attribute name. - * This is done by replacing underscores, dashes and dots with blanks and - * changing the first letter of each word to upper case. - * For example, 'department_name' or 'DepartmentName' will generate 'Department Name'. - * @param string $name the column name - * @return string the attribute label - */ - public function generateAttributeLabel($name) - { - return Inflector::camel2words($name, true); - } - - /** - * Returns attribute values. - * @param array $names list of attributes whose value needs to be returned. - * Defaults to null, meaning all attributes listed in [[attributes()]] will be returned. - * If it is an array, only the attributes in the array will be returned. - * @param array $except list of attributes whose value should NOT be returned. - * @return array attribute values (name => value). - */ - public function getAttributes($names = null, $except = []) - { - $values = []; - if ($names === null) { - $names = $this->attributes(); - } - foreach ($names as $name) { - $values[$name] = $this->$name; - } - foreach ($except as $name) { - unset($values[$name]); - } - - return $values; - } - - /** - * Sets the attribute values in a massive way. - * @param array $values attribute values (name => value) to be assigned to the model. - * @param bool $safeOnly whether the assignments should only be done to the safe attributes. - * A safe attribute is one that is associated with a validation rule in the current [[scenario]]. - * @see safeAttributes() - * @see attributes() - */ - public function setAttributes($values, $safeOnly = true) - { - if (is_array($values)) { - $attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes()); - foreach ($values as $name => $value) { - if (isset($attributes[$name])) { - $this->$name = $value; - } elseif ($safeOnly) { - $this->onUnsafeAttribute($name, $value); - } - } - } - } - - /** - * This method is invoked when an unsafe attribute is being massively assigned. - * The default implementation will log a warning message if YII_DEBUG is on. - * It does nothing otherwise. - * @param string $name the unsafe attribute name - * @param mixed $value the attribute value - */ - public function onUnsafeAttribute($name, $value) - { - if (YII_DEBUG) { - Yii::debug("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__); - } - } - - /** - * Returns the scenario that this model is used in. - * - * Scenario affects how validation is performed and which attributes can - * be massively assigned. - * - * @return string the scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]]. - */ - public function getScenario() - { - return $this->_scenario; - } - - /** - * Sets the scenario for the model. - * Note that this method does not check if the scenario exists or not. - * The method [[validate()]] will perform this check. - * @param string $value the scenario that this model is in. - */ - public function setScenario($value) - { - $this->_scenario = $value; - } - - /** - * Returns the attribute names that are safe to be massively assigned in the current scenario. - * @return string[] safe attribute names - */ - public function safeAttributes() - { - $scenario = $this->getScenario(); - $scenarios = $this->scenarios(); - if (!isset($scenarios[$scenario])) { - return []; - } - $attributes = []; - foreach ($scenarios[$scenario] as $attribute) { - if (strncmp($attribute, '!', 1) !== 0 && !in_array('!' . $attribute, $scenarios[$scenario])) { - $attributes[] = $attribute; - } - } - - return $attributes; - } - - /** - * Returns the attribute names that are subject to validation in the current scenario. - * @return string[] safe attribute names - */ - public function activeAttributes() - { - $scenario = $this->getScenario(); - $scenarios = $this->scenarios(); - if (!isset($scenarios[$scenario])) { - return []; - } - $attributes = array_keys(array_flip($scenarios[$scenario])); - foreach ($attributes as $i => $attribute) { - if (strncmp($attribute, '!', 1) === 0) { - $attributes[$i] = substr($attribute, 1); - } - } - - return $attributes; - } - - /** - * Populates the model with input data. - * - * This method provides a convenient shortcut for: - * - * ```php - * if (isset($_POST['FormName'])) { - * $model->attributes = $_POST['FormName']; - * if ($model->save()) { - * // handle success - * } - * } - * ``` - * - * which, with `load()` can be written as: - * - * ```php - * if ($model->load($_POST) && $model->save()) { - * // handle success - * } - * ``` - * - * `load()` gets the `'FormName'` from the model's [[formName()]] method (which you may override), unless the - * `$formName` parameter is given. If the form name is empty, `load()` populates the model with the whole of `$data`, - * instead of `$data['FormName']`. - * - * Note, that the data being populated is subject to the safety check by [[setAttributes()]]. - * - * @param array $data the data array to load, typically `$_POST` or `$_GET`. - * @param string $formName the form name to use to load the data into the model. - * If not set, [[formName()]] is used. - * @return bool whether `load()` found the expected form in `$data`. - */ - public function load($data, $formName = null) - { - $scope = $formName === null ? $this->formName() : $formName; - if ($scope === '' && !empty($data)) { - $this->setAttributes($data); - - return true; - } elseif (isset($data[$scope])) { - $this->setAttributes($data[$scope]); - - return true; - } - - return false; - } - - /** - * Populates a set of models with the data from end user. - * This method is mainly used to collect tabular data input. - * The data to be loaded for each model is `$data[formName][index]`, where `formName` - * refers to the value of [[formName()]], and `index` the index of the model in the `$models` array. - * If [[formName()]] is empty, `$data[index]` will be used to populate each model. - * The data being populated to each model is subject to the safety check by [[setAttributes()]]. - * @param array $models the models to be populated. Note that all models should have the same class. - * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array - * supplied by end user. - * @param string $formName the form name to be used for loading the data into the models. - * If not set, it will use the [[formName()]] value of the first model in `$models`. - * This parameter is available since version 2.0.1. - * @return bool whether at least one of the models is successfully populated. - */ - public static function loadMultiple($models, $data, $formName = null) - { - if ($formName === null) { - /* @var $first Model|false */ - $first = reset($models); - if ($first === false) { - return false; - } - $formName = $first->formName(); - } - - $success = false; - foreach ($models as $i => $model) { - /* @var $model Model */ - if ($formName == '') { - if (!empty($data[$i]) && $model->load($data[$i], '')) { - $success = true; - } - } elseif (!empty($data[$formName][$i]) && $model->load($data[$formName][$i], '')) { - $success = true; - } - } - - return $success; - } - - /** - * Validates multiple models. - * This method will validate every model. The models being validated may - * be of the same or different types. - * @param array $models the models to be validated - * @param array $attributeNames list of attribute names that should be validated. - * If this parameter is empty, it means any attribute listed in the applicable - * validation rules should be validated. - * @return bool whether all models are valid. False will be returned if one - * or multiple models have validation error. - */ - public static function validateMultiple($models, $attributeNames = null) - { - $valid = true; - /* @var $model Model */ - foreach ($models as $model) { - $valid = $model->validate($attributeNames) && $valid; - } - - return $valid; - } - - /** - * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified. - * - * A field is a named element in the returned array by [[toArray()]]. - * - * This method should return an array of field names or field definitions. - * If the former, the field name will be treated as an object property name whose value will be used - * as the field value. If the latter, the array key should be the field name while the array value should be - * the corresponding field definition which can be either an object property name or a PHP callable - * returning the corresponding field value. The signature of the callable should be: - * - * ```php - * function ($model, $field) { - * // return field value - * } - * ``` - * - * For example, the following code declares four fields: - * - * - `email`: the field name is the same as the property name `email`; - * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their - * values are obtained from the `first_name` and `last_name` properties; - * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name` - * and `last_name`. - * - * ```php - * return [ - * 'email', - * 'firstName' => 'first_name', - * 'lastName' => 'last_name', - * 'fullName' => function ($model) { - * return $model->first_name . ' ' . $model->last_name; - * }, - * ]; - * ``` - * - * In this method, you may also want to return different lists of fields based on some context - * information. For example, depending on [[scenario]] or the privilege of the current application user, - * you may return different sets of visible fields or filter out some fields. - * - * The default implementation of this method returns [[attributes()]] indexed by the same attribute names. - * - * @return array the list of field names or field definitions. - * @see toArray() - */ - public function fields() - { - $fields = $this->attributes(); - - return array_combine($fields, $fields); - } - - /** - * Returns an iterator for traversing the attributes in the model. - * This method is required by the interface [[\IteratorAggregate]]. - * @return ArrayIterator an iterator for traversing the items in the list. - */ - public function getIterator() - { - $attributes = $this->getAttributes(); - return new ArrayIterator($attributes); - } - - /** - * Returns whether there is an element at the specified offset. - * This method is required by the SPL interface [[\ArrayAccess]]. - * It is implicitly called when you use something like `isset($model[$offset])`. - * @param mixed $offset the offset to check on. - * @return bool whether or not an offset exists. - */ - public function offsetExists($offset) - { - return isset($this->$offset); - } - - /** - * Returns the element at the specified offset. - * This method is required by the SPL interface [[\ArrayAccess]]. - * It is implicitly called when you use something like `$value = $model[$offset];`. - * @param mixed $offset the offset to retrieve element. - * @return mixed the element at the offset, null if no element is found at the offset - */ - public function offsetGet($offset) - { - return $this->$offset; - } - - /** - * Sets the element at the specified offset. - * This method is required by the SPL interface [[\ArrayAccess]]. - * It is implicitly called when you use something like `$model[$offset] = $item;`. - * @param int $offset the offset to set element - * @param mixed $item the element value - */ - public function offsetSet($offset, $item) - { - $this->$offset = $item; - } - - /** - * Sets the element value at the specified offset to null. - * This method is required by the SPL interface [[\ArrayAccess]]. - * It is implicitly called when you use something like `unset($model[$offset])`. - * @param mixed $offset the offset to unset element - */ - public function offsetUnset($offset) - { - $this->$offset = null; - } -} - diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Record.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Record.php.test deleted file mode 100644 index 449998a66b..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/Record.php.test +++ /dev/null @@ -1,21 +0,0 @@ - - * @since 2.0.13 - * @see StaticInstanceTrait - */ -interface StaticInstanceInterface -{ - /** - * Returns static class instance, which can be used to obtain meta information. - * @param bool $refresh whether to re-create static instance even, if it is already cached. - * @return static class instance. - */ - public static function instance($refresh = false); -} - diff --git a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/StaticInstanceTrait.php.test b/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/StaticInstanceTrait.php.test deleted file mode 100644 index 2b89e77d35..0000000000 --- a/lib/WorseReflection/Tests/Benchmarks/fixtures/yii/StaticInstanceTrait.php.test +++ /dev/null @@ -1,41 +0,0 @@ - - * @since 2.0.13 - */ -trait StaticInstanceTrait -{ - /** - * @var static[] static instances in format: `[className => object]` - */ - private static $_instances = []; - - - /** - * Returns static class instance, which can be used to obtain meta information. - * @param bool $refresh whether to re-create static instance even, if it is already cached. - * @return static class instance. - */ - public static function instance($refresh = false) - { - $className = get_called_class(); - if ($refresh || !isset(self::$_instances[$className])) { - self::$_instances[$className] = Yii::createObject($className); - } - return self::$_instances[$className]; - } -} diff --git a/lib/WorseReflection/Tests/Inference/SelfTest.php b/lib/WorseReflection/Tests/Inference/SelfTest.php deleted file mode 100644 index 8eb6633739..0000000000 --- a/lib/WorseReflection/Tests/Inference/SelfTest.php +++ /dev/null @@ -1,62 +0,0 @@ -build(); - $reflector = $this->createBuilder($source)->enableCache()->build(); - $reflector->reflectOffset($source, mb_strlen($source)); - - // the wrAssertType function in the source code will cause - // an exception to be thrown if it fails - $this->addToAssertionCount(1); - } - - /** - * @return Generatormixed - */ - public static function provideSelf(): Generator - { - foreach ((array)glob(__DIR__ . '/*/*.test') as $fname) { - $dirName = basename(dirname((string)$fname)); - if (in_array($dirName, self::DISABLED_TESTS)) { - continue; - } - yield $dirName .' ' . basename((string)$fname) => [ - $fname - ]; - } - } - - private static function shouldSkip(string $path): bool - { - if (!preg_match('{php-([0-9]+\.[0-9]+\.[0-9]+)-}', $path, $matches)) { - return false; - } - - return version_compare(phpversion(), $matches[1], 'lt'); - } -} diff --git a/lib/WorseReflection/Tests/Inference/anonymous_function/as_closure.test b/lib/WorseReflection/Tests/Inference/anonymous_function/as_closure.test deleted file mode 100644 index 253f73e3df..0000000000 --- a/lib/WorseReflection/Tests/Inference/anonymous_function/as_closure.test +++ /dev/null @@ -1,3 +0,0 @@ - null); diff --git a/lib/WorseReflection/Tests/Inference/arrow_function/as_closure_with_args.test b/lib/WorseReflection/Tests/Inference/arrow_function/as_closure_with_args.test deleted file mode 100644 index 6243d3385b..0000000000 --- a/lib/WorseReflection/Tests/Inference/arrow_function/as_closure_with_args.test +++ /dev/null @@ -1,3 +0,0 @@ - 'foo'); diff --git a/lib/WorseReflection/Tests/Inference/arrow_function/parameter.test b/lib/WorseReflection/Tests/Inference/arrow_function/parameter.test deleted file mode 100644 index f7c4b295e8..0000000000 --- a/lib/WorseReflection/Tests/Inference/arrow_function/parameter.test +++ /dev/null @@ -1,3 +0,0 @@ - wrAssertType('string', $foo); diff --git a/lib/WorseReflection/Tests/Inference/arrow_function/parameter2.test b/lib/WorseReflection/Tests/Inference/arrow_function/parameter2.test deleted file mode 100644 index dbb13d97d8..0000000000 --- a/lib/WorseReflection/Tests/Inference/arrow_function/parameter2.test +++ /dev/null @@ -1,8 +0,0 @@ - $this->convert(wrAssertType('Foo\TypeNode', $node)), - $parameters -); diff --git a/lib/WorseReflection/Tests/Inference/arrow_function/parameter3.test b/lib/WorseReflection/Tests/Inference/arrow_function/parameter3.test deleted file mode 100644 index c259eda0e5..0000000000 --- a/lib/WorseReflection/Tests/Inference/arrow_function/parameter3.test +++ /dev/null @@ -1,9 +0,0 @@ - $this->convert(wrAssertType('"hello"', $foo)), - $parameters -); diff --git a/lib/WorseReflection/Tests/Inference/assignment/array1.test b/lib/WorseReflection/Tests/Inference/assignment/array1.test deleted file mode 100644 index dc67898190..0000000000 --- a/lib/WorseReflection/Tests/Inference/assignment/array1.test +++ /dev/null @@ -1,22 +0,0 @@ - [ - 'hello' => 'world', - ], - 'bar' => 'baz', -]; -wrAssertType('array{foo:array{hello:"world"},bar:"baz"}', $foo); - -// list -$foo = [1, 2, 3, 4, 5]; -wrAssertType('array{1,2,3,4,5}', $foo); - -// numerical keys -$foo = [1 => "a", 2 => "b"]; -wrAssertType('array{1:"a",2:"b"}', $foo); diff --git a/lib/WorseReflection/Tests/Inference/assignment/array_2.test b/lib/WorseReflection/Tests/Inference/assignment/array_2.test deleted file mode 100644 index 14f7aa04dc..0000000000 --- a/lib/WorseReflection/Tests/Inference/assignment/array_2.test +++ /dev/null @@ -1,13 +0,0 @@ - 'one', - 'type' => 'two', -]; - -if ($node === null || !($node->getParent() instanceof NamespaceUseClause)) { - $options['class_import'] = true; - $options['name_import'] = false; -} - -wrAssertType('array{short_description:"one",type:"two"}|array{short_description:"one",type:"two",class_import:true,name_import:false}', $options); diff --git a/lib/WorseReflection/Tests/Inference/assignment/array_add.test b/lib/WorseReflection/Tests/Inference/assignment/array_add.test deleted file mode 100644 index 80ab406aac..0000000000 --- a/lib/WorseReflection/Tests/Inference/assignment/array_add.test +++ /dev/null @@ -1,6 +0,0 @@ - - */ -function bar(): Generator { -} - -$highlights = []; -foreach (bar() as $highlight) { - wrAssertType("Bar", $highlight); - $highlights[] = $highlight; -} - - -wrAssertType("Bar[]", $highlights); - -$lineCols = EfficientLineCols::fromByteOffsetInts($source, $offsets, true); -$lspHighlights = []; - -foreach ($highlights as $highlight) { - wrAssertType("Bar", $highlight); -} diff --git a/lib/WorseReflection/Tests/Inference/assignment/array_add_string.test b/lib/WorseReflection/Tests/Inference/assignment/array_add_string.test deleted file mode 100644 index 9aa8d489bb..0000000000 --- a/lib/WorseReflection/Tests/Inference/assignment/array_add_string.test +++ /dev/null @@ -1,7 +0,0 @@ -withFoo($bar); - -wrAssertType('Test\Bar', $foo); -wrAssertOffset('Test\Foo', 153); -wrAssertOffset('Test\Bar', 145); diff --git a/lib/WorseReflection/Tests/Inference/assignment/ternary_expression.test b/lib/WorseReflection/Tests/Inference/assignment/ternary_expression.test deleted file mode 100644 index 3d7389dab0..0000000000 --- a/lib/WorseReflection/Tests/Inference/assignment/ternary_expression.test +++ /dev/null @@ -1,10 +0,0 @@ -cost <= 10) ? $two : $one; - - wrAssertType('Foo|Bar', $true); -} diff --git a/lib/WorseReflection/Tests/Inference/assignment/unknown_key.test b/lib/WorseReflection/Tests/Inference/assignment/unknown_key.test deleted file mode 100644 index 5ccc777337..0000000000 --- a/lib/WorseReflection/Tests/Inference/assignment/unknown_key.test +++ /dev/null @@ -1,10 +0,0 @@ -analyser->analyse($input->getArgument(self::ARG_PATH)) as $file => $diagnostics) { - $results[$file] = 1234; -} - -wrAssertType('array<,1234>', $results); - diff --git a/lib/WorseReflection/Tests/Inference/binary-expression/arithmetic.test b/lib/WorseReflection/Tests/Inference/binary-expression/arithmetic.test deleted file mode 100644 index a58a700892..0000000000 --- a/lib/WorseReflection/Tests/Inference/binary-expression/arithmetic.test +++ /dev/null @@ -1,22 +0,0 @@ - 1]); diff --git a/lib/WorseReflection/Tests/Inference/binary-expression/bitwise.test b/lib/WorseReflection/Tests/Inference/binary-expression/bitwise.test deleted file mode 100644 index 76ac3349cb..0000000000 --- a/lib/WorseReflection/Tests/Inference/binary-expression/bitwise.test +++ /dev/null @@ -1,30 +0,0 @@ -> 2); diff --git a/lib/WorseReflection/Tests/Inference/binary-expression/compare.scalar.test b/lib/WorseReflection/Tests/Inference/binary-expression/compare.scalar.test deleted file mode 100644 index fcb3e958f0..0000000000 --- a/lib/WorseReflection/Tests/Inference/binary-expression/compare.scalar.test +++ /dev/null @@ -1,44 +0,0 @@ - 1, 'greater than'); -wrAssertType('false', 1 > 1, 'greater than'); - -wrAssertType('true', 2 >= 1, 'greater than equal'); -wrAssertType('true', 1 >= 1, 'greater than equal'); -wrAssertType('false', 0 >= 1, 'greater than equal'); - -wrAssertType('false', 2 < 1, 'less than'); -wrAssertType('true', 1 < 2, 'less than'); - -wrAssertType('false', 2 <= 1, 'less than equal'); -wrAssertType('true', 1 <= 2, 'less than equal'); -wrAssertType('true', 2 <= 2, 'less than equal'); - -// floats -wrAssertType('true', 1.0 == 1.0, 'equality'); -wrAssertType('true', 1.0 === 1.0, 'identity'); -wrAssertType('true', 1.0 === 1.0, 'identity'); - -// bool -wrAssertType('true', true == true, 'equality'); -wrAssertType('false', true === false, 'identity'); - - diff --git a/lib/WorseReflection/Tests/Inference/binary-expression/concat.test b/lib/WorseReflection/Tests/Inference/binary-expression/concat.test deleted file mode 100644 index 8a1fba9ac7..0000000000 --- a/lib/WorseReflection/Tests/Inference/binary-expression/concat.test +++ /dev/null @@ -1,9 +0,0 @@ -baz(...); -wrAssertType('Closure(string): bool', $callable); -wrAssertType('bool', $callable()); - -$callable = Foobar::boo(...); -wrAssertType('Closure(string): bool', $callable); -wrAssertType('bool', $callable()); diff --git a/lib/WorseReflection/Tests/Inference/call-expression/invoke-gh-1686.test b/lib/WorseReflection/Tests/Inference/call-expression/invoke-gh-1686.test deleted file mode 100644 index 3b89b02004..0000000000 --- a/lib/WorseReflection/Tests/Inference/call-expression/invoke-gh-1686.test +++ /dev/null @@ -1,32 +0,0 @@ - - */ - public function __invoke(): Generator - { - yield new DateTime(); - } -} - -class Baz -{ - private Foobar $foo; - - public function __construct(Foobar $foo) { - $this->foo = $foo; - } - - public function baz(): void { - $f = $this->foo; - - foreach (($this->foo)() as $bar) { - wrAssertType('DateTime', $bar); - } - } -} - diff --git a/lib/WorseReflection/Tests/Inference/call-expression/type-from-invoked-callable.test b/lib/WorseReflection/Tests/Inference/call-expression/type-from-invoked-callable.test deleted file mode 100644 index e6f5f60729..0000000000 --- a/lib/WorseReflection/Tests/Inference/call-expression/type-from-invoked-callable.test +++ /dev/null @@ -1,4 +0,0 @@ - 'string')(); -wrAssertType('string', $type); diff --git a/lib/WorseReflection/Tests/Inference/call-expression/unpacked-array-args.test b/lib/WorseReflection/Tests/Inference/call-expression/unpacked-array-args.test deleted file mode 100644 index 3565909eeb..0000000000 --- a/lib/WorseReflection/Tests/Inference/call-expression/unpacked-array-args.test +++ /dev/null @@ -1,10 +0,0 @@ -timeline instanceof Trip) { - wrAssertType('Trip', $this->timeline); - } - } -} diff --git a/lib/WorseReflection/Tests/Inference/combination/union.test b/lib/WorseReflection/Tests/Inference/combination/union.test deleted file mode 100644 index 2031616056..0000000000 --- a/lib/WorseReflection/Tests/Inference/combination/union.test +++ /dev/null @@ -1,8 +0,0 @@ -value); diff --git a/lib/WorseReflection/Tests/Inference/enum/custom_member.test b/lib/WorseReflection/Tests/Inference/enum/custom_member.test deleted file mode 100644 index d5a6e44c4d..0000000000 --- a/lib/WorseReflection/Tests/Inference/enum/custom_member.test +++ /dev/null @@ -1,18 +0,0 @@ -isA()); diff --git a/lib/WorseReflection/Tests/Inference/enum/enum_case.test b/lib/WorseReflection/Tests/Inference/enum/enum_case.test deleted file mode 100644 index decf575085..0000000000 --- a/lib/WorseReflection/Tests/Inference/enum/enum_case.test +++ /dev/null @@ -1,48 +0,0 @@ -', Foo::FOO->value); -wrAssertType('string', Foo::FOO->name); -wrAssertType('"bar"', Foo::BAR); -wrAssertType('Foo[]', Foo::cases()); - -class UnitEnumCase { - public string $name; -} - -class BackedEnumCase extends UnitEnumCase { - /** @var int|string */ - public $value; -} - -interface UnitEnum -{ - /** - * @return UnitEnumCase[] - */ - public static function cases(): array; -} - -/** - * @method static BackedEnumCase[] cases() - */ -interface BackedEnum extends UnitEnum -{ - /** - * @param int|string $value - * @return static - */ - public static function from($value): static; - - /** - * @param int|string $value - * @return static|null - */ - public static function tryFrom($value): ?static; -} - diff --git a/lib/WorseReflection/Tests/Inference/enum/enum_trait.test b/lib/WorseReflection/Tests/Inference/enum/enum_trait.test deleted file mode 100644 index af1828e208..0000000000 --- a/lib/WorseReflection/Tests/Inference/enum/enum_trait.test +++ /dev/null @@ -1,20 +0,0 @@ -foo()); - } -} diff --git a/lib/WorseReflection/Tests/Inference/enum/gh-2220.test b/lib/WorseReflection/Tests/Inference/enum/gh-2220.test deleted file mode 100644 index 2ac224d158..0000000000 --- a/lib/WorseReflection/Tests/Inference/enum/gh-2220.test +++ /dev/null @@ -1,16 +0,0 @@ -cases(); - } -} - -wrAssertType('string', Test::from('bar')->foo()); diff --git a/lib/WorseReflection/Tests/Inference/foreach/assigns_type_to_item.test b/lib/WorseReflection/Tests/Inference/foreach/assigns_type_to_item.test deleted file mode 100644 index 5850568162..0000000000 --- a/lib/WorseReflection/Tests/Inference/foreach/assigns_type_to_item.test +++ /dev/null @@ -1,9 +0,0 @@ - $items */ -$items; - -foreach ($items as $key => $item) { - wrAssertType('string', $key); -} - diff --git a/lib/WorseReflection/Tests/Inference/foreach/generic_iterator_aggregate.test b/lib/WorseReflection/Tests/Inference/foreach/generic_iterator_aggregate.test deleted file mode 100644 index f2a1ef3ff5..0000000000 --- a/lib/WorseReflection/Tests/Inference/foreach/generic_iterator_aggregate.test +++ /dev/null @@ -1,16 +0,0 @@ - - */ -final class TypeAssertions implements \IteratorAggregate {} - -function (TypeAssertions $assertions): void -{ - foreach ($assertions as $typeAssertion) { - wrAssertType('Foo\TypeAssertion', $typeAssertion); - } -} - diff --git a/lib/WorseReflection/Tests/Inference/foreach/generic_iterator_aggregate_then_foreach.test b/lib/WorseReflection/Tests/Inference/foreach/generic_iterator_aggregate_then_foreach.test deleted file mode 100644 index b85787493d..0000000000 --- a/lib/WorseReflection/Tests/Inference/foreach/generic_iterator_aggregate_then_foreach.test +++ /dev/null @@ -1,21 +0,0 @@ - - */ -final class TypeAssertions implements \IteratorAggregate {} - -function (TypeAssertions $assertions): void -{ - foreach ([ - [ $assertions, 'bar', ], - [ $assertions, 'foo', ], - ] as [ $typeAssertions, $frameVariables ]) { - foreach ($typeAssertions as $typeAssertion) { - wrAssertType('Foo\TypeAssertion', $typeAssertion); - } - } -} - diff --git a/lib/WorseReflection/Tests/Inference/foreach/gh-1708.test b/lib/WorseReflection/Tests/Inference/foreach/gh-1708.test deleted file mode 100644 index bb35beb1d9..0000000000 --- a/lib/WorseReflection/Tests/Inference/foreach/gh-1708.test +++ /dev/null @@ -1,6 +0,0 @@ - - */ - private $subjects = []; - - public function search() - { - $this->open(); - - foreach ($this->subjects as [ $recordType, $identifier ]) { - wrAssertType('string', $recordType); - } - } -} - diff --git a/lib/WorseReflection/Tests/Inference/foreach/literal_keys.test b/lib/WorseReflection/Tests/Inference/foreach/literal_keys.test deleted file mode 100644 index 33f162e45e..0000000000 --- a/lib/WorseReflection/Tests/Inference/foreach/literal_keys.test +++ /dev/null @@ -1,9 +0,0 @@ - 1, "two" => 2]; - -foreach ($items as $key => $item) { - wrAssertType('"one"|"two"', $key); -} - - diff --git a/lib/WorseReflection/Tests/Inference/foreach/literal_values.test b/lib/WorseReflection/Tests/Inference/foreach/literal_values.test deleted file mode 100644 index 6e016e9cd1..0000000000 --- a/lib/WorseReflection/Tests/Inference/foreach/literal_values.test +++ /dev/null @@ -1,9 +0,0 @@ - $array */ - public function analyse(array $array): Generator - { - $background = null; - - foreach ($array as $child) { - if ($child) { - $background = $child; - break; - } - } - - wrAssertType('?string', $background); - } -} - diff --git a/lib/WorseReflection/Tests/Inference/foreach/with_docblock.test b/lib/WorseReflection/Tests/Inference/foreach/with_docblock.test deleted file mode 100644 index 0606abfbbb..0000000000 --- a/lib/WorseReflection/Tests/Inference/foreach/with_docblock.test +++ /dev/null @@ -1,13 +0,0 @@ -foobar(); - wrAssertType('Foobar', $foobar); - } - } -} diff --git a/lib/WorseReflection/Tests/Inference/function-like/function_intersection_docblock-param.test b/lib/WorseReflection/Tests/Inference/function-like/function_intersection_docblock-param.test deleted file mode 100644 index dcfb1bfa7c..0000000000 --- a/lib/WorseReflection/Tests/Inference/function-like/function_intersection_docblock-param.test +++ /dev/null @@ -1,9 +0,0 @@ - 'hello', [10, 20, 30]), - 'arrow', -); - -wrAssertType( - 'string[]', - array_map(function (): string { - return 'hello'; - }, [10, 20, 30]), - 'anonymous' -); diff --git a/lib/WorseReflection/Tests/Inference/function/array_merge.test b/lib/WorseReflection/Tests/Inference/function/array_merge.test deleted file mode 100644 index acc81be8f1..0000000000 --- a/lib/WorseReflection/Tests/Inference/function/array_merge.test +++ /dev/null @@ -1,25 +0,0 @@ - */ -$arr1; -/** @var array */ -$arr2; - -wrAssertType( - 'array', - array_merge($arr1, $arr2, $arr1), -); - -function childNames(): array -{ - return array_merge(['methods'], [ - 'properties', - 'constants', - ]); - wrReturnType('array{"methods","properties","constants"}'); -} diff --git a/lib/WorseReflection/Tests/Inference/function/array_pop.test b/lib/WorseReflection/Tests/Inference/function/array_pop.test deleted file mode 100644 index 874fc921b6..0000000000 --- a/lib/WorseReflection/Tests/Inference/function/array_pop.test +++ /dev/null @@ -1,10 +0,0 @@ -', $reduced); - - -// we cannot currently analyze the closure to determine the -$reduced = array_reduce(['foobar'], function (array $carry, string $foo): int { - $carry[] = 'foo'; - return $carry; -}, []); - -// should be string[] but we can't currently analyze the closure frames return type -wrAssertType('array', $reduced); - -$reduced = array_reduce(['foobar'], function (int $carry, string $foo): int { -}, ''); - -wrAssertType('string', $reduced); - -$reduced = array_reduce(['foobar'], function (int $carry, string $foo): int { -}); -wrAssertType('array', $reduced); diff --git a/lib/WorseReflection/Tests/Inference/function/array_shift.test b/lib/WorseReflection/Tests/Inference/function/array_shift.test deleted file mode 100644 index 5fe88c8ad5..0000000000 --- a/lib/WorseReflection/Tests/Inference/function/array_shift.test +++ /dev/null @@ -1,13 +0,0 @@ -', array_sum()); diff --git a/lib/WorseReflection/Tests/Inference/function/assert.properties.test b/lib/WorseReflection/Tests/Inference/function/assert.properties.test deleted file mode 100644 index 5b38492adc..0000000000 --- a/lib/WorseReflection/Tests/Inference/function/assert.properties.test +++ /dev/null @@ -1,10 +0,0 @@ -foo instanceof Bar); - wrAssertType('Bar', $this->foo); - } -} diff --git a/lib/WorseReflection/Tests/Inference/function/assert.test b/lib/WorseReflection/Tests/Inference/function/assert.test deleted file mode 100644 index c053903d7b..0000000000 --- a/lib/WorseReflection/Tests/Inference/function/assert.test +++ /dev/null @@ -1,34 +0,0 @@ -', $foo); diff --git a/lib/WorseReflection/Tests/Inference/function/assert_not_object.test b/lib/WorseReflection/Tests/Inference/function/assert_not_object.test deleted file mode 100644 index 25cae6e4a3..0000000000 --- a/lib/WorseReflection/Tests/Inference/function/assert_not_object.test +++ /dev/null @@ -1,7 +0,0 @@ - $iterable */ -$iterable; - -wrAssertType('Foobar[]', iterator_to_array($iterable)); diff --git a/lib/WorseReflection/Tests/Inference/function/iterator_to_array_from_generic.test b/lib/WorseReflection/Tests/Inference/function/iterator_to_array_from_generic.test deleted file mode 100644 index 343dab96b7..0000000000 --- a/lib/WorseReflection/Tests/Inference/function/iterator_to_array_from_generic.test +++ /dev/null @@ -1,18 +0,0 @@ - - */ -function suggestions(): Generator {} - -$array = iterator_to_array(suggestions()); - -wrAssertType('Foo\Suggestion[]', iterator_to_array($array)); -wrAssertType('Foo\Suggestion', iterator_to_array($array)[0]); -wrAssertType('string', iterator_to_array($array)[0]->foo()); diff --git a/lib/WorseReflection/Tests/Inference/function/namespaced.test b/lib/WorseReflection/Tests/Inference/function/namespaced.test deleted file mode 100644 index 5be1d17d1c..0000000000 --- a/lib/WorseReflection/Tests/Inference/function/namespaced.test +++ /dev/null @@ -1,8 +0,0 @@ - - assert(is_string($f)); - wrAssertType('string', $f); -} -function t2($f) { - // int and string are narrower than - assert(is_string($f) || is_int($f)); - wrAssertType('string|int', $f); -} -function t3($f) { - assert(is_string($f) && is_int($f)); - wrAssertType('', $f, 'impossible to be string and int'); -} -function t4($f) { - assert($f instanceof Bar); - wrAssertType('Bar', $f); -} -function t5($f) { - assert($f instanceof Bar || $f instanceof Baz); - wrAssertType('Bar|Baz', $f); -} -function t6($f) { - assert($f instanceof Bar || $f instanceof Baz); - wrAssertType('Bar|Baz', $f); -} -function t7(Bar|Baz $f) { - assert($f instanceof Bar); - wrAssertType('Bar', $f); -} -function t8(Bar|Baz $f) { - assert($f instanceof Bar || $f instanceof Baz); - wrAssertType('Bar|Baz', $f); -} -function t9(Bar $f) { - assert($f instanceof Bar || $f instanceof Baz); - wrAssertType('Bar|(Bar&Baz)', $f, 'can never be Baz'); -} -function t10(Bar|Boo $f) { - assert($f instanceof Baz); - wrAssertType('(Bar&Baz)|(Boo&Baz)', $f, 'intersection'); -} -function t11(Foo $f) { - assert($f instanceof Baz && $f instanceof Boo); - wrAssertType('Foo&Baz&Boo', $f, 'intersection'); -} diff --git a/lib/WorseReflection/Tests/Inference/generator/yield.test b/lib/WorseReflection/Tests/Inference/generator/yield.test deleted file mode 100644 index b58ce3aa04..0000000000 --- a/lib/WorseReflection/Tests/Inference/generator/yield.test +++ /dev/null @@ -1,51 +0,0 @@ -'); -} - -function t1() -{ - yield 'foo'; - yield 12; - wrReturnType('Generator<"foo"|12>'); -} -function t2() -{ - yield 'string' => 'foo'; - yield 52 => 12; - wrReturnType('Generator<"string"|52,"foo"|12>'); -} -function t3() -{ - yield 'string' => 'foo'; - yield 'string' => 123; - yield 52 => 12; - wrReturnType('Generator<"string"|52,"foo"|123|12>'); -} -function t4() -{ - yield 'string' => 'foo'; - yield 'string' => 123; - yield 52 => 12; - yield 52 => new stdClass(); - wrReturnType('Generator<"string"|52,"foo"|123|12|stdClass>'); -} -function t5() -{ - yield 'foo' => [ - 'string', - new stdClass(), - ]; - yield 'bar' => [ - 'string', - new stdClass(), - ]; - yield 'baz' => [ - 'string', - new stdClass(), - ]; - wrReturnType('Generator<"foo"|"bar"|"baz",array{"string",stdClass}>'); -} diff --git a/lib/WorseReflection/Tests/Inference/generics/array_access1.test b/lib/WorseReflection/Tests/Inference/generics/array_access1.test deleted file mode 100644 index 7c51c70288..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/array_access1.test +++ /dev/null @@ -1,7 +0,0 @@ -bar()); - diff --git a/lib/WorseReflection/Tests/Inference/generics/class-string-generic-decared-interface.test b/lib/WorseReflection/Tests/Inference/generics/class-string-generic-decared-interface.test deleted file mode 100644 index 415ad94f14..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class-string-generic-decared-interface.test +++ /dev/null @@ -1,24 +0,0 @@ - $class - * @return T - */ - public function foobar($class): object; -} - - -class Foo implements FooInterface { - public function foobar($class): object - { - } -} - -$f = new Foo(); -$f = $f->foobar(Foo::class); - -wrAssertType('Foo', $f); - - diff --git a/lib/WorseReflection/Tests/Inference/generics/class-string-generic-nested-return.test b/lib/WorseReflection/Tests/Inference/generics/class-string-generic-nested-return.test deleted file mode 100644 index dd5563398d..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class-string-generic-nested-return.test +++ /dev/null @@ -1,20 +0,0 @@ - $class - * @return Bar - */ - public function foobar($class) - { - } -} - -$f = new Foo(); -$f = $f->foobar(Foo::class); - -wrAssertType('Bar', $f); - - diff --git a/lib/WorseReflection/Tests/Inference/generics/class-string-generic-union.test b/lib/WorseReflection/Tests/Inference/generics/class-string-generic-union.test deleted file mode 100644 index 531dd20f13..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class-string-generic-union.test +++ /dev/null @@ -1,19 +0,0 @@ -|null $tagFqn - * @return ($tagFqn is string ? Generator : Generator) - */ - public function tags(?string $tagFqn = null): Generator - { - } -} - -$f = new Foo(); -$f = $f->tags(Foo::class); - -wrAssertType('Generator', $f); - diff --git a/lib/WorseReflection/Tests/Inference/generics/class-string-generic.test b/lib/WorseReflection/Tests/Inference/generics/class-string-generic.test deleted file mode 100644 index c7338be05f..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class-string-generic.test +++ /dev/null @@ -1,20 +0,0 @@ - $class - * @return T - */ - public function foobar($class): object - { - } -} - -$f = new Foo(); -$f = $f->foobar(Foo::class); - -wrAssertType('Foo', $f); - - diff --git a/lib/WorseReflection/Tests/Inference/generics/class_extend1.test b/lib/WorseReflection/Tests/Inference/generics/class_extend1.test deleted file mode 100644 index 95256bc921..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class_extend1.test +++ /dev/null @@ -1,24 +0,0 @@ - - */ -class Foo extends Bar -{ -} - -$foo = new Foo(); - -wrAssertType('Baz', $foo->bar()); diff --git a/lib/WorseReflection/Tests/Inference/generics/class_extend2.test b/lib/WorseReflection/Tests/Inference/generics/class_extend2.test deleted file mode 100644 index 6c0d9e241e..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class_extend2.test +++ /dev/null @@ -1,39 +0,0 @@ - - */ -class Second extends First { - /** - * @return Y - */ - public function boo() - { - } -} - -/** - * @extends Second - */ -class Foo extends Second -{ -} - -$foo = new Foo(); - -wrAssertType('Baz', $foo->bar()); -wrAssertType('Boo', $foo->boo()); diff --git a/lib/WorseReflection/Tests/Inference/generics/class_implements_multiple1.test b/lib/WorseReflection/Tests/Inference/generics/class_implements_multiple1.test deleted file mode 100644 index e07e6e36be..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class_implements_multiple1.test +++ /dev/null @@ -1,41 +0,0 @@ -, First - */ -class Foo implements Second, First -{ - public function boo() - { - } - public function bar() - { - } -} - -$foo = new Foo(); - -wrAssertType('Boo', $foo->bar()); -wrAssertType('Baz', $foo->boo()); diff --git a/lib/WorseReflection/Tests/Inference/generics/class_implements_single1.test b/lib/WorseReflection/Tests/Inference/generics/class_implements_single1.test deleted file mode 100644 index 230c6bf936..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class_implements_single1.test +++ /dev/null @@ -1,37 +0,0 @@ - - */ -class Foo implements Second -{ - public function boo() - { - } -} - -$foo = new Foo(); - -wrAssertType('Baz', $foo->boo()); diff --git a/lib/WorseReflection/Tests/Inference/generics/class_template_extends1.test b/lib/WorseReflection/Tests/Inference/generics/class_template_extends1.test deleted file mode 100644 index b5ee07a808..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class_template_extends1.test +++ /dev/null @@ -1,39 +0,0 @@ - - */ -class Second extends First { - /** - * @return Y - */ - public function boo() - { - } -} - -/** - * @template-extends Second - */ -class Foo extends Second -{ -} - -$foo = new Foo(); - -wrAssertType('Baz', $foo->bar()); -wrAssertType('Boo', $foo->boo()); diff --git a/lib/WorseReflection/Tests/Inference/generics/class_template_implements1.test b/lib/WorseReflection/Tests/Inference/generics/class_template_implements1.test deleted file mode 100644 index 67348614d3..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/class_template_implements1.test +++ /dev/null @@ -1,37 +0,0 @@ - - */ -class Foo implements Second -{ - public function boo() - { - } -} - -$foo = new Foo(); - -wrAssertType('Baz', $foo->boo()); diff --git a/lib/WorseReflection/Tests/Inference/generics/constructor-array_arg.test b/lib/WorseReflection/Tests/Inference/generics/constructor-array_arg.test deleted file mode 100644 index 753af90b00..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/constructor-array_arg.test +++ /dev/null @@ -1,26 +0,0 @@ -a = $a; - } - - /** - * @return T - */ - public function a() - { - } -} - -$f = new Foo(['hello']); -wrAssertType('string', $f->a()); diff --git a/lib/WorseReflection/Tests/Inference/generics/constructor-generic-arg.test b/lib/WorseReflection/Tests/Inference/generics/constructor-generic-arg.test deleted file mode 100644 index b4159fd0b8..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/constructor-generic-arg.test +++ /dev/null @@ -1,29 +0,0 @@ - $a */ - public function __construct(Foobar $a) {} - - /** - * @return T - */ - public function a() - { - } -} - -$f = new Foo(new Foobar('foobar')); -wrAssertType('string', $f->a()); diff --git a/lib/WorseReflection/Tests/Inference/generics/constructor-param-and-extend.test b/lib/WorseReflection/Tests/Inference/generics/constructor-param-and-extend.test deleted file mode 100644 index c9f9ac9707..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/constructor-param-and-extend.test +++ /dev/null @@ -1,41 +0,0 @@ - - */ -class Foo extends Bar { - /** - * @var T - */ - private $a; - - /** @param T $a */ - public function __construct($a) { - $this->a = $a; - } - - /** - * @return T - */ - public function a() - { - return $this->a; - } -} - -$f = new Foo('hello'); -wrAssertType('Foo', $f); -wrAssertType('string', $f->b()); diff --git a/lib/WorseReflection/Tests/Inference/generics/constructor-params.test b/lib/WorseReflection/Tests/Inference/generics/constructor-params.test deleted file mode 100644 index f367fce1a5..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/constructor-params.test +++ /dev/null @@ -1,27 +0,0 @@ -a = $a; - } - - /** - * @return T - */ - public function a() - { - return $this->a; - } -} - -$f = new Foo('hello'); -wrAssertType('string', $f->a()); diff --git a/lib/WorseReflection/Tests/Inference/generics/generator_1.test b/lib/WorseReflection/Tests/Inference/generics/generator_1.test deleted file mode 100644 index 8b22e997aa..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/generator_1.test +++ /dev/null @@ -1,17 +0,0 @@ - - */ - public function gen(): Generator - { - } -} - -$foo = new Foobar(); - -foreach ($foo->gen() as $bar) { - wrAssertType('int', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/generator_2.test b/lib/WorseReflection/Tests/Inference/generics/generator_2.test deleted file mode 100644 index 5f60a43f6c..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/generator_2.test +++ /dev/null @@ -1,17 +0,0 @@ - - */ - public function gen(): Generator - { - } -} - -$foo = new Foobar(); - -foreach ($foo->gen() as $bar) { - wrAssertType('int', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/generator_yield_from_1.test b/lib/WorseReflection/Tests/Inference/generics/generator_yield_from_1.test deleted file mode 100644 index 5ed4179ce1..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/generator_yield_from_1.test +++ /dev/null @@ -1,15 +0,0 @@ -gen2(); - wrReturnType('Generator'); - } - /** - * @return Generator - */ - public function gen2(): Generator - { - } -} diff --git a/lib/WorseReflection/Tests/Inference/generics/generic_with_this.test b/lib/WorseReflection/Tests/Inference/generics/generic_with_this.test deleted file mode 100644 index cf541b4694..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/generic_with_this.test +++ /dev/null @@ -1,29 +0,0 @@ - - */ - public function builder() { - } -} - -wrAssertType('Parent', (new Parent())->builder()->parent()); diff --git a/lib/WorseReflection/Tests/Inference/generics/gh-1530-example.test b/lib/WorseReflection/Tests/Inference/generics/gh-1530-example.test deleted file mode 100644 index 27feae44ac..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/gh-1530-example.test +++ /dev/null @@ -1,49 +0,0 @@ - - */ - public function all(): Collection; -} - -/** - * @template TKey of array-key - * @template T - * @template-extends \IteratorAggregate - * @template-extends \ArrayAccess - */ -interface Collection extends \IteratorAggregate, \ArrayAccess -{ - /** - * @return T - */ - public function first(); - - /** - * @return Collection - */ - public function filter(\Closure $p); -} - -function foobar(Foos $foos) { - foreach ($foos->all() as $foo) { - wrAssertType('void', $foo->bar()); // OK - } - - wrAssertType('Test\Collection', $foos->all()); - wrAssertType('Test\Foo', $foos->all()->first()); - wrAssertType('void', $foos->all()->first()->bar()); - wrAssertType('Test\Collection', $foos->all()->filter(fn($foo) => $foo)); - - $filtered = $foos->all()->filter(fn($foo) => $foo); - foreach ($filtered as $foo) { - wrAssertType('void', $foo->bar()); - } -} diff --git a/lib/WorseReflection/Tests/Inference/generics/gh-1771.test b/lib/WorseReflection/Tests/Inference/generics/gh-1771.test deleted file mode 100644 index b46692f68b..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/gh-1771.test +++ /dev/null @@ -1,31 +0,0 @@ - $x */ -foo($x); - -wrAssertOffset('B', 264); diff --git a/lib/WorseReflection/Tests/Inference/generics/gh-1800.test b/lib/WorseReflection/Tests/Inference/generics/gh-1800.test deleted file mode 100644 index d689306fae..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/gh-1800.test +++ /dev/null @@ -1,52 +0,0 @@ - - */ -class ReflectionArgumentCollection extends AbstractReflectionCollection -{ -} - -/** - * @template T - * @implements ReflectionCollection - */ -abstract class AbstractReflectionCollection implements ReflectionCollection -{ -} - -/** - * @template T - * @extends IteratorAggregate - */ -interface ReflectionCollection extends IteratorAggregate -{ -} - -interface ReflectionArgument -{ -} - -interface IteratorAggregate {} - -class ReflectionNode {} -class ReflectionNodeArgumentExpression extends ReflectioNode { - public function arguments(): ReflectionArgumentCollection {} -} - -function a(): ReflectionNode {} - -$foo = a(); -if (!$foo instanceof ReflectionNodeArgumentExpression) { - throw new \Exception(); -} - -$foo = $foo->arguments(); -wrAssertType('ReflectionArgumentCollection', $foo); - -foreach ($foo as $a => $bar) { - $bar; - wrAssertType('ReflectionArgument', $bar); -} - -wrAssertOffset('ReflectionArgument', 883); diff --git a/lib/WorseReflection/Tests/Inference/generics/gh-1875.test b/lib/WorseReflection/Tests/Inference/generics/gh-1875.test deleted file mode 100644 index 0fa12d28f0..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/gh-1875.test +++ /dev/null @@ -1,28 +0,0 @@ - - */ -abstract class Test1 implements Iterator -{ -} - -/** - * @extends Test1 - */ -class Test2 extends Test1 -{ -} - -/** @var Test2 $a */ -foreach ($a as $key => $value) { - wrAssertType('string', $value); -} diff --git a/lib/WorseReflection/Tests/Inference/generics/gh-2295-test.test b/lib/WorseReflection/Tests/Inference/generics/gh-2295-test.test deleted file mode 100644 index 0f14555e6a..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/gh-2295-test.test +++ /dev/null @@ -1,49 +0,0 @@ - - */ -class ScheduleFactory extends Factory -{ - /** - * @param TModel $schedule - */ - public function __construct(Schedule $schedule) - { - } -} - -class Schedule extends Model -{ -} - -/** @extends ScheduleFactory */ -class ChildScheduleFactory extends ScheduleFactory -{ -} - -class ChildSchedule extends Schedule -{ -} - -$s = (new ScheduleFactory(new ChildSchedule()))->create(); -wrAssertType('ChildSchedule', $s); - -$s = (new ChildScheduleFactory())->create(); -wrAssertType('ChildSchedule', $s); diff --git a/lib/WorseReflection/Tests/Inference/generics/interface.test b/lib/WorseReflection/Tests/Inference/generics/interface.test deleted file mode 100644 index 5f7323fb2b..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/interface.test +++ /dev/null @@ -1,19 +0,0 @@ - - * @extends \Traversable - */ -interface ConstraintViolationListInterface extends \Traversable, \Countable, \ArrayAccess -{ -} - -function foo(ConstraintViolationListInterface $list) { - foreach ($list as $violation) { - wrAssertType('ConstraintViolationInterface', $violation); - } -} diff --git a/lib/WorseReflection/Tests/Inference/generics/iterable.test b/lib/WorseReflection/Tests/Inference/generics/iterable.test deleted file mode 100644 index e19cb2efa5..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/iterable.test +++ /dev/null @@ -1,7 +0,0 @@ - $foo */ -foreach ($foo as $bar) { - wrAssertType('int', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/iterator1.test b/lib/WorseReflection/Tests/Inference/generics/iterator1.test deleted file mode 100644 index 0c1d710454..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/iterator1.test +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class Foobar implements Iterator { -} - -$foo = new Foobar(); - -foreach ($foo as $bar) { - wrAssertType('int', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/iterator2.test b/lib/WorseReflection/Tests/Inference/generics/iterator2.test deleted file mode 100644 index 22759dd2be..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/iterator2.test +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class Foobar implements Iterator { -} - -$foo = new Foobar(); - -foreach ($foo as $bar) { - wrAssertType('string', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/iterator_aggregate1.test b/lib/WorseReflection/Tests/Inference/generics/iterator_aggregate1.test deleted file mode 100644 index 40d689983a..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/iterator_aggregate1.test +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class Foobar implements IteratorAggregate { -} - -$foo = new Foobar(); - -foreach ($foo as $bar) { - wrAssertType('int', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/iterator_aggregate2.test b/lib/WorseReflection/Tests/Inference/generics/iterator_aggregate2.test deleted file mode 100644 index 56f315ffa2..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/iterator_aggregate2.test +++ /dev/null @@ -1,14 +0,0 @@ -> - */ -class Foobar implements IteratorAggregate { -} - -$foo = new Foobar(); - -foreach ($foo as $bar) { - wrAssertType('array', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/method_generic.test b/lib/WorseReflection/Tests/Inference/generics/method_generic.test deleted file mode 100644 index 84fd825f03..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/method_generic.test +++ /dev/null @@ -1,19 +0,0 @@ -bar('hello'); -wrAssertType('string', $res); diff --git a/lib/WorseReflection/Tests/Inference/generics/method_generic_class-string-2nd-arg.test b/lib/WorseReflection/Tests/Inference/generics/method_generic_class-string-2nd-arg.test deleted file mode 100644 index e3e119a4ee..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/method_generic_class-string-2nd-arg.test +++ /dev/null @@ -1,21 +0,0 @@ - $class - * @return T - */ - public function bar(string $foo, string $class) - { - return $foo; - } -} - - -$f = new Foo(); -$res = $f->bar('Hello', B::class); -wrAssertType('B', $res); diff --git a/lib/WorseReflection/Tests/Inference/generics/method_generic_class-string-union_return.test b/lib/WorseReflection/Tests/Inference/generics/method_generic_class-string-union_return.test deleted file mode 100644 index 8c727f981a..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/method_generic_class-string-union_return.test +++ /dev/null @@ -1,24 +0,0 @@ - $foo - * @return T - */ - public function bar(string ...$foo) - { - return $foo; - } -} - - -$f = new Foo(); -$res = $f->bar(A::class, B::class); -wrAssertType('A|B', $res); -$res = $f->bar(A::class, 'B'); -wrAssertType('A|B', $res); diff --git a/lib/WorseReflection/Tests/Inference/generics/method_generic_covariant.test b/lib/WorseReflection/Tests/Inference/generics/method_generic_covariant.test deleted file mode 100644 index 1403c151cd..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/method_generic_covariant.test +++ /dev/null @@ -1,13 +0,0 @@ - $dataSource - */ - public function __construct( - private DataSourceExecutor $dataSource, - ) { - wrAssertType('DataSourceExecutor', $dataSource); - } -} diff --git a/lib/WorseReflection/Tests/Inference/generics/method_returns_collection.test b/lib/WorseReflection/Tests/Inference/generics/method_returns_collection.test deleted file mode 100644 index 965428754e..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/method_returns_collection.test +++ /dev/null @@ -1,27 +0,0 @@ - - */ -class Collection implements IteratorAggregate { -} - -/** - * @extends Collection - */ -class Foobar extends Collection { -} - -class Test{ - public function foobar(): Foobar - { - } -} - -$foo = new Test(); - -foreach ($foo->foobar() as $bar) { - wrAssertType('int', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/method_returns_collection2.test b/lib/WorseReflection/Tests/Inference/generics/method_returns_collection2.test deleted file mode 100644 index f2b0b9ed3e..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/method_returns_collection2.test +++ /dev/null @@ -1,31 +0,0 @@ - - */ -interface Collection extends IteratorAggregate { -} - -/** - * @extends Collection - */ -interface Foobar extends Collection { -} - -class Test{ - public function foobar(): Foobar - { - } -} - -$foo = new Test(); - -foreach ($foo->foobar() as $bar) { - wrAssertType('Bar\Five', $bar); -} - diff --git a/lib/WorseReflection/Tests/Inference/generics/method_returns_templated_generic.test b/lib/WorseReflection/Tests/Inference/generics/method_returns_templated_generic.test deleted file mode 100644 index e6bf574fb1..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/method_returns_templated_generic.test +++ /dev/null @@ -1,37 +0,0 @@ - - */ -interface Collection -{ - /** - * @return T - */ - public function get(); -} - -/** - * @template T - */ -class Test -{ - /** - * @param T $foo - */ - public function __construct($foo) - { - } - /** - * @return Collection - */ - public function foobar(): Collection - { - } -} - -$foo = new Test(new \stdClass()); -wrAssertType('stdClass', $foo->foobar()->get()); diff --git a/lib/WorseReflection/Tests/Inference/generics/nullable_template_param.test b/lib/WorseReflection/Tests/Inference/generics/nullable_template_param.test deleted file mode 100644 index 11cab52858..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/nullable_template_param.test +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class A extends AbstractFoo {} - -class B {} - -$a = new A(); -wrAssertType('?B', $a->foo()); diff --git a/lib/WorseReflection/Tests/Inference/generics/parameter.test b/lib/WorseReflection/Tests/Inference/generics/parameter.test deleted file mode 100644 index 0566c8816c..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/parameter.test +++ /dev/null @@ -1,26 +0,0 @@ - - */ -class Foo implements PluginInterface -{ - public function map(Mapper $mapper, object $entity, TransferInterface $transfer): void - { - wrAssertType('Bar', $transfer); - wrAssertType('Boo', $entity); - } -} diff --git a/lib/WorseReflection/Tests/Inference/generics/phpactor_reflection_collection.test b/lib/WorseReflection/Tests/Inference/generics/phpactor_reflection_collection.test deleted file mode 100644 index 42cc9434c1..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/phpactor_reflection_collection.test +++ /dev/null @@ -1,45 +0,0 @@ - - */ -interface ReflectionCollection extends \IteratorAggregate, \Countable -{ -} - -/** - * @template T of ReflectionMember - * @extends ReflectionCollection - */ -interface ReflectionMemberCollection extends ReflectionCollection -{ - /** - * @return ReflectionMemberCollection - */ - public function byName(string $name): ReflectionMemberCollection; -} - -/** - * @extends ReflectionMemberCollection - */ -interface ReflectionMethodCollection extends ReflectionMemberCollection -{ -} - -interface ReflectionClassLike -{ - public function methods(): ReflectionMethodCollection; -} - - -/** @var ReflectionClassLike $reflection */ -$reflection; -foreach ($reflection->methods()->byName('__construct') as $constructor) { - wrAssertType('Foo\ReflectionClassLike', $reflection); - wrAssertType('Foo\ReflectionMethodCollection', $reflection->methods()); - wrAssertType('Foo\ReflectionMemberCollection', $reflection->methods()->byName('foo')); - wrAssertType('Foo\ReflectionMethod', $constructor); -} diff --git a/lib/WorseReflection/Tests/Inference/generics/phpactor_reflection_of_type.test b/lib/WorseReflection/Tests/Inference/generics/phpactor_reflection_of_type.test deleted file mode 100644 index 5f0f2a2cdc..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/phpactor_reflection_of_type.test +++ /dev/null @@ -1,45 +0,0 @@ - - */ -interface ReflectionCollection extends \IteratorAggregate, \Countable -{ -} - -/** - * @template T of ReflectionMember - * @extends ReflectionCollection - */ -interface ReflectionMemberCollection extends ReflectionCollection -{ - /** - * @return ReflectionMemberCollection - */ - public function byName(string $name): ReflectionMemberCollection; - - /** - * @return ReflectionMemberCollection - */ - public function byMemberType(string $type): ReflectionMemberCollection; -} - -interface ReflectionClassLike -{ - public function members(): ReflectionMemberCollection; -} - - -/** @var ReflectionClassLike $reflection */ -$reflection; -foreach ($reflection->members()->byMemberType('fii')->byName('__construct') as $constructor) { - wrAssertType('Foo\ReflectionMemberCollection', $reflection->members()->byName('foo')->byMemberType('asd')); - wrAssertType('Foo\ReflectionClassLike', $reflection); - wrAssertType('Foo\ReflectionMemberCollection', $reflection->members()); - - - wrAssertType('Foo\ReflectionMember', $constructor); -} diff --git a/lib/WorseReflection/Tests/Inference/generics/type_from_template_in_class.test b/lib/WorseReflection/Tests/Inference/generics/type_from_template_in_class.test deleted file mode 100644 index 5c70ffbce7..0000000000 --- a/lib/WorseReflection/Tests/Inference/generics/type_from_template_in_class.test +++ /dev/null @@ -1,17 +0,0 @@ - - */ - private array $collections = []; - - public function foobar(){ - wrAssertType('ReflectionMemberCollection[]', $this->collections); - } -} - diff --git a/lib/WorseReflection/Tests/Inference/global/global_keyword.test b/lib/WorseReflection/Tests/Inference/global/global_keyword.test deleted file mode 100644 index 0e2767e6b5..0000000000 --- a/lib/WorseReflection/Tests/Inference/global/global_keyword.test +++ /dev/null @@ -1,8 +0,0 @@ - ', $foobar); - die('asd'); -} -wrAssertType('Foobar', $foobar); diff --git a/lib/WorseReflection/Tests/Inference/if-statement/bangbang.test b/lib/WorseReflection/Tests/Inference/if-statement/bangbang.test deleted file mode 100644 index 620220918e..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/bangbang.test +++ /dev/null @@ -1,8 +0,0 @@ -', $foobar); diff --git a/lib/WorseReflection/Tests/Inference/if-statement/die.test b/lib/WorseReflection/Tests/Inference/if-statement/die.test deleted file mode 100644 index c0a0693583..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/die.test +++ /dev/null @@ -1,15 +0,0 @@ -', $foobar); - die(); -} - -wrAssertType('Foobar', $foobar); diff --git a/lib/WorseReflection/Tests/Inference/if-statement/if_or.test b/lib/WorseReflection/Tests/Inference/if-statement/if_or.test deleted file mode 100644 index 2b428fd85d..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/if_or.test +++ /dev/null @@ -1,5 +0,0 @@ -', $foobar); diff --git a/lib/WorseReflection/Tests/Inference/if-statement/instanceof_removes_null.test b/lib/WorseReflection/Tests/Inference/if-statement/instanceof_removes_null.test deleted file mode 100644 index 2e25fccf71..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/instanceof_removes_null.test +++ /dev/null @@ -1,8 +0,0 @@ -', $foobar); - diff --git a/lib/WorseReflection/Tests/Inference/if-statement/multiple_statements.test b/lib/WorseReflection/Tests/Inference/if-statement/multiple_statements.test deleted file mode 100644 index 1571494ab7..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/multiple_statements.test +++ /dev/null @@ -1,25 +0,0 @@ -', $foo); diff --git a/lib/WorseReflection/Tests/Inference/if-statement/no_vars.test b/lib/WorseReflection/Tests/Inference/if-statement/no_vars.test deleted file mode 100644 index 98c5b9f792..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/no_vars.test +++ /dev/null @@ -1,8 +0,0 @@ -', $f); diff --git a/lib/WorseReflection/Tests/Inference/if-statement/nullable.test b/lib/WorseReflection/Tests/Inference/if-statement/nullable.test deleted file mode 100644 index d87e0b2047..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/nullable.test +++ /dev/null @@ -1,11 +0,0 @@ -bar instanceof Bar) { - wrAssertType('Bar', $this->bar); - } - } -} - diff --git a/lib/WorseReflection/Tests/Inference/if-statement/property_negated.test b/lib/WorseReflection/Tests/Inference/if-statement/property_negated.test deleted file mode 100644 index 7d26cc84d8..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/property_negated.test +++ /dev/null @@ -1,16 +0,0 @@ -bar instanceof Bar) { - return; - } - - wrAssertType('Bar', $this->bar); - } -} - diff --git a/lib/WorseReflection/Tests/Inference/if-statement/remove_null_type1.test b/lib/WorseReflection/Tests/Inference/if-statement/remove_null_type1.test deleted file mode 100644 index 8a79c76c42..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/remove_null_type1.test +++ /dev/null @@ -1,10 +0,0 @@ -', $foobar); -} diff --git a/lib/WorseReflection/Tests/Inference/if-statement/type_after_exception.test b/lib/WorseReflection/Tests/Inference/if-statement/type_after_exception.test deleted file mode 100644 index 802915dbca..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/type_after_exception.test +++ /dev/null @@ -1,7 +0,0 @@ -', $foobar); - diff --git a/lib/WorseReflection/Tests/Inference/if-statement/type_after_return.test b/lib/WorseReflection/Tests/Inference/if-statement/type_after_return.test deleted file mode 100644 index 4a5783a326..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/type_after_return.test +++ /dev/null @@ -1,13 +0,0 @@ -', $foobar); - diff --git a/lib/WorseReflection/Tests/Inference/if-statement/union_and_else.test b/lib/WorseReflection/Tests/Inference/if-statement/union_and_else.test deleted file mode 100644 index 2adc06999f..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/union_and_else.test +++ /dev/null @@ -1,12 +0,0 @@ -', $foobar); - diff --git a/lib/WorseReflection/Tests/Inference/if-statement/union_or_else.test b/lib/WorseReflection/Tests/Inference/if-statement/union_or_else.test deleted file mode 100644 index e3ca0f20c2..0000000000 --- a/lib/WorseReflection/Tests/Inference/if-statement/union_or_else.test +++ /dev/null @@ -1,25 +0,0 @@ -timeline instanceof Trip) { - wrAssertType('string', $this->timeline->getLinkedCustomer()); - } - } -} - diff --git a/lib/WorseReflection/Tests/Inference/member-access/class-constant-glob-array-shape.test b/lib/WorseReflection/Tests/Inference/member-access/class-constant-glob-array-shape.test deleted file mode 100644 index 88b04928c2..0000000000 --- a/lib/WorseReflection/Tests/Inference/member-access/class-constant-glob-array-shape.test +++ /dev/null @@ -1,16 +0,0 @@ -s()); - - diff --git a/lib/WorseReflection/Tests/Inference/member-access/class-constant-glob-self.test b/lib/WorseReflection/Tests/Inference/member-access/class-constant-glob-self.test deleted file mode 100644 index 2e5d84bf99..0000000000 --- a/lib/WorseReflection/Tests/Inference/member-access/class-constant-glob-self.test +++ /dev/null @@ -1,18 +0,0 @@ -s()); - - diff --git a/lib/WorseReflection/Tests/Inference/member-access/class-constant-typed.test b/lib/WorseReflection/Tests/Inference/member-access/class-constant-typed.test deleted file mode 100644 index f39edee3b0..0000000000 --- a/lib/WorseReflection/Tests/Inference/member-access/class-constant-typed.test +++ /dev/null @@ -1,12 +0,0 @@ -onTraitOne()); -wrAssertType('string', $one->onTraitTwo()); -wrAssertType('int', $one->onTraitThree()); diff --git a/lib/WorseReflection/Tests/Inference/narrowing/function-narrow.test b/lib/WorseReflection/Tests/Inference/narrowing/function-narrow.test deleted file mode 100644 index 8bd40c4f6d..0000000000 --- a/lib/WorseReflection/Tests/Inference/narrowing/function-narrow.test +++ /dev/null @@ -1,14 +0,0 @@ - $expected - * @psalm-assert ExpectedType $actual - */ - public static function assertFoobar(string $expected, object $actual): void - { - } -} - -function foo(object $obj): void -{ - Assert::assertFoobar('Foobar', $obj); - wrAssertType('Foobar', $obj); -} diff --git a/lib/WorseReflection/Tests/Inference/narrowing/narrow-negate.test b/lib/WorseReflection/Tests/Inference/narrowing/narrow-negate.test deleted file mode 100644 index d605c4d7da..0000000000 --- a/lib/WorseReflection/Tests/Inference/narrowing/narrow-negate.test +++ /dev/null @@ -1,17 +0,0 @@ -bar()); diff --git a/lib/WorseReflection/Tests/Inference/null-coalesce/null-coalesce_null.test b/lib/WorseReflection/Tests/Inference/null-coalesce/null-coalesce_null.test deleted file mode 100644 index ae9dca23af..0000000000 --- a/lib/WorseReflection/Tests/Inference/null-coalesce/null-coalesce_null.test +++ /dev/null @@ -1,7 +0,0 @@ -bar); - diff --git a/lib/WorseReflection/Tests/Inference/pipe-operator/pipe-operator.test b/lib/WorseReflection/Tests/Inference/pipe-operator/pipe-operator.test deleted file mode 100644 index 11f3023d8e..0000000000 --- a/lib/WorseReflection/Tests/Inference/pipe-operator/pipe-operator.test +++ /dev/null @@ -1,17 +0,0 @@ - trim(...) - |> strtolower(...) - |> (fn ($str) => new \stdClass()); - -wrAssertType('stdClass', $slug); - -function strtolower(): string {} -$slug = $title - |> trim(...) - |> strtolower(...); - -wrAssertType('string', $slug); diff --git a/lib/WorseReflection/Tests/Inference/postfix-update/decrement.test b/lib/WorseReflection/Tests/Inference/postfix-update/decrement.test deleted file mode 100644 index fd19d9bc87..0000000000 --- a/lib/WorseReflection/Tests/Inference/postfix-update/decrement.test +++ /dev/null @@ -1,4 +0,0 @@ - $this->foo . ($this->modified ? ' (modified)' : ''); - set(string $value) { - wrAssertType('Example', $this); - wrAssertType('string', $value); - wrAssertType('', $modified); - - } - } -} - -wrAssertType('string', (new Example())->foo); diff --git a/lib/WorseReflection/Tests/Inference/property-hooks/property-get-body.test b/lib/WorseReflection/Tests/Inference/property-hooks/property-get-body.test deleted file mode 100644 index 38047b913a..0000000000 --- a/lib/WorseReflection/Tests/Inference/property-hooks/property-get-body.test +++ /dev/null @@ -1,12 +0,0 @@ -foobar); diff --git a/lib/WorseReflection/Tests/Inference/property-hooks/property-set.test b/lib/WorseReflection/Tests/Inference/property-hooks/property-set.test deleted file mode 100644 index 82112737dd..0000000000 --- a/lib/WorseReflection/Tests/Inference/property-hooks/property-set.test +++ /dev/null @@ -1,16 +0,0 @@ -foo = strtolower($value); - $this->modified = true; - wrAssertType('string', $value); - } - } -} - -wrAssertType('string', (new Example())->foo); diff --git a/lib/WorseReflection/Tests/Inference/qualified-name/function-fallback-to-global.test b/lib/WorseReflection/Tests/Inference/qualified-name/function-fallback-to-global.test deleted file mode 100644 index 8577d42419..0000000000 --- a/lib/WorseReflection/Tests/Inference/qualified-name/function-fallback-to-global.test +++ /dev/null @@ -1,11 +0,0 @@ -', Timer); - - diff --git a/lib/WorseReflection/Tests/Inference/reflection/circular-dependency-trait.test b/lib/WorseReflection/Tests/Inference/reflection/circular-dependency-trait.test deleted file mode 100644 index 96bc31f9da..0000000000 --- a/lib/WorseReflection/Tests/Inference/reflection/circular-dependency-trait.test +++ /dev/null @@ -1,19 +0,0 @@ -$name(...$arguments); - } -} - -$b = new B(); -wrAssertType('void', $b->doB()); -wrAssertType('string', $b->doA()); diff --git a/lib/WorseReflection/Tests/Inference/reflection/mixin_generic.test b/lib/WorseReflection/Tests/Inference/reflection/mixin_generic.test deleted file mode 100644 index 89f760fa9d..0000000000 --- a/lib/WorseReflection/Tests/Inference/reflection/mixin_generic.test +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class B -{ -} - -$b = new B(); -wrAssertType('bool', $b->toBeTrue()); diff --git a/lib/WorseReflection/Tests/Inference/reflection/mixin_properties.test b/lib/WorseReflection/Tests/Inference/reflection/mixin_properties.test deleted file mode 100644 index 9ed03a6466..0000000000 --- a/lib/WorseReflection/Tests/Inference/reflection/mixin_properties.test +++ /dev/null @@ -1,16 +0,0 @@ -foo); diff --git a/lib/WorseReflection/Tests/Inference/reflection/mixin_recursive.test b/lib/WorseReflection/Tests/Inference/reflection/mixin_recursive.test deleted file mode 100644 index 441fefa91b..0000000000 --- a/lib/WorseReflection/Tests/Inference/reflection/mixin_recursive.test +++ /dev/null @@ -1,26 +0,0 @@ -doA()); diff --git a/lib/WorseReflection/Tests/Inference/reflection/mixin_static.test b/lib/WorseReflection/Tests/Inference/reflection/mixin_static.test deleted file mode 100644 index d772940873..0000000000 --- a/lib/WorseReflection/Tests/Inference/reflection/mixin_static.test +++ /dev/null @@ -1,23 +0,0 @@ -doA()); diff --git a/lib/WorseReflection/Tests/Inference/reflection/multiple_mixins.test b/lib/WorseReflection/Tests/Inference/reflection/multiple_mixins.test deleted file mode 100644 index c323b2f58d..0000000000 --- a/lib/WorseReflection/Tests/Inference/reflection/multiple_mixins.test +++ /dev/null @@ -1,37 +0,0 @@ -$name(...$arguments); - } -} - -$b = new B(); -wrAssertType('int', $b->doC()); -wrAssertType('void', $b->doB()); -wrAssertType('string', $b->doA()); diff --git a/lib/WorseReflection/Tests/Inference/reflection/promoted_property_with_params.test b/lib/WorseReflection/Tests/Inference/reflection/promoted_property_with_params.test deleted file mode 100644 index 04bbb6e596..0000000000 --- a/lib/WorseReflection/Tests/Inference/reflection/promoted_property_with_params.test +++ /dev/null @@ -1,17 +0,0 @@ - $tags - */ - public function __construct( - public Location $location = new Location(), - public array $tags = [], - ) {} - - public function bar() - { - wrAssertType('array', $this->tags); - } -} - diff --git a/lib/WorseReflection/Tests/Inference/reflection/self-referencing-constant.test b/lib/WorseReflection/Tests/Inference/reflection/self-referencing-constant.test deleted file mode 100644 index 4e4ae52885..0000000000 --- a/lib/WorseReflection/Tests/Inference/reflection/self-referencing-constant.test +++ /dev/null @@ -1,5 +0,0 @@ - sendMessage(string $text) - */ -class Mailer -{ -} - -wrAssertType('Promise', Mailer::sendMessage('foo')); diff --git a/lib/WorseReflection/Tests/Inference/require_and_include/foo.php b/lib/WorseReflection/Tests/Inference/require_and_include/foo.php deleted file mode 100644 index 0fd1c8df50..0000000000 --- a/lib/WorseReflection/Tests/Inference/require_and_include/foo.php +++ /dev/null @@ -1,7 +0,0 @@ -'); -} - -function bar() -{ -} diff --git a/lib/WorseReflection/Tests/Inference/return-statement/multiple_return.test b/lib/WorseReflection/Tests/Inference/return-statement/multiple_return.test deleted file mode 100644 index 4280d99fc6..0000000000 --- a/lib/WorseReflection/Tests/Inference/return-statement/multiple_return.test +++ /dev/null @@ -1,13 +0,0 @@ - - */ -$arr = ['hello' => 'world']; - -/** @return array{name: string, data: list} */ -function data() -{ - // ... -} - - -$data = data(); - -$list = $data['data']; -wrAssertType('array', $list); diff --git a/lib/WorseReflection/Tests/Inference/ternary_expression/for_missing.test b/lib/WorseReflection/Tests/Inference/ternary_expression/for_missing.test deleted file mode 100644 index ec8edfab57..0000000000 --- a/lib/WorseReflection/Tests/Inference/ternary_expression/for_missing.test +++ /dev/null @@ -1,3 +0,0 @@ -|stdClass', $barfoo ?: new \stdClass()); diff --git a/lib/WorseReflection/Tests/Inference/ternary_expression/use_if_branch_if_truthy.test b/lib/WorseReflection/Tests/Inference/ternary_expression/use_if_branch_if_truthy.test deleted file mode 100644 index 63e799c58f..0000000000 --- a/lib/WorseReflection/Tests/Inference/ternary_expression/use_if_branch_if_truthy.test +++ /dev/null @@ -1,7 +0,0 @@ -bar()); - } - - - /** - * @return FooBar - */ - public function bar(): array - { - } -} diff --git a/lib/WorseReflection/Tests/Inference/type-alias/psalm-type-alias.test b/lib/WorseReflection/Tests/Inference/type-alias/psalm-type-alias.test deleted file mode 100644 index aa83e17f4e..0000000000 --- a/lib/WorseReflection/Tests/Inference/type-alias/psalm-type-alias.test +++ /dev/null @@ -1,24 +0,0 @@ -bar()); - } - - - /** - * @return FooBar - */ - public function bar(): array - { - } -} diff --git a/lib/WorseReflection/Tests/Inference/type/arrayshape.test b/lib/WorseReflection/Tests/Inference/type/arrayshape.test deleted file mode 100644 index 9fba3284f9..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/arrayshape.test +++ /dev/null @@ -1,12 +0,0 @@ -', $bar); -wrAssertType('string', $bar::demo()); diff --git a/lib/WorseReflection/Tests/Inference/type/class-string.test b/lib/WorseReflection/Tests/Inference/type/class-string.test deleted file mode 100644 index ab9f28cc87..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/class-string.test +++ /dev/null @@ -1,5 +0,0 @@ -', $f); diff --git a/lib/WorseReflection/Tests/Inference/type/closure.test b/lib/WorseReflection/Tests/Inference/type/closure.test deleted file mode 100644 index 87282eb1f3..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/closure.test +++ /dev/null @@ -1,8 +0,0 @@ -|string $id - * @return ($id is class-string ? T : mixed) - */ - public function get($id); -} - -function foo(Container $container): void -{ - $map = $container->get(Foobar::class); - - wrAssertType('Bang\Foobar', $map); -} diff --git a/lib/WorseReflection/Tests/Inference/type/conditional-type-nested.test b/lib/WorseReflection/Tests/Inference/type/conditional-type-nested.test deleted file mode 100644 index 095e5e710a..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/conditional-type-nested.test +++ /dev/null @@ -1,17 +0,0 @@ -map('foo', 'bar'); - - wrAssertType('float', $map); -} diff --git a/lib/WorseReflection/Tests/Inference/type/conditional-type-nullable.test b/lib/WorseReflection/Tests/Inference/type/conditional-type-nullable.test deleted file mode 100644 index a538de0e33..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/conditional-type-nullable.test +++ /dev/null @@ -1,22 +0,0 @@ - - * ? int - * : ($array is array - * ? float - * : float|int - * ) - * ) - */ -function array_some(array $array) {} - -wrAssertType('float|int', array_some([])); -wrAssertType('int', array_some([1])); -wrAssertType('float', array_some([1.2])); diff --git a/lib/WorseReflection/Tests/Inference/type/conditional-type.test b/lib/WorseReflection/Tests/Inference/type/conditional-type.test deleted file mode 100644 index 21585023ec..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/conditional-type.test +++ /dev/null @@ -1,28 +0,0 @@ - $signature - * @param mixed $source - * @return ( - * $signature is class-string - * ? T - * : mixed - * ) - * - * @throws MappingError - */ - public function map(string $signature, $source); -} - -function foo(TreeMapper $mapper) { - $map = $mapper->map('foo', 'bar'); - wrAssertType('mixed', $map); - - $map = $mapper->map(Foo::class, 'bar'); - wrAssertType('Foo', $map); -} diff --git a/lib/WorseReflection/Tests/Inference/type/conditional-type2.test b/lib/WorseReflection/Tests/Inference/type/conditional-type2.test deleted file mode 100644 index 4fe3dd73d7..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/conditional-type2.test +++ /dev/null @@ -1,17 +0,0 @@ -map('foo', 'bar'); - - wrAssertType('string', $map); -} diff --git a/lib/WorseReflection/Tests/Inference/type/conditional-type3.test b/lib/WorseReflection/Tests/Inference/type/conditional-type3.test deleted file mode 100644 index 8df220d9a4..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/conditional-type3.test +++ /dev/null @@ -1,17 +0,0 @@ -map('bar', 'bar'); - - wrAssertType('int', $map); -} diff --git a/lib/WorseReflection/Tests/Inference/type/false.test b/lib/WorseReflection/Tests/Inference/type/false.test deleted file mode 100644 index 40c9ce0a0b..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/false.test +++ /dev/null @@ -1,17 +0,0 @@ - $minMax - * @param int<1,max> $max - * @param int $min - * @param int<1, 2> $range - */ -function foo(int $minMax, int $max, int $min, int $range) { - wrAssertType('int', $minMax); - wrAssertType('int', $min); - wrAssertType('int<1, max>', $max); - wrAssertType('int<1, 2>', $range); -} - - diff --git a/lib/WorseReflection/Tests/Inference/type/list.test b/lib/WorseReflection/Tests/Inference/type/list.test deleted file mode 100644 index 3647372232..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/list.test +++ /dev/null @@ -1,17 +0,0 @@ - $foo - */ -function listWithArgument(array $foo): void -{ - wrAssertType('array', $foo); -} - -/** - * @param list $foo - */ -function listWithoutArgument(array $foo): void -{ - wrAssertType('array', $foo); -} diff --git a/lib/WorseReflection/Tests/Inference/type/never.test b/lib/WorseReflection/Tests/Inference/type/never.test deleted file mode 100644 index 66653fef7d..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/never.test +++ /dev/null @@ -1,16 +0,0 @@ -baz()); diff --git a/lib/WorseReflection/Tests/Inference/type/static_context.test b/lib/WorseReflection/Tests/Inference/type/static_context.test deleted file mode 100644 index 3eac72c209..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/static_context.test +++ /dev/null @@ -1,8 +0,0 @@ -baz()); diff --git a/lib/WorseReflection/Tests/Inference/type/string-literal.test b/lib/WorseReflection/Tests/Inference/type/string-literal.test deleted file mode 100644 index 67e6b0f918..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/string-literal.test +++ /dev/null @@ -1,2 +0,0 @@ -$a = "abc"; -wrAssertType('"abc"', $a); diff --git a/lib/WorseReflection/Tests/Inference/type/union_from_relative_docblock.test b/lib/WorseReflection/Tests/Inference/type/union_from_relative_docblock.test deleted file mode 100644 index 21094d2af3..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/union_from_relative_docblock.test +++ /dev/null @@ -1,11 +0,0 @@ -typeDeclarationList); diff --git a/lib/WorseReflection/Tests/Inference/type/variadic.test b/lib/WorseReflection/Tests/Inference/type/variadic.test deleted file mode 100644 index 17372c42c1..0000000000 --- a/lib/WorseReflection/Tests/Inference/type/variadic.test +++ /dev/null @@ -1,5 +0,0 @@ -reflectClass('ClassOne'); -$methods = $reflection->methods(); -$method = $methods->get('foobar'); -wrAssertType('Wr\Reflector', $reflector); -wrAssertType('Wr\ReflectionClass', $reflection); -wrAssertType('Wr\MethodCollection', $methods); -wrAssertType('Wr\ReflectionMethod', $method); - diff --git a/lib/WorseReflection/Tests/Inference/virtual_member/method2.test b/lib/WorseReflection/Tests/Inference/virtual_member/method2.test deleted file mode 100644 index 517e6fd516..0000000000 --- a/lib/WorseReflection/Tests/Inference/virtual_member/method2.test +++ /dev/null @@ -1,41 +0,0 @@ - - */ -interface MethodCollection extends MemberCollection -{ -} - -class Reflector -{ - public function reflectClass(string $name): ReflectionClass; -} - -/** @var Reflector $reflector */ -$reflector; - -$reflection = $reflector->reflectClass('ClassOne'); -$methods = $reflection->methods(); -$method = $methods->get('foobar'); -wrAssertType('Wr\Reflector', $reflector); -wrAssertType('Wr\ReflectionMethod', $method); - diff --git a/lib/WorseReflection/Tests/Inference/virtual_member/method_and_property_with_same_name.test b/lib/WorseReflection/Tests/Inference/virtual_member/method_and_property_with_same_name.test deleted file mode 100644 index a62c2f9901..0000000000 --- a/lib/WorseReflection/Tests/Inference/virtual_member/method_and_property_with_same_name.test +++ /dev/null @@ -1,10 +0,0 @@ -foo()); diff --git a/lib/WorseReflection/Tests/Inference/virtual_member/property.test b/lib/WorseReflection/Tests/Inference/virtual_member/property.test deleted file mode 100644 index b839fd16e8..0000000000 --- a/lib/WorseReflection/Tests/Inference/virtual_member/property.test +++ /dev/null @@ -1,11 +0,0 @@ -get); diff --git a/lib/WorseReflection/Tests/Inference/virtual_member/trait_method1.test b/lib/WorseReflection/Tests/Inference/virtual_member/trait_method1.test deleted file mode 100644 index f151397f9b..0000000000 --- a/lib/WorseReflection/Tests/Inference/virtual_member/trait_method1.test +++ /dev/null @@ -1,16 +0,0 @@ -sayHello()); - } -} diff --git a/lib/WorseReflection/Tests/Inference/virtual_member/virtual-method-returns-static.test b/lib/WorseReflection/Tests/Inference/virtual_member/virtual-method-returns-static.test deleted file mode 100644 index 6c8642a0d2..0000000000 --- a/lib/WorseReflection/Tests/Inference/virtual_member/virtual-method-returns-static.test +++ /dev/null @@ -1,18 +0,0 @@ -sendMessage('foo')); - -$coolMailer = new CoolMailer(); -wrAssertType('CoolMailer', $coolMailer->sendMessage('foo')); diff --git a/lib/WorseReflection/Tests/Inference/virtual_member/virtual-method-returns-this.test b/lib/WorseReflection/Tests/Inference/virtual_member/virtual-method-returns-this.test deleted file mode 100644 index 0379befc88..0000000000 --- a/lib/WorseReflection/Tests/Inference/virtual_member/virtual-method-returns-this.test +++ /dev/null @@ -1,18 +0,0 @@ -sendMessage('foo')); -t ,fjk -$coolMailer = new CoolMailer(); -wrAssertType('CoolMailer', $coolMailer->sendMessage('foo')); diff --git a/lib/WorseReflection/Tests/Integration/Bridge/Composer/ComposerSourceCodeLocatorTest.php b/lib/WorseReflection/Tests/Integration/Bridge/Composer/ComposerSourceCodeLocatorTest.php deleted file mode 100644 index 0d6c515f4c..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/Composer/ComposerSourceCodeLocatorTest.php +++ /dev/null @@ -1,18 +0,0 @@ -locate(Name::fromString(__CLASS__)); - $this->assertSame(file_get_contents(__FILE__), (string) $code); - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/Phpactor/ClassToFileSourceLocatorTest.php b/lib/WorseReflection/Tests/Integration/Bridge/Phpactor/ClassToFileSourceLocatorTest.php deleted file mode 100644 index de4c17f06f..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/Phpactor/ClassToFileSourceLocatorTest.php +++ /dev/null @@ -1,40 +0,0 @@ -locator = new ClassToFileSourceLocator($classToFile); - } - - /** - * It should locate source. - */ - public function testLocator(): void - { - $source = $this->locator->locate(ClassName::fromString(__CLASS__)); - $this->assertEquals(file_get_contents(__FILE__), (string) $source); - $this->assertEquals(Path::canonicalize(__FILE__), $source->uri()->path()); - } - - /** - * It should throw an exception if class was not found. - */ - public function testLocateNotFound(): void - { - $this->expectException(SourceNotFound::class); - $this->locator->locate(ClassName::fromString('asdDSA')); - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionClassCollectionTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionClassCollectionTest.php deleted file mode 100644 index 9a9cd7a864..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionClassCollectionTest.php +++ /dev/null @@ -1,59 +0,0 @@ -createReflector($source)->reflectClassesIn(TextDocumentBuilder::create($source)->build()); - $assertion($collection); - } - - /** - * @return Generator - */ - public function provideCollection(): Generator - { - yield 'It has all the classes' => [ - <<<'EOT' - assertEquals(2, $collection->count()); - }, - ]; - yield 'It reflects nested classes' => [ - <<<'EOT' - assertEquals(1, $collection->count()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionMethodCollectionTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionMethodCollectionTest.php deleted file mode 100644 index fb5d630019..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionMethodCollectionTest.php +++ /dev/null @@ -1,67 +0,0 @@ -createReflector($source)->reflectClass('Foobar'); - $assertion = $assertion->bindTo($this); - $assertion($collection); - } - - /** - * @return Generator - */ - public static function provideCollection(): Generator - { - yield 'Get abstract methods' => [ - <<<'EOT' - assertEquals(2, $class->methods()->abstract()->count()); - }, - ]; - - yield 'Get abstract methods with virtual methods' => [ - <<<'EOT' - assertEquals(2, $class->methods()->abstract()->count()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionParameterCollectionTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionParameterCollectionTest.php deleted file mode 100644 index 8ecfc93476..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/Collection/ReflectionParameterCollectionTest.php +++ /dev/null @@ -1,44 +0,0 @@ -createReflector($source)->reflectClassesIn($source)->first()->methods()->first()->parameters(); - $assertion($collection); - } - - /** - * @return Generator - */ - public function provideCollection(): Generator - { - yield 'returns promoted parameters' => [ - <<<'EOT' - assertEquals(3, $collection->count()); - $this->assertEquals(1, $collection->notPromoted()->count()); - $this->assertEquals(2, $collection->promoted()->count()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionArgumentTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionArgumentTest.php deleted file mode 100644 index fdb17adc88..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionArgumentTest.php +++ /dev/null @@ -1,155 +0,0 @@ -reflectMethodCall(TextDocumentBuilder::create($source)->build(), $offset); - $assertion($reflection->arguments()); - } - - /** - * @return Generator - */ - public static function provideReflectionMethod():Generator - { - yield 'It guesses the name from the var name' => [ - <<<'EOT' - b<>ar($foo); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertEquals('foo', $arguments->first()->guessName()); - }, - ]; - yield 'It returns a named argument' => [ - <<<'EOT' - b<>ar(foo: 'hello'); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertEquals('"hello"', $arguments->get('foo')->type()->__toString()); - }, - ]; - yield 'It returns node context' => [ - <<<'EOT' - b<>ar(foo: 'hello'); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertInstanceOf(NodeContext::class, $arguments->get('foo')->nodeContext()); - }, - ]; - yield 'It guesses the name from return type' => [ - <<<'EOT' - b<>ar($foo->bob()); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertEquals('alice', $arguments->first()->guessName()); - }, - ]; - yield 'It returns a generated name if it cannot be determined' => [ - <<<'EOT' - b<>ar($foo->bob(), $foo->zed()); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertEquals('argument0', $arguments->first()->guessName()); - self::assertEquals('argument1', $arguments->last()->guessName()); - }, - ]; - yield 'It returns the argument type' => [ - <<<'EOT' - b<>ar($integer); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertEquals('1', (string) $arguments->first()->type()); - }, - ]; - yield 'It returns the value' => [ - <<<'EOT' - b<>ar($integer); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertEquals(1, $arguments->first()->value()); - }, - ]; - yield 'It returns the position' => [ - <<<'EOT' - b<>ar($integer); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertEquals(17, $arguments->first()->position()->start()->toInt()); - self::assertEquals(25, $arguments->first()->position()->end()->toInt()); - }, - ]; - yield 'It infers named parameters' => [ - <<<'EOT' - b<>ar(foo: $integer); - EOT - , [ - ], - function (ReflectionArgumentCollection $arguments): void { - self::assertEquals('foo', $arguments->first()->guessName()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionClassTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionClassTest.php deleted file mode 100644 index a7cb610d35..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionClassTest.php +++ /dev/null @@ -1,1188 +0,0 @@ -expectException(ClassNotFound::class); - $this->createReflector('')->reflectClassLike(ClassName::fromString('Foobar')); - } - - #[DataProvider('provideReflectionClass')] - public function testReflectClass(string $source, string $class, Closure $assertion): void - { - $class = $this->createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion->bindTo($this)->__invoke($class); - } - - /** - * @return Generator - */ - public function provideReflectionClass(): Generator - { - yield 'It reflects an empty class' => [ - <<<'EOT' - assertEquals('Foobar', (string) $class->name()->short()); - $this->assertInstanceOf(ReflectionClass::class, $class); - }, - ]; - - yield 'It reflects a class which extends another' => [ - <<<'EOT' - assertEquals('Foobar', (string) $class->name()->short()); - $this->assertEquals('Barfoo', (string) $class->parent()->name()->short()); - }, - ]; - - yield 'It reflects class constants' => [ - <<<'EOT' - assertCount(3, $class->constants()); - $this->assertInstanceOf(ReflectionConstant::class, $class->constants()->get('FOOBAR')); - $this->assertInstanceOf(ReflectionConstant::class, $class->constants()->get('EEEBAR')); - }, - ]; - - yield 'It can provide the name of its last member' => [ - <<<'EOT' - assertEquals('bar', $class->properties()->last()->name()); - }, - ]; - - yield 'It can provide the name of its first member' => [ - <<<'EOT' - assertEquals('foo', $class->properties()->first()->name()); - }, - ]; - - yield 'It can provide its position' => [ - <<<'EOT' - assertEquals(7, $class->position()->start()->toInt()); - }, - ]; - - yield 'It can provide the position of its member declarations' => [ - <<<'EOT' - assertEquals(20, $class->memberListPosition()->start()->toInt()); - }, - ]; - - yield 'It provides list of its interfaces' => [ - <<<'EOT' - assertEquals(1, $class->interfaces()->count()); - $this->assertEquals('InterfaceOne', $class->interfaces()->first()->name()); - }, - ]; - - yield 'It list of interfaces includes interfaces from parent classes' => [ - <<<'EOT' - assertEquals(1, $class->interfaces()->count()); - $this->assertEquals('InterfaceOne', $class->interfaces()->first()->name()); - }, - ]; - - yield 'It provides list of its traits' => [ - <<<'EOT' - assertEquals(2, $class->traits()->count()); - $this->assertEquals('TraitNUMBERone', $class->traits()->get('TraitNUMBERone')->name()); - $this->assertEquals('TraitNUMBERtwo', $class->traits()->get('TraitNUMBERtwo')->name()); - }, - ]; - - yield 'Traits are inherited from parent classes (?)' => [ - <<<'EOT' - assertEquals(1, $class->traits()->count()); - $this->assertEquals('TraitNUMBERone', $class->traits()->first()->name()); - }, - ]; - - yield 'Get methods includes trait methods' => [ - <<<'EOT' - assertEquals(3, $class->methods()->count()); - $this->assertTrue($class->methods()->has('traitMethod1')); - $this->assertTrue($class->methods()->has('traitMethod2')); - }, - ]; - - yield 'Tolerates not found traits' => [ - <<<'EOT' - assertEquals(1, $class->methods()->count()); - }, - ]; - - yield 'Get methods includes aliased trait methods' => [ - <<<'EOT' - assertEquals(4, $class->methods()->count()); - $this->assertTrue($class->methods()->has('one')); - $this->assertTrue($class->methods()->has('two')); - $this->assertTrue($class->methods()->has('three')); - $this->assertTrue($class->methods()->has('four')); - $this->assertEquals(Visibility::private(), $class->methods()->get('two')->visibility()); - $this->assertEquals(Visibility::protected(), $class->methods()->get('three')->visibility()); - $this->assertFalse($class->methods()->belongingTo(ClassName::fromString('Class2'))->has('two')); - $this->assertEquals('TraitOne', $class->methods()->get('two')->declaringClass()->name()->short()); - }, - ]; - - yield 'Get methods includes namespaced aliased trait methods' => [ - <<<'EOT' - assertEquals(3, $class->methods()->count()); - $this->assertTrue($class->methods()->has('one')); - $this->assertTrue($class->methods()->has('three')); - }, - ]; - - yield 'Get trait properties' => [ - <<<'EOT' - assertEquals(1, $class->properties()->count()); - $this->assertEquals('prop1', $class->properties()->first()->name()); - }, - ]; - - yield 'Get methods at offset' => [ - <<<'EOT' - assertEquals(1, $class->methods()->atOffset(27)->count()); - }, - ]; - - yield 'Get properties includes trait properties' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - }, - ]; - - yield 'Get properties for belonging to' => [ - <<<'EOT' - assertCount(1, $class->properties()->belongingTo(ClassName::fromString('Class1'))); - $this->assertCount(0, $class->properties()->belongingTo(ClassName::fromString('Class2'))); - }, - ]; - - - yield 'If it extends an interface, then ignore' => [ - <<<'EOT' - assertEquals(0, $class->methods()->count()); - }, - ]; - - - yield 'isInstanceOf returns false when it is not an instance of' => [ - <<<'EOT' - assertFalse($class->isInstanceOf(ClassName::fromString('Foobar'))); - }, - ]; - - yield 'isInstanceOf returns true for itself' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('Class2'))); - }, - ]; - - yield 'isInstanceOf returns true when it is not an instance of an interface' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('SomeInterface'))); - }, - ]; - - yield 'isInstanceOf returns true when a class implements the interface and has a parent' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('SomeInterface'))); - }, - ]; - - yield 'isInstanceOf returns true for a parent class' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('SomeParent'))); - }, - ]; - - yield 'Returns source code' => [ - <<<'EOT' - assertStringContainsString('class Class2', (string) $class->sourceCode()); - }, - ]; - - yield 'Returns imported classes' => [ - <<<'EOT' - assertEquals(NameImports::fromNames([ - 'Barfoo' => Name::fromString('Foobar\\Barfoo'), - 'Carzatz' => Name::fromString('Barfoo\\Foobaz'), - ]), $class->scope()->nameImports()); - }, - ]; - - yield 'Inherits constants from interface' => [ - <<<'EOT' - assertCount(1, $class->constants()); - $this->assertEquals('SOME_CONSTANT', $class->constants()->get('SOME_CONSTANT')->name()); - }, - ]; - - yield 'Returns all members' => [ - <<<'EOT' - assertCount(3, $class->members()); - $this->assertTrue($class->members()->has('FOOBAR')); - $this->assertTrue($class->members()->has('foobar')); - $this->assertTrue($class->members()->has('foo')); - }, - ]; - - yield 'Incomplete extends' => [ - <<<'EOT' - assertNull($class->parent()); - $this->assertEquals('Class1', $class->name()->short()); - }, - ]; - - yield 'Does not infinite loop with self-referencing class on get interfaces' => [ - <<<'EOT' - assertCount(0, $class->interfaces()); - }, - ]; - - yield 'Says if class is abstract' => [ - <<<'EOT' - assertTrue($class->isAbstract()); - }, - ]; - - yield 'Says if class is not abstract' => [ - <<<'EOT' - assertFalse($class->isAbstract()); - }, - ]; - - yield 'Says if class is final' => [ - <<<'EOT' - assertTrue($class->isFinal()); - }, - ]; - - yield 'Says if class is deprecated' => [ - <<<'EOT' - assertTrue($class->deprecation()->isDefined()); - }, - ]; - } - - #[DataProvider('provideVirtualMethods')] - public function testVirtualMethods(string $source, string $class, Closure $assertion): void - { - $class = $this->createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion->bindTo($this)->__invoke($class); - } - - /** - * @return Generator - */ - public function provideVirtualMethods(): Generator - { - yield 'virtual methods' => [ - <<<'EOT' - assertEquals(2, $class->methods()->count()); - $this->assertEquals('foobar', $class->methods()->first()->name()); - } - ]; - - yield 'virtual methods merge onto existing ones' => [ - <<<'EOT' - assertCount(1, $class->methods()); - - // originally this returned the declared type - $this->assertEquals( - 'Foobar', - $class->methods()->first()->type()->__toString(), - ); - $this->assertEquals( - 'Foobar', - $class->methods()->first()->inferredType()->__toString(), - ); - }, - ]; - - yield 'virtual methods are inherited' => [ - <<<'EOT' - assertCount(2, $class->methods()); - $this->assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods are inherited from interface' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods are inherited from multiple layers of interfaces' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods are inherited from parent class which implements interface' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - $this->assertEquals( - 'ParentInterface', - $class->methods()->get('foobar')->declaringClass()->name()->__toString() - ); - }, - ]; - - yield 'virtual method types can be relative' => [ - 'assertEquals( - 'Bosh\Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual method types can be absolute' => [ - 'assertEquals( - 'Foobar', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods of child classes override those of parents' => [ - <<<'EOT' - assertCount(2, $class->methods()); - $this->assertEquals( - 'Barfoo', - $class->methods()->get('foobar')->inferredType()->__toString() - ); - }, - ]; - - yield 'virtual methods are extracted from traits' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals('Foobar', $class->methods()->first()->inferredType()->__toString()); - }, - ]; - - yield 'virtual methods are extracted from traits of a parent class' => [ - <<<'EOT' - assertCount(1, $class->methods()); - $this->assertEquals('Foobar', $class->methods()->first()->inferredType()->__toString()); - }, - ]; - } - - #[DataProvider('provideVirtualProperties')] - public function testVirtualProperties(string $source, string $class, Closure $assertion): void - { - $class = $this->createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion->bindTo($this)->__invoke($class); - } - - /** - * @return Generator - */ - public function provideVirtualProperties(): Generator - { - yield 'virtual properties' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - } - ]; - - yield 'invalid properties' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - } - ]; - - yield 'multiple types' => [ - <<<'EOT' - assertEquals(1, $class->properties()->count()); - self::assertInstanceOf(UnionType::class, $class->properties()->first()->type()); - self::assertEquals('string|int', $class->properties()->first()->type()); - } - ]; - - yield 'virtual properties are extracted from traits' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - $this->assertEquals('Foobar', $class->properties()->first()->inferredType()->__toString()); - $this->assertEquals('barfoo', $class->properties()->last()->name()); - $this->assertEquals('Barfoo', $class->properties()->last()->inferredType()->__toString()); - } - ]; - - yield 'virtual properties are extracted from traits of a parent class' => [ - <<<'EOT' - assertEquals(2, $class->properties()->count()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - $this->assertEquals('Foobar', $class->properties()->first()->inferredType()->__toString()); - $this->assertEquals('barfoo', $class->properties()->last()->name()); - $this->assertEquals('Barfoo', $class->properties()->last()->inferredType()->__toString()); - } - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionConstantTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionConstantTest.php deleted file mode 100644 index 44085133cb..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionConstantTest.php +++ /dev/null @@ -1,216 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString($class)); - assert($class instanceof ReflectionClass); - $assertion($class->constants()); - } - /** - * @return Generator - */ - public function provideReflectionConstant(): Generator - { - yield 'Returns declaring class' => [ - <<<'EOT' - assertEquals('Foobar', $constants->get('FOOBAR')->declaringClass()->name()->__toString()); - $this->assertEquals(Visibility::public(), $constants->first()->visibility()); - } - ]; - - yield 'Returns original member' => [ - <<<'EOT' - assertEquals('Barfoo', $constants->get('FOOBAR')->original()->declaringClass()->name()->__toString()); - $this->assertEquals(Visibility::public(), $constants->first()->visibility()); - } - ]; - - yield 'Returns visibility' => [ - <<<'EOT' - assertEquals(Visibility::private(), $constants->first()->visibility()); - } - ]; - - yield 'Returns docblock' => [ - <<<'EOT' - assertStringContainsString('/** Hello! */', $constants->first()->docblock()->raw()); - } - ]; - - yield 'Returns declared type' => [ - <<<'EOT' - assertEquals('string', $constants->first()->type()->__toString()); - } - ]; - - yield 'Returns type' => [ - <<<'EOT' - assertEquals('"foobar"', $constants->first()->type()->__toString()); - } - ]; - - yield 'Doesnt return inferred retutrn types (not implemented)' => [ - <<<'EOT' - assertEquals('"foobar"', $constants->first()->inferredType()); - } - ]; - - yield 'Delimited constant list' => [ - <<<'EOT' - assertCount(2, $constants); - } - ]; - - yield 'returns value' => [ - <<<'EOT' - first(); - self::assertEquals('foobar', $constant->value()); - } - ]; - - yield 'array value' => [ - <<<'EOT' - first(); - self::assertEquals(['one', 'two'], $constant->value()); - } - ]; - - yield 'no value' => [ - <<<'EOT' - first(); - self::assertEquals(null, $constant->value()); - } - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionEnumTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionEnumTest.php deleted file mode 100644 index f2bf0c28b9..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionEnumTest.php +++ /dev/null @@ -1,175 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion($class); - } - - public function provideReflectionEnum(): Generator - { - yield 'It reflects a enum' => [ - <<<'EOT' - assertEquals('Barfoo', (string) $class->name()->short()); - $this->assertInstanceOf(ReflectionEnum::class, $class); - }, - ]; - yield 'It reflect enum methods' => [ - <<<'EOT' - assertEquals('Barfoo', (string) $class->name()->short()); - $this->assertEquals(['foobar', 'cases'], $class->methods()->keys()); - }, - ]; - yield 'Returns all members' => [ - <<<'EOT' - assertCount(4, $class->members()); - $this->assertInstanceOf(ReflectionEnumCase::class, $class->members()->get('FOOBAR')); - $this->assertInstanceOf(ReflectionMethod::class, $class->members()->get('cases')); - }, - ]; - - yield 'Return case' => [ - <<<'EOT' - cases()->get('FOOBAR'); - self::assertEquals('FOOBAR', $case->name()); - self::assertEquals('enum(Enum1::FOOBAR)', $case->type()->__toString()); - self::assertInstanceOf(MissingType::class, $case->value()); - self::assertInstanceOf(EnumCaseType::class, $case->type()); - self::assertEquals('FOOBAR', $case->name()); - self::assertFalse($class->isBacked()); - }, - ]; - yield 'Return backed case' => [ - <<<'EOT' - cases()->get('FOOBAR'); - self::assertEquals('FOOBAR', $case->name()); - self::assertEquals('"FOO"', $case->value()->__toString()); - self::assertEquals('enum(Enum1::FOOBAR)', $case->type()->__toString()); - self::assertInstanceOf(EnumBackedCaseType::class, $case->type()); - self::assertTrue($class->isBacked()); - self::assertEquals('string', $class->backedType()); - }, - ]; - yield 'Return backed case with const' => [ - <<<'EOT' - cases()->get('FOOBAR'); - self::assertEquals('FOOBAR', $case->name()); - self::assertEquals('"BAR"', $case->value()->__toString()); - self::assertEquals('enum(Enum1::FOOBAR)', $case->type()->__toString()); - self::assertInstanceOf(EnumBackedCaseType::class, $case->type()); - self::assertTrue($class->isBacked()); - self::assertEquals('string', $class->backedType()); - $const = $class->constants()->get('BAR'); - self::assertEquals('BAR', $const->value()); - }, - ]; - yield 'Return backed methods' => [ - <<<'EOT' - methods()->get('from'); - self::assertTrue($class->methods()->has('cases')); - self::assertEquals('Enum1', $method->returnType()->__toString()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionFunctionTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionFunctionTest.php deleted file mode 100644 index ce19b1653c..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionFunctionTest.php +++ /dev/null @@ -1,169 +0,0 @@ -createReflector($source)->reflectFunctionsIn($source); - $assertion->bindTo($this)->__invoke($functions->get($functionName)); - } - - public function provideReflectsFunction(): Generator - { - yield 'single function with no params' => [ - <<<'EOT' - assertEquals('hello', $function->name()); - $this->assertEquals(ByteOffsetRange::fromInts(6, 26), $function->position()); - } - ]; - - yield 'function\'s frame' => [ - <<<'EOT' - assertCount(1, $function->frame()->locals()); - } - ]; - - yield 'the docblock' => [ - <<<'EOT' - assertEquals('/** Hello */', trim($function->docblock()->raw())); - } - ]; - - yield 'the declared scalar type' => [ - <<<'EOT' - assertEquals('string', $function->type()); - } - ]; - - yield 'the declared class type' => [ - <<<'EOT' - assertEquals('Foobar\Barfoo', $function->type()); - } - ]; - - yield 'the declared union type' => [ - <<<'EOT' - assertEquals('string|Foobar\Barfoo', $function->type()->__toString()); - } - ]; - yield 'unknown if nothing declared as type' => [ - <<<'EOT' - assertEquals(TypeFactory::unknown(), $function->type()); - } - ]; - - yield 'type from docblock' => [ - <<<'EOT' - assertEquals(TypeFactory::string(), $function->inferredType()); - } - ]; - - yield 'resolved type class from docblock' => [ - <<<'EOT' - assertEquals('Foo\Goodbye', $function->inferredType()->__toString()); - } - ]; - - - yield 'parameters' => [ - <<<'EOT' - assertCount(3, $function->parameters()); - $this->assertEquals('Bar\Barfoo', $function->parameters()->get('barfoo')->inferredType()); - }, - ]; - - yield 'returns the source code' => [ - <<<'EOT' - assertStringContainsString('function hello(', (string) $function->sourceCode()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionInterfaceTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionInterfaceTest.php deleted file mode 100644 index 3eaeb2320f..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionInterfaceTest.php +++ /dev/null @@ -1,232 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion($class); - } - - /** - * @return Generator - */ - public function provideReflectionInterface(): Generator - { - yield 'It reflects an interface' => [ - <<<'EOT' - assertEquals('Barfoo', (string) $class->name()->short()); - $this->assertInstanceOf(ReflectionInterface::class, $class); - }, - ]; - - yield 'It reflects a classes interfaces' => [ - <<<'EOT' - interfaces(); - $this->assertCount(2, $interfaces); - $interface = $interfaces->get('Barfoo'); - $this->assertInstanceOf(ReflectionInterface::class, $interface); - }, - ]; - - yield 'It reflects a class which implements an interface which extends other interfaces' => [ - <<<'EOT' - parents(); - $this->assertCount(2, $interfaces); - $interface = $interfaces->get('Barfoo'); - $this->assertInstanceOf(ReflectionInterface::class, $interface); - }, - ]; - - yield 'It reflects inherited methods in an interface' => [ - <<<'EOT' - assertInstanceOf(ReflectionInterface::class, $interface); - $this->assertCount(2, $interface->methods()); - }, - ]; - - yield 'It reflect interface methods' => [ - <<<'EOT' - assertEquals('Barfoo', (string) $class->name()->short()); - $this->assertEquals(['foobar'], $class->methods()->keys()); - }, - ]; - - yield 'It interface constants' => [ - <<<'EOT' - assertCount(3, $class->constants()); - $this->assertInstanceOf(ReflectionConstant::class, $class->constants()->get('FOOBAR')); - $this->assertInstanceOf(ReflectionConstant::class, $class->constants()->get('EEEBAR')); - }, - ]; - - yield 'instanceof' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('Interface2'))); - $this->assertTrue($class->isInstanceOf(ClassName::fromString('Interface1'))); - $this->assertFalse($class->isInstanceOf(ClassName::fromString('Interface3'))); - }, - ]; - - yield 'Method class is of context class, not declaration class' => [ - <<<'EOT' - assertEquals( - 'Acme\Foobar', - (string) $class->methods()->get('method1')->class()->name() - ); - $this->assertEquals( - 'Acme\Barfoo', - (string) $class->methods()->get('method1')->declaringClass()->name() - ); - }, - ]; - - yield 'Returns all members' => [ - <<<'EOT' - assertCount(2, $class->members()); - $this->assertTrue($class->members()->has('FOOBAR')); - $this->assertTrue($class->members()->has('foobar')); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodCallTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodCallTest.php deleted file mode 100644 index 6d135e268a..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodCallTest.php +++ /dev/null @@ -1,135 +0,0 @@ -createReflector($source)->reflectMethodCall($source, $offset); - $assertion($reflection); - } - - /** - * @return Generator - */ - public function provideReflectionMethod(): Generator - { - yield 'It reflects the method name' => [ - <<<'EOT' - b<>ar(); - EOT - , [ - ], - function (ReflectionMethodCall $method): void { - $this->assertEquals('bar', $method->name()); - }, - ]; - yield'It reflects a method' => [ - <<<'EOT' - b<>ar(); - EOT - , [ - ], - function (ReflectionMethodCall $method): void { - $this->assertEquals('bar', $method->name()); - }, - ]; - yield 'It returns the position' => [ - <<<'EOT' - foo->b<>ar(); - EOT - , [ - ], - function (ReflectionMethodCall $method): void { - $this->assertInstanceOf(ByteOffsetRange::class, $method->position()); - $this->assertEquals(7, $method->position()->start()->toInt()); - $this->assertEquals(21, $method->position()->end()->toInt()); - }, - ]; - yield 'It returns the containing class' => [ - <<<'EOT' - foo()->b<>ar(); - - EOT - , [ - ], - function (ReflectionMethodCall $method): void { - $this->assertInstanceOf(ByteOffsetRange::class, $method->position()); - $this->assertEquals(ClassName::fromString('BBB'), $method->class()->name()); - }, - ]; - yield 'It returns if the call is static' => [ - <<<'EOT' - ar(); - - EOT - , [ - ], - function (ReflectionMethodCall $method): void { - $this->assertInstanceOf(ByteOffsetRange::class, $method->position()); - $this->assertTrue($method->isStatic()); - $this->assertEquals(ClassName::fromString('AAA'), $method->class()->name()); - }, - ]; - yield 'It has arguments' => [ - <<<'EOT' - b<>ar($a); - - EOT - , [ - ], - function (ReflectionMethodCall $method): void { - $this->assertInstanceOf(ByteOffsetRange::class, $method->position()); - $this->assertEquals('a', $method->arguments()->first()->guessName()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodTest.php deleted file mode 100644 index 3dab9f1bd6..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodTest.php +++ /dev/null @@ -1,824 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion->bindTo($this)->__invoke($class->methods(), $this->logger()); - } - - public function provideReflectionMethod(): Generator - { - yield 'It reflects a method' => [ - <<<'EOT' - assertEquals('method', $methods->get('method')->name()); - $this->assertInstanceOf(ReflectionMethod::class, $methods->get('method')); - }, - ]; - yield 'Private visibility' => [ - <<<'EOT' - assertEquals(Visibility::private(), $methods->get('method')->visibility()); - }, - ]; - yield 'Protected visibility' => [ - <<<'EOT' - assertEquals(Visibility::protected(), $methods->get('method')->visibility()); - }, - ]; - yield 'Public visibility' => [ - <<<'EOT' - assertEquals(Visibility::public(), $methods->get('method')->visibility()); - }, - ]; - yield 'Union type' => [ - <<<'EOT' - assertEquals(new UnionType( - TypeFactory::string(), - TypeFactory::int(), - ), $methods->get('method1')->inferredType()); - }, - ]; - yield 'Return type' => [ - <<<'EOT' - assertEquals(TypeFactory::int(), $methods->get('method1')->returnType()); - $this->assertEquals(TypeFactory::string(), $methods->get('method2')->returnType()); - $this->assertEquals(TypeFactory::float(), $methods->get('method3')->returnType()); - $this->assertEquals(TypeFactory::array(), $methods->get('method4')->returnType()); - $this->assertEquals(ClassName::fromString('Test\Barfoo'), $methods->get('method5')->returnType()->name); - $this->assertEquals(ClassName::fromString('Acme\Post'), $methods->get('method6')->returnType()->name); - $this->assertEquals('self(Test\Foobar)', $methods->get('method7')->returnType()->__toString()); - $this->assertEquals(TypeFactory::iterable(), $methods->get('method8')->returnType()); - $this->assertEquals(TypeFactory::callable(), $methods->get('method9')->returnType()); - $this->assertEquals(TypeFactory::resource(), $methods->get('method10')->returnType()); - }, - ]; - yield 'Nullable return type' => [ - <<<'EOT' - assertEquals( - TypeFactory::fromString('?int'), - $methods->get('method1')->returnType() - ); - }, - ]; - yield 'Inherited methods' => [ - <<<'EOT' - assertEquals( - ['method5', 'method2', 'method3', 'method4'], - $methods->keys() - ); - self::assertEquals('Foobar', $methods->get('method5')->class()->name()->head()->__toString()); - }, - ]; - - yield 'Return type from docblock' => [ - <<<'EOT' - assertEquals( - 'Acme\Post', - $methods->get('method1')->inferredType()->__toString(), - ); - }, - ]; - - yield 'Return type from array docblock' => [ - <<<'EOT' - assertEquals( - 'Acme\Post[]', - $methods->get('method1')->inferredType()->__toString() - ); - }, - ]; - yield 'Return type from docblock this and static' => [ - <<<'EOT' - assertEquals('$this(Foobar)', $methods->get('method1')->inferredType()->__toString(), '$this(Foobar)'); - $this->assertEquals('static(Foobar)', $methods->get('method2')->inferredType()->__toString(), 'static(Foobar)'); - }, - ]; - yield 'Return type from docblock this and static from a trait' => [ - <<<'EOT' - assertEquals('$this(Foobar)', $methods->get('method1')->inferredType()->__toString()); - $this->assertEquals('static(Foobar)', $methods->get('method2')->inferredType()->__toString()); - }, - ]; - yield 'Return type from class @method annotation' => [ - <<<'EOT' - is( - $methods->get('method1')->inferredType() - ) - ); - }, - ]; - yield 'Return type from overridden @method annotation' => [ - <<<'EOT' - is( - $methods->get('method1')->inferredType() - ) - ); - }, - ]; - yield 'Return type from inherited docblock' => [ - <<<'EOT' - assertEquals('Articles\Blog', $methods->get('method1')->inferredType()->__toString()); - }, - ]; - yield 'Return type from inherited docblock (from interface)' => [ - <<<'EOT' - assertEquals('Articles\Blog', $methods->get('method1')->inferredType()->__toString()); - }, - ]; - yield 'It reflects an abstract method' => [ - <<<'EOT' - assertTrue($methods->get('method')->isAbstract()); - $this->assertFalse($methods->get('methodNonAbstract')->isAbstract()); - }, - ]; - yield 'It returns the method parameters' => [ - <<<'EOT' - assertCount(3, $methods->get('barfoo')->parameters()); - }, - ]; - yield 'It returns the nullable parameter types' => [ - <<<'EOT' - assertCount(1, $methods->get('barfoo')->parameters()); - $this->assertEquals( - '?Test\Barfoo', - $methods->get('barfoo')->parameters()->first()->type()->__toString(), - ); - }, - ]; - yield 'It tolerantes and logs method parameters with missing variables parameter' => [ - <<<'EOT' - assertEquals('', $methods->get('barfoo')->parameters()->first()->name()); - $this->assertStringContainsString( - 'Parameter has no variable', - $logger->messages()[2] - ); - }, - ]; - yield 'It returns the raw docblock' => [ - <<<'EOT' - assertStringContainsString(<<get('barfoo')->docblock()->raw()); - }, - ]; - yield 'It returns the formatted docblock' => [ - <<<'EOT' - assertEquals(<<get('barfoo')->docblock()->formatted()); - }, - ]; - yield 'It returns true if the method is static' => [ - <<<'EOT' - assertTrue($methods->get('barfoo')->isStatic()); - }, - ]; - yield 'It returns the method body' => [ - <<<'EOT' - assertEquals('echo "Hello!";', (string) $methods->get('barfoo')->body()); - }, - ]; - yield 'It reflects a method from an inteface' => [ - <<<'EOT' - assertTrue($methods->has('barfoo')); - $this->assertEquals('Foobar', (string) $methods->get('barfoo')->declaringClass()->name()); - }, - ]; - yield 'It reflects a method from a trait' => [ - <<<'EOT' - assertTrue($methods->has('barfoo')); - $this->assertEquals('Foobar', (string) $methods->get('barfoo')->declaringClass()->name()); - }, - ]; - yield 'It returns methods when a class extends itself' => [ - <<<'EOT' - assertTrue($methods->has('barfoo')); - }, - ]; - } - - /** - * Note that generics are now resolved during analysis and not statically. - * - * @return Generator - */ - public static function provideGenerics(): Generator - { - yield 'return type from generic' => [ - <<<'PHP' - - */ - class Foobar extends Generic - { - } - PHP - , - 'Foobar', - function (ReflectionMethodCollection $methods): void { - self::assertTrue($methods->has('bar')); - self::assertEquals('T', $methods->get('bar')->inferredType()->__toString()); - }, - ]; - yield 'return type from generic with multiple parameters' => [ - <<<'PHP' - - */ - class Foobar extends Generic - { - } - PHP - , - 'Foobar', - function (ReflectionMethodCollection $methods): void { - self::assertTrue($methods->has('tee')); - self::assertTrue($methods->has('vee')); - self::assertEquals('T', $methods->get('tee')->inferredType()->__toString()); - self::assertEquals('V', $methods->get('vee')->inferredType()->__toString()); - }, - ]; - yield 'return type from generic with multiple parameters at a distance' => [ - <<<'PHP' - - */ - abstract class Middle extends Generic { - /** @return G */ - public function gee() {} - } - - /** - * @extends Middle - */ - class Foobar extends Middle - { - } - PHP - , - 'Foobar', - function (ReflectionMethodCollection $methods): void { - self::assertTrue($methods->has('tee')); - self::assertTrue($methods->has('vee')); - self::assertTrue($methods->has('gee')); - self::assertEquals('T', $methods->get('tee')->inferredType()->__toString()); - self::assertEquals('V', $methods->get('vee')->inferredType()->__toString()); - self::assertEquals('G', $methods->get('gee')->inferredType()->__toString()); - }, - ]; - } - - /** - * @return Generator - */ - public function provideDeprecations(): Generator - { - yield 'It shows when method is deprecated' => [ - <<<'EOT' - assertTrue($methods->has('barfoo')); - $this->assertTrue($methods->get('barfoo')->deprecation()->isDefined()); - }, - ]; - } - - #[DataProvider('provideReflectionMethodCollection')] - public function testReflectCollection(string $source, string $class, Closure $assertion): void - { - $class = $this->createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion($class); - } - - public function provideReflectionMethodCollection(): array - { - return [ - 'Only methods belonging to a given class' => [ - <<<'EOT' - methods()->belongingTo($class->name()); - $this->assertEquals( - ['method4'], - $methods->keys() - ); - }, - ], - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionParameterTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionParameterTest.php deleted file mode 100644 index bb27d2ba40..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionParameterTest.php +++ /dev/null @@ -1,233 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString('Acme\Foobar')); - $assertion->bindTo($this)->__invoke($class->methods()->get('method')); - } - - public function provideReflectionParameter() - { - yield 'It reflects a an empty list with no parameters' => [ - '', - function (ReflectionMethod $method): void { - $this->assertCount(0, $method->parameters()); - }, - ]; - - yield 'It reflects a single parameter' => [ - '$foobar', - function (ReflectionMethod $method): void { - $this->assertCount(1, $method->parameters()); - $parameter = $method->parameters()->get('foobar'); - $this->assertInstanceOf(ReflectionParameter::class, $parameter); - $this->assertEquals('foobar', $parameter->name()); - }, - ]; - - yield 'It returns false if the parameter has no type' => [ - '$foobar', - function (ReflectionMethod $method): void { - $this->assertTrue( - $method->parameters()->get('foobar')->type() instanceof MissingType - ); - }, - ]; - - yield 'It returns the parameter type' => [ - 'Foobar $foobar', - function (ReflectionMethod $method): void { - $this->assertEquals('Acme\Foobar', $method->parameters()->get('foobar')->type()->__toString()); - }, - ]; - - yield 'It returns false if the parameter has no default' => [ - '$foobar', - function (ReflectionMethod $method): void { - $this->assertFalse($method->parameters()->get('foobar')->default()->isDefined()); - }, - ]; - - yield 'It returns the default value for a string' => [ - '$foobar = "foo"', - function (ReflectionMethod $method): void { - $this->assertTrue($method->parameters()->get('foobar')->default()->isDefined()); - $this->assertEquals( - 'foo', - $method->parameters()->get('foobar')->default()->value() - ); - }, - ]; - - yield 'It returns the default value for a number' => [ - '$foobar = 1234', - function (ReflectionMethod $method): void { - $this->assertEquals( - 1234, - $method->parameters()->get('foobar')->default()->value() - ); - }, - ]; - - yield 'It returns the default value for an array' => [ - '$foobar = [ "foobar" ]', - function (ReflectionMethod $method): void { - $this->assertEquals( - ['foobar'], - $method->parameters()->get('foobar')->default()->value() - ); - }, - ]; - - yield 'It returns the default value for null' => [ - '$foobar = null', - function (ReflectionMethod $method): void { - $this->assertEquals( - null, - $method->parameters()->get('foobar')->default()->value() - ); - }, - ]; - - yield 'It returns the default value for empty array' => [ - '$foobar = []', - function (ReflectionMethod $method): void { - $foobar = $method->parameters()->get('foobar'); - $this->assertEquals( - [], - $foobar->default()->value() - ); - }, - ]; - - yield 'It returns the default value for a boolean' => [ - '$foobar = false', - function (ReflectionMethod $method): void { - $this->assertEquals( - false, - $method->parameters()->get('foobar')->default()->value() - ); - }, - ]; - - yield 'Passed by reference' => [ - '&$foobar', - function (ReflectionMethod $method): void { - $this->assertTrue( - $method->parameters()->get('foobar')->byReference() - ); - }, - ]; - - yield 'Not passed by reference' => [ - '$foobar', - function (ReflectionMethod $method): void { - $this->assertFalse( - $method->parameters()->get('foobar')->byReference() - ); - }, - ]; - - yield 'It reflects iterable type properly' => [ - 'iterable $foobar', - function (ReflectionMethod $method): void { - $this->assertEquals( - TypeFactory::fromString('iterable'), - $method->parameters()->get('foobar')->type() - ); - }, - ]; - - yield 'It reflects resource type properly' => [ - 'resource $foobar', - function (ReflectionMethod $method): void { - $this->assertEquals( - TypeFactory::fromString('resource')->__toString(), - $method->parameters()->get('foobar')->type()->__toString() - ); - }, - ]; - - yield 'It reflects callable type properly' => [ - 'callable $foobar', - function (ReflectionMethod $method): void { - $this->assertEquals( - TypeFactory::fromString('callable'), - $method->parameters()->get('foobar')->type() - ); - }, - ]; - - yield 'It reflects a nullable parameter' => [ - '?string $foobar', - function (ReflectionMethod $method): void { - $this->assertEquals( - TypeFactory::nullable(TypeFactory::string()), - $method->parameters()->get('foobar')->type() - ); - }, - ]; - - yield 'It reflects a promoted parameter' => [ - 'private string $foobar', - function (ReflectionMethod $method): void { - $this->assertTrue( - $method->parameters()->get('foobar')->isPromoted() - ); - }, - ]; - - yield 'It reflects a (not) promoted parameter' => [ - 'string $foobar', - function (ReflectionMethod $method): void { - $this->assertFalse( - $method->parameters()->get('foobar')->isPromoted() - ); - }, - ]; - } - - #[DataProvider('provideReflectionParameterWithDocblock')] - public function testReflectParameterWithDocblock(string $source, string $docblock, Closure $assertion): void - { - $source = sprintf('createReflector($source)->reflectClassLike(ClassName::fromString('Acme\Foobar')); - $assertion->bindTo($this)->__invoke($class->methods()->get('method')); - } - - public function provideReflectionParameterWithDocblock() - { - yield 'It returns docblock parameter type' => [ - '$foobar', - '/** @param Foobar $foobar */', - function (ReflectionMethod $method): void { - $this->assertCount(1, $method->parameters()); - $this->assertEquals('Acme\Foobar', (string) $method->parameters()->get('foobar')->inferredType()); - }, - ]; - - yield 'It returns unknown type when no type hinting is available' => [ - '$foobar', - '/** */', - function (ReflectionMethod $method): void { - $this->assertCount(1, $method->parameters()); - $this->assertEquals(TypeFactory::unknown(), $method->parameters()->get('foobar')->inferredType()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionPromotedPropertyTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionPromotedPropertyTest.php deleted file mode 100644 index 7e19554e92..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionPromotedPropertyTest.php +++ /dev/null @@ -1,104 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion->bindTo($this)->__invoke($class->properties()); - } - - public function provideConsturctorPropertyPromotion(): Generator - { - yield 'Typed properties' => [ - <<<'EOT' - assertTrue($properties->get('foobar')->isPromoted()); - $this->assertEquals( - TypeFactory::string(), - $properties->get('foobar')->type() - ); - $this->assertEquals(Visibility::private(), $properties->get('foobar')->visibility()); - $this->assertEquals( - TypeFactory::int(), - $properties->get('barfoo')->type() - ); - $this->assertEquals( - TypeFactory::union( - TypeFactory::string(), - TypeFactory::int(), - ), - $properties->get('baz')->inferredType() - ); - }, - ]; - - yield 'Nullable' => [ - 'assertEquals( - TypeFactory::fromString('?string'), - $properties->get('foobar')->type() - ); - }, - ]; - - yield 'No types' => [ - 'assertEquals( - TypeFactory::undefined(), - $properties->get('foobar')->type() - ); - }, - ]; - - yield 'With docblock' => [ - <<<'EOT' - assertEquals( - 'Foobar', - $properties->get('foobar')->inferredType()->__toString(), - ); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionPropertyTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionPropertyTest.php deleted file mode 100644 index c00b62c4f1..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionPropertyTest.php +++ /dev/null @@ -1,404 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion->bindTo($this)->__invoke($class->properties()); - } - - /** - * @return Generator - */ - public function provideReflectionPropertyTypes(): Generator - { - yield 'It reflects a property with union type' => [ - 'assertEquals('property', $properties->get('property')->name()); - $this->assertEquals(TypeFactory::union(...[ - TypeFactory::int(), - TypeFactory::string(), - ]), $properties->get('property')->inferredType()); - }, - ]; - } - - /** - * @return Generator - */ - public function provideReflectionProperty() - { - yield 'It reflects a property' => [ - <<<'EOT' - assertEquals('property', $properties->get('property')->name()); - $this->assertInstanceOf(ReflectionProperty::class, $properties->get('property')); - }, - ]; - - yield 'Private visibility' => [ - <<<'EOT' - assertEquals(Visibility::private(), $properties->get('property')->visibility()); - }, - ]; - - yield 'Protected visibility' => [ - <<<'EOT' - assertEquals(Visibility::protected(), $properties->get('property')->visibility()); - }, - ]; - - yield 'Public visibility' => [ - <<<'EOT' - assertEquals(Visibility::public(), $properties->get('property')->visibility()); - }, - ]; - - yield 'Inherited properties' => [ - <<<'EOT' - assertEquals( - ['property5', 'property2', 'property3', 'property4'], - $properties->keys() - ); - self::assertEquals( - 'ParentParentClass', - $properties->get('property5')->declaringClass()->name()->head()->__toString() - ); - self::assertEquals('Foobar', $properties->get('property5')->class()->name()->head()->__toString()); - }, - ]; - - yield 'Return type from docblock' => [ - <<<'EOT' - assertEquals( - 'Acme\Post', - $properties->get('property1')->inferredType()->__toString(), - ); - $this->assertFalse($properties->get('property1')->isStatic()); - }, - ]; - - yield 'Returns unknown type for (real) type' => [ - <<<'EOT' - assertEquals( - TypeFactory::unknown(), - $properties->get('property1')->type() - ); - }, - ]; - - yield 'Property with assignment' => [ - <<<'EOT' - assertTrue($properties->has('property1')); - }, - ]; - - yield 'Return true if property is static' => [ - <<<'EOT' - assertTrue($properties->get('property1')->isStatic()); - }, - ]; - - yield 'Returns declaring class' => [ - <<<'EOT' - assertEquals('Foobar', $properties->get('property1')->declaringClass()->name()->__toString()); - }, - ]; - - yield 'Property type from class @property annotation' => [ - <<<'EOT' - assertEquals(TypeFactory::fromString('string'), $properties->get('bar')->inferredType()); - }, - ]; - - yield 'Property type from class @property annotation with imported name' => [ - <<<'EOT' - assertEquals('Bar\Foo', $properties->get('bar')->inferredType()->__toString()); - }, - ]; - - yield 'Property type from parent class @property annotation with imported name' => [ - <<<'EOT' - assertEquals('Bar\Foo', $properties->get('bar')->inferredType()->__toString()); - }, - ]; - - yield 'Typed property from imported class' => [ - <<<'EOT' - assertEquals('Bar\Foo', $properties->get('bar')->type()->__toString()); - $this->assertEquals('Bar\Foo', $properties->get('bar')->inferredType()->__toString()); - - $this->assertEquals(TypeFactory::string(), $properties->get('baz')->type()); - - $this->assertEquals(TypeFactory::undefined(), $properties->get('undefined')->type()); - - $this->assertEquals(TypeFactory::iterable(), $properties->get('collection')->type()); - $this->assertEquals('Bar\Foo[]', $properties->get('collection')->inferredType()->__toString()); - $this->assertEquals( - TypeFactory::iterable(), - $properties->get('it')->type() - ); - }, - ]; - - yield 'Nullable typed property' => [ - <<<'EOT' - assertEquals( - TypeFactory::fromString('?string'), - $properties->get('foo')->type() - ); - }, - ]; - - yield 'Property with intersection' => [ - <<<'EOT' - assertEquals( - TypeFactory::intersection( - TypeFactory::class('Test\Foo'), - TypeFactory::class('Test\Bar'), - )->__toString(), - $properties->get('foo')->type()->__toString() - ); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionScopeTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionScopeTest.php deleted file mode 100644 index 6fa7a5f043..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionScopeTest.php +++ /dev/null @@ -1,68 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion($class); - } - - public function provideScope(): Generator - { - yield 'Returns imported classes' => [ - <<<'EOT' - assertEquals(NameImports::fromNames([ - 'Barfoo' => Name::fromString('Foobar\\Barfoo'), - 'Carzatz' => Name::fromString('Barfoo\\Foobaz'), - ]), $class->scope()->nameImports()); - }, - ]; - - yield 'Returns local name' => [ - <<<'EOT' - assertEquals( - Name::fromString('Barfoo'), - $class->scope()->resolveLocalName(Name::fromString('Foobar\Barfoo')) - ); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionTraitTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionTraitTest.php deleted file mode 100644 index ca7f37e005..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionTraitTest.php +++ /dev/null @@ -1,165 +0,0 @@ -createReflector($source)->reflectClassLike(ClassName::fromString($class)); - $assertion($class); - } - - /** - * @return Generator - */ - public function provideReflectionTrait(): Generator - { - yield 'It reflects a trait' => [ - <<<'EOT' - assertEquals('Barfoo', (string) $class->name()->short()); - $this->assertInstanceOf(ReflectionTrait::class, $class); - }, - ]; - - yield 'It reflects a classes traits' => [ - <<<'EOT' - traits(); - $this->assertCount(2, $traits); - $trait = $traits->first(); - $this->assertInstanceOf(ReflectionTrait::class, $trait); - }, - ]; - - yield 'It reflect trait methods' => [ - <<<'EOT' - assertEquals('Barfoo', (string) $class->name()->short()); - $this->assertEquals(['foobar'], $class->methods()->keys()); - }, - ]; - - yield 'Trait properties' => [ - <<<'EOT' - assertCount(2, $class->properties()); - $this->assertEquals('foobar', $class->properties()->first()->name()); - }, - ]; - - yield 'Ignores inherit docs on trait' => [ - <<<'EOT' - methods()->first(); - $this->assertEquals(TypeFactory::unknown(), $method->type()); - }, - ]; - - yield 'instanceof' => [ - <<<'EOT' - assertTrue($class->isInstanceOf(ClassName::fromString('Trait1'))); - $this->assertFalse($class->isInstanceOf(ClassName::fromString('Interface1'))); - }, - ]; - - yield 'Returns all members' => [ - <<<'EOT' - assertCount(2, $class->members()); - $this->assertTrue($class->members()->has('foovar')); - $this->assertTrue($class->members()->has('foobar')); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/TraitImport/TraitImportsTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/TraitImport/TraitImportsTest.php deleted file mode 100644 index 4710e661a6..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/TraitImport/TraitImportsTest.php +++ /dev/null @@ -1,93 +0,0 @@ -parseSource($source); - $classDeclaration = $rootNode->getFirstDescendantNode(ClassDeclaration::class); - $assertion(TraitImports::forClassDeclaration($classDeclaration)); - } - - /** - * @return Generator - */ - public function provideTraitImports(): Generator - { - yield 'simple use' => [ - 'assertCount(1, $traitImports); - $this->assertTrue($traitImports->has('A')); - $this->assertEquals('A', $traitImports->get('A')->name()); - } - ]; - - yield 'incomplete statement' => [ - 'assertCount(0, $traitImports); - } - ]; - - yield 'simple use with alias' => [ - 'assertCount(1, $traitImports); - $traitImport = $traitImports->get('A'); - $this->assertCount(1, $traitImport->traitAliases()); - $traitAlias = $traitImport->traitAliases()['foo']; - assert($traitAlias instanceof TraitAlias); - $this->assertEquals('foo', $traitAlias->originalName()); - $this->assertEquals('bar', $traitAlias->newName()); - } - ]; - - yield 'simple use with alias and visiblity' => [ - 'get('A'); - ; - $this->assertEquals(Visibility::private(), $traitImport->traitAliases()['foo']->visiblity()); - $this->assertEquals(Visibility::protected(), $traitImport->traitAliases()['bar']->visiblity()); - $this->assertEquals(Visibility::public(), $traitImport->traitAliases()['zed']->visiblity()); - } - ]; - - yield 'does not support insteadof' => [ - 'get('A'); - $this->assertCount(0, $traitImport->traitAliases()); - } - ]; - - yield 'multiple traits with single alias maping' => [ - 'assertCount(2, $traitImports); - $traitImport = $traitImports->get('A'); - $this->assertCount(2, $traitImport->traitAliases()); - - $traitImport = $traitImports->get('B'); - $this->assertCount(2, $traitImport->traitAliases()); - } - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolverTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolverTest.php deleted file mode 100644 index 155d2972b0..0000000000 --- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolverTest.php +++ /dev/null @@ -1,43 +0,0 @@ -createReflector($source)->reflectClass(ClassName::fromString($class)); - $assertion($class->properties()->get('p')); - } - - public function provideResolveTypes(): Generator - { - yield 'union type' => [ - 'assertEquals(TypeFactory::union(...[ - TypeFactory::int(), - TypeFactory::string(), - ]), $property->inferredType()); - }, - ]; - - yield 'union type with FQN' => [ - 'assertEquals('int|Foobar|Baz', $property->inferredType()); - }, - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/ClassReflectorTest.php b/lib/WorseReflection/Tests/Integration/Core/ClassReflectorTest.php deleted file mode 100644 index 23871c41dd..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/ClassReflectorTest.php +++ /dev/null @@ -1,80 +0,0 @@ -createReflector($source)->$method($class); - $this->assertInstanceOf($expectedType, $reflection); - } - - /** - * @return Generator - */ - public static function provideReflectClassSuccess(): Generator - { - yield 'Class' => [ - ' [ - ' [ - 'expectException(ClassNotFound::class); - $this->expectExceptionMessage($expectedErrorMessage); - - $this->createReflector($source)->$method($class); - } - - /** - * @return Generator - */ - public static function provideReflectClassNotCorrectType(): Generator - { - yield 'Class' => [ - ' [ - ' [ - 'workspace()->reset(); - $this->workspace()->loadManifest($manifest); - - $locator = new StubSourceLocator( - ReflectorBuilder::create()->build(), - $this->workspace()->path('project'), - $this->workspace()->path('cache'), - ); - $reflection = ReflectorBuilder::create()->addLocator($locator)->build()->reflectConstant($name); - $assertion($reflection); - } - - /** - * @return Generator - */ - public function provideReflectDeclaredConstant(): Generator - { - yield 'reflect in root namespace' => [ - <<<'EOT' - // File: project/hello.php - assertEquals('"hello"', $constant->type()->__toString()); - } - ]; - - yield 'fallback to global constant' => [ - <<<'EOT' - // File: project/global.php - assertEquals('HELLO', $constant->name()); - } - ]; - - yield 'namespaced function' => [ - <<<'EOT' - // File: project/global.php - assertEquals('Foo\HELLO', $function->name()); - } - ]; - } - - public function testThrowsExceptionIfFunctionNotFound(): void - { - $this->expectException(ConstantNotFound::class); - $this->createReflector('reflectConstant('hallo'); - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/FunctionReflectorTest.php b/lib/WorseReflection/Tests/Integration/Core/FunctionReflectorTest.php deleted file mode 100644 index f33cff26e7..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/FunctionReflectorTest.php +++ /dev/null @@ -1,81 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest($manifest); - - $locator = new StubSourceLocator( - ReflectorBuilder::create()->build(), - $this->workspace()->path('project'), - $this->workspace()->path('cache'), - ); - $reflection = ReflectorBuilder::create()->addLocator($locator)->build()->reflectFunction($name); - $assertion($reflection); - } - - /** - * @return Generator - */ - public function provideReflectFunction(): Generator - { - yield 'reflect function' => [ - <<<'EOT' - // File: project/hello.php - assertEquals('hello', $function->name()); - } - ]; - - yield 'fallback to global function' => [ - <<<'EOT' - // File: project/global.php - assertEquals('hello', $function->name()); - } - ]; - - yield 'namespaced function' => [ - <<<'EOT' - // File: project/global.php - assertEquals('Foo\hello', $function->name()); - } - ]; - } - - public function testThrowsExceptionIfFunctionNotFound(): void - { - $this->expectException(FunctionNotFound::class); - $this->createReflector('reflectFunction('hallo'); - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameResolverTest.php b/lib/WorseReflection/Tests/Integration/Core/Inference/FrameResolverTest.php deleted file mode 100644 index 619a72bb3c..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameResolverTest.php +++ /dev/null @@ -1,150 +0,0 @@ -createReflector($source); - $method = $reflector->reflectClassLike(ClassName::fromString($className))->methods()->get($methodName); - $frame = $method->frame(); - - $assertion($frame, $this->logger()); - } - - /** - * @return Generator,Closure(Frame,LoggerInterface): void}> - */ - public static function provideForMethod(): Generator - { - yield 'Tolerates missing assert arguments' => [ - <<<'EOT' - problems()->count(), $frame->problems()->__toString()); - }]; - - yield 'Tolerates missing tokens' => [ - <<<'EOT' - classReflector->reflect(TestCase::class); - } - } - EOT - , [ 'Foobar', 'hello' ], function (Frame $frame, $logger): void { - self::assertEquals(0, $frame->problems()->count()); - }]; - } - - #[DataProvider('provideCache')] - public function testCache(string $source, int $expectedCacheMisses): void - { - preg_match_all('{(<[0-9]+>)}', $source, $matches, PREG_OFFSET_CAPTURE); - $edits = []; - foreach ($matches[0] as [$placeholder, $offset]) { - $edits[] = TextEdit::create(ByteOffset::fromInt((int)$offset), strlen($placeholder), ''); - $index = (int)trim($placeholder, '<>'); - $offsets[$index] = $offset; - } - ksort($offsets); - - $source = TextEdits::fromTextEdits($edits)->apply($source); - $reflector = $this->createReflector($source); - $docblockFactory = $this->createMock(DocBlockFactory::class); - $cache = new StaticCache(); - - $ast = (new TolerantAstProvider())->parseString($source); - $ast->uri = 'file:///test.php'; - - foreach ($offsets as $offset) { - $nodeResolver = new NodeContextResolver( - $reflector, - $docblockFactory, - new NullLogger(), - $cache, - [] - ); - $frameResolver = new FrameResolver( - $nodeResolver, - [ - new PassThroughWalker(), - ], - [ - Variable::class => new VariableResolver(), - ], - new CacheForDocument(fn () => $cache), - ); - $node = $ast->getDescendantNodeAtPosition($offset); - $frame = $frameResolver->build($node); - } - - self::assertEquals($expectedCacheMisses, $nodeResolver->cacheMisses); - } - - public static function provideCache(): Generator - { - yield 'cold cache' => [ - <<<'PHP' - v2; - PHP, - 6 - ]; - - yield 'uses cache if before last' => [ - <<<'PHP' - v1; - $<1>v2; - PHP, - 0 - ]; - - yield 'rebuilds cache if ahead of first' => [ - <<<'PHP' - v1; - $<2>v2; - PHP, - 6 - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/AssertWalkerTest.php b/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/AssertWalkerTest.php deleted file mode 100644 index b35a28db8a..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/AssertWalkerTest.php +++ /dev/null @@ -1,65 +0,0 @@ - [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()); - $this->assertEquals('Foobar', (string) $frame->locals()->first()->type()); - } - ]; - - yield 'assert instanceof negative' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertEquals(1, $frame->locals()->count()); - $this->assertEquals('', $frame->locals()->first()->type()->__toString()); - } - ]; - - yield 'should handle properties' => [ - <<<'EOT' - bar instanceof Bar); - - <> - } - } - EOT - , function (Frame $frame, int $offset): void { - $this->assertCount(1, $frame->locals()); - $this->assertEquals('Foo', $frame->locals()->atIndex(0)->type()->__toString()); - $this->assertCount(2, $frame->properties()); - $this->assertEquals('Foo', $frame->properties()->atIndex(1)->classType()->__toString()); - $this->assertEquals('Bar', $frame->properties()->atIndex(1)->type()->__toString()); - }]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/AssignmentWalkerTest.php b/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/AssignmentWalkerTest.php deleted file mode 100644 index 773371afcf..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/AssignmentWalkerTest.php +++ /dev/null @@ -1,321 +0,0 @@ - [ - <<<'EOT' - - } - } - - EOT - , function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('foobar')); - $var = $frame->locals()->byName('foobar')->first(); - $this->assertEquals('"foobar"', (string) $var->type()); - }]; - yield 'It returns types for reassigned variables' => [ - <<<'EOT' - - } - } - - EOT - , function (Frame $frame): void { - $vars = $frame->locals()->byName('foobar'); - $this->assertCount(1, $vars); - $var = $vars->first(); - $this->assertEquals('World', (string) $var->type()); - }]; - - yield 'It returns type for $this' => [ - <<<'EOT' - - } - } - - EOT - , function (Frame $frame): void { - $vars = $frame->locals()->byName('this'); - $this->assertCount(1, $vars); - $var = $vars->first(); - $this->assertEquals('Foobar', (string) $var->type()); - }]; - - yield 'It tracks assigned properties' => [ - <<<'EOT' - foobar = 'foobar'; - <> - } - } - EOT - , function (Frame $frame): void { - $vars = $frame->properties()->byName('foobar'); - $this->assertCount(1, $vars); - $var = $vars->first(); - $this->assertEquals('"foobar"', (string) $var->type()); - }]; - - yield 'It assigns property values to assignments' => [ - <<<'EOT' - foobar; - <> - } - } - EOT - , function (Frame $frame): void { - $vars = $frame->locals()->byName('foobar'); - $this->assertCount(1, $vars); - $var = $vars->last(); - $type = $var->type(); - assert($type instanceof IterableType); - $this->assertEquals('Foobar[]', (string) $type); - $this->assertEquals('Foobar', (string) $type->iterableValueType()); - }]; - - - yield 'It tracks assigned array properties' => [ - <<<'EOT' - foobar[] = 'foobar'; - <> - } - } - EOT - , function (Frame $frame): void { - $vars = $frame->properties()->byName('foobar'); - $this->assertCount(1, $vars); - $var = $vars->first(); - $this->assertEquals('array', (string) $var->type()); - }]; - - yield 'It tracks assigned from variable' => [ - <<<'EOT' - $foobar = 'foobar'; - <> - } - } - EOT - , function (Frame $frame): void { - $vars = $frame->properties()->byName('foobar'); - $this->assertCount(1, $vars); - $var = $vars->first(); - $this->assertEquals('"foobar"', (string) $var->type()); - }]; - - yield 'Handles array assignments' => [ - <<<'EOT' - 'bar' ]; - $bar = $foo['foo']; - <> - EOT - , - function (Frame $frame): void { - $this->assertCount(2, $frame->locals()); - $this->assertEquals('array{foo:"bar"}', (string) $frame->locals()->first()->type()); - $this->assertEquals('"bar"', (string) $frame->locals()->last()->type()); - } - ]; - - yield 'Includes list assignments' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(2, $frame->locals()); - $this->assertEquals('foo', $frame->locals()->first()->name()); - $this->assertEquals('"foo"', (string)$frame->locals()->first()->type()); - $this->assertEquals('bar', $frame->locals()->atIndex(1)->name()); - $this->assertEquals('"bar"', (string)$frame->locals()->atIndex(1)->type()); - } - ]; - - yield 'New list assignment' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(2, $frame->locals()); - $this->assertEquals('"foo"', (string)$frame->locals()->atIndex(0)->type()); - $this->assertEquals('"bar"', (string)$frame->locals()->atIndex(1)->type()); - } - ]; - - yield 'From generic return type with docblock' => [ - <<<'EOT' - - */ - interface Listy extends \Iterator { - } - - interface Barfoo - { - /** - * @return Listy - */ - public static function bar(): Listy; - } - - class Baz - { - public function (Barfoo $barfoo) - { - $bar = $barfoo->bar(); - <> - } - } - <> - } - EOT - , - function (Frame $frame): void { - $this->assertCount(3, $frame->locals()); - $this->assertEquals( - 'Foobar\Listy', - (string) $frame->locals()->byName('bar')->first()->type() - ); - $type = $frame->locals()->byName('bar')->first()->type(); - $this->assertEquals( - 'Foobar\Collection', - $type->iterableValueType()->__toString() - ); - } - ]; - - yield 'From incomplete assignment' => [ - <<<'EOT' - as<> - } - - public function function2(): Baz; - } - <> - } - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('barfoo')); - $type = $frame->locals()->byName('barfoo')->first()->type(); - assert($type instanceof ClassType); - $this->assertEquals('Barfoo', $type->name->short()); - } - ]; - - yield 'References previously walked member' => [ - <<<'EOT' - foobar = new Car(); - $this->options = $this->foobar->options; - } - public function bar() - { - $barfoo = $this->options->foobar(); - $barfo<>o; - } - } - <> - } - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('barfoo')); - } - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/FunctionLikeWalkerTest.php b/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/FunctionLikeWalkerTest.php deleted file mode 100644 index 87e7ca2f39..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/FunctionLikeWalkerTest.php +++ /dev/null @@ -1,243 +0,0 @@ - [ - <<<'EOT' - - } - } - - EOT - , function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('this')); - $this->assertEquals('Foobar\Barfoo\Foobar', $frame->locals()->byName('this')->first()->type()->__toString()); - $this->assertEquals(false, $frame->locals()->byName('this')->first()->isProperty()); - }]; - - yield 'It returns this with correct type in an anonymous function' => [ - <<<'EOT' - }); - } - } - - EOT - , function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('this')); - $this->assertEquals('Foobar\Barfoo\Foobar', $frame->locals()->byName('this')->first()->type()->__toString()); - $this->assertEquals(false, $frame->locals()->byName('this')->first()->isProperty()); - }]; - - yield 'It returns method arguments' => [ - <<<'EOT' - - } - } - - EOT - , function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('this')); - $this->assertEquals( - 'Foobar\Barfoo\Foobar', - $frame->locals()->byName('this')->first()->type()->__toString() - ); - }]; - - yield 'It injects method argument with inferred types' => [ - <<<'EOT' - - } - } - - EOT - , function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('many')); - $this->assertEquals('string', (string) $frame->locals()->byName('many')->first()->type()); - - $this->assertCount(1, $frame->locals()->byName('worlds')); - $this->assertEquals('Foobar\Barfoo\World[]', (string) $frame->locals()->byName('worlds')->first()->type()); - $type = $frame->locals()->byName('worlds')->first()->type(); - assert($type instanceof IterableType); - $this->assertEquals('Foobar\Barfoo\World', (string) $type->iterableValueType()); - }]; - - yield 'Variadic argument' => [ - <<<'EOT' - - } - } - - EOT - , function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('hellos')); - $variable = $frame->locals()->byName('hellos')->first(); - $type = $variable->type(); - assert($type instanceof IterableType); - $this->assertEquals('string', (string)$type->iterableValueType()); - }]; - - yield 'Respects closure scope' => [ - <<<'EOT' - - }; - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$bar'), 'Scoped variable exists'); - $this->assertCount(0, $frame->locals()->byName('$foo'), 'Parent scoped variable doesnt exist'); - } - ]; - - yield 'Static anonymous function has no $this or self::' => [ - <<<'EOT' - - }; - } - } - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$bar')); - $this->assertCount(0, $frame->locals()->byName('$this')); - } - ]; - - yield 'Injects closure parameters' => [ - <<<'EOT' - - }; - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$foo')); - $variable = $frame->locals()->byName('$foo')->first(); - $this->assertEquals('Foobar', $variable->type()->__toString()); - } - ]; - - yield 'Injects imported closure parent scope variables' => [ - <<<'EOT' - - }; - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $zed = $frame->locals()->byName('$zed')->first(); - $this->assertEquals('"zed"', (string) $zed->type()); - } - ]; - - yield 'Incomplete use name' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(0, $frame->locals()); - } - ]; - - yield 'Injects variables with @var (non-standard)' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('string', (string) $frame->locals()->byName('$zed')->last()->type()); - } - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/ReturnTypeWalkerTest.php b/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/ReturnTypeWalkerTest.php deleted file mode 100644 index 7df16d9801..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/ReturnTypeWalkerTest.php +++ /dev/null @@ -1,74 +0,0 @@ - [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - self::assertEquals('"string"', $frame->returnType()->__toString()); - } - ]; - - yield 'Get union return type from frame' => [ - <<<'EOT' - - } - - <> - EOT - , - function (Frame $frame): void { - self::assertEquals('null|"string"', $frame->returnType()->__toString()); - } - ]; - - yield 'Get do not duplicate union return type from frame' => [ - <<<'EOT' - - } - - <> - EOT - , - function (Frame $frame): void { - self::assertEquals('null|"string"', $frame->returnType()->__toString()); - } - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/VariableWalkerTest.php b/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/VariableWalkerTest.php deleted file mode 100644 index eaf73954b1..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalker/VariableWalkerTest.php +++ /dev/null @@ -1,174 +0,0 @@ - [ - <<<'EOT' - - } - } - EOT - , function (Frame $frame): void { - $vars = $frame->locals()->byName('$foobar'); - $this->assertCount(2, $vars); - $this->assertEquals('Foobar', (string) $vars->first()->type()); - $this->assertEquals('stdClass', (string) $vars->last()->type()); - }]; - - yield 'Injects variables with @var (standard)' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('string', (string) $frame->locals()->byName('$zed')->last()->type()); - } - ]; - - yield 'Injects variables with @var namespaced' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('Foo\\Bar', (string) $frame->locals()->byName('$zed')->last()->type()); - } - ]; - - yield 'Injects variables with @var namespaced and qualified name' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('Foo\\Bar\\Baz', (string) $frame->locals()->byName('$zed')->last()->type()); - } - ]; - - yield 'Injects variables with @var namespaced with fully qualified name' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('Bar\\Baz', (string) $frame->locals()->byName('$zed')->last()->type()); - } - ]; - - yield 'Injects variables with @var with imported namespace' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('Foo\Bar\Zed\Baz', (string) $frame->locals()->byName('$zed')->last()->type()); - } - ]; - - yield 'Injects named union type' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('Bar|Baz', $frame->locals()->byName('$zed')->last()->type()->__toString()); - } - ]; - - yield 'Unspecified type for following variable' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('Zed\Baz', (string) $frame->locals()->byName('$zed')->first()->type()); - } - ]; - - yield 'Unspecified type for following variable with class import' => [ - <<<'EOT' - - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('Zed\Baz', (string) $frame->locals()->byName('$zed')->first()->type()); - } - ]; - - yield 'Targeted variable not matching following variable assignment' => [ - <<<'EOT' - hello(); - <> - EOT - , - function (Frame $frame): void { - $this->assertCount(1, $frame->locals()->byName('$zed')); - $this->assertEquals('string', (string) $frame->locals()->byName('$zed')->last()->type()); - } - ]; - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalkerTestCase.php b/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalkerTestCase.php deleted file mode 100644 index 360e56ad03..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Inference/FrameWalkerTestCase.php +++ /dev/null @@ -1,44 +0,0 @@ -workspace()->path('test.php'); - $source = TextDocumentBuilder::create($source)->uri($path)->build(); - $reflector = $this->createReflectorWithWalker($source, $this->walker()); - $reflectionOffset = $reflector->reflectOffset($source, $offset); - $assertion->bindTo($this)->__invoke($reflectionOffset->frame(), $offset); - } - - abstract public static function provideWalk(): Generator; - - public function walker(): ?Framewalker - { - return null; - } - - private function createReflectorWithWalker($source, ?Walker $frameWalker = null): Reflector - { - $reflector = $this->createBuilder($source); - - if ($frameWalker) { - $reflector->addFrameWalker($frameWalker); - } - - return $reflector->build(); - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Inference/NodeContextResolverTest.php b/lib/WorseReflection/Tests/Integration/Core/Inference/NodeContextResolverTest.php deleted file mode 100644 index 3258f71b30..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Inference/NodeContextResolverTest.php +++ /dev/null @@ -1,1222 +0,0 @@ -logger()); - } - - #[DataProvider('provideGeneral')] - public function testGeneral(string $source, array $locals, array $expectedInformation): void - { - $variables = []; - $properties = []; - $offset = 0; - foreach ($locals as $name => $varSymbolInfo) { - $offset++; - if ($varSymbolInfo instanceof Type) { - $varSymbolInfo = NodeContext::for( - Symbol::fromTypeNameAndPosition( - 'variable', - $name, - ByteOffsetRange::fromInts($offset, $offset) - ) - )->withType($varSymbolInfo); - } - - $variable = Variable::fromSymbolContext($varSymbolInfo); - - if (Symbol::PROPERTY === $varSymbolInfo->symbol()->symbolType()) { - $properties[$varSymbolInfo->symbol()->position()->start()->toInt()] = $variable; - - continue; - } - - $variables[$varSymbolInfo->symbol()->position()->start()->toInt()] = $variable; - } - - $symbolInfo = $this->resolveNodeAtOffset( - LocalAssignments::fromArray($variables), - PropertyAssignments::fromArray($properties), - $source, - ); - $this->assertExpectedInformation($expectedInformation, $symbolInfo); - } - - #[DataProvider('provideValues')] - public function testValues(string $source, array $variables, array $expected): void - { - $information = $this->resolveNodeAtOffset( - LocalAssignments::fromArray($variables), - PropertyAssignments::create(), - $source, - ); - $this->assertExpectedInformation($expected, $information); - } - - /** - * These tests test the case where a class in the resolution tree was not found, however - * their usefulness is limited because we use the StringSourceLocator for these tests which - * "always" finds the source. - */ - #[DataProvider('provideNotResolvableClass')] - public function testNotResolvableClass(string $source): void - { - $value = $this->resolveNodeAtOffset( - LocalAssignments::fromArray([ - 0 => Variable::fromSymbolContext( - NodeContext::for(Symbol::fromTypeNameAndPosition( - Symbol::CLASS_, - 'bar', - ByteOffsetRange::fromInts(0, 0) - ))->withType(TypeFactory::fromString('Foobar')) - ) - ]), - PropertyAssignments::create(), - $source - ); - $this->assertEquals(TypeFactory::unknown(), $value->type()); - } - - public static function provideGeneral() - { - yield 'It should return none value for whitespace' => [ - ' <> ', [], - ['type' => ''], - ]; - - yield 'It should return the name of a class' => [ - <<<'EOT' - assName(); - - EOT - , [], ['type' => 'ClassName', 'symbol_type' => Symbol::CLASS_] - ]; - - yield 'It should return the fully qualified name of a class' => [ - <<<'EOT' - assName(); - - EOT - , [], ['type' => 'Foobar\Barfoo\ClassName'] - ]; - - yield 'It should return the fully qualified name of a with an imported name.' => [ - <<<'EOT' - sName(); - - EOT - , [], ['type' => 'BarBar\ClassName', 'symbol_type' => Symbol::CLASS_, 'symbol_name' => 'ClassName'] - ]; - - yield 'It should return the fully qualified name of a use definition' => [ - <<<'EOT' - sName(); - - $foo = new ClassName(); - - EOT - , [], ['type' => 'BarBar\ClassName'] - ]; - - yield 'It returns the FQN of a method parameter with a default' => [ - <<<'EOT' - barfoo = 'test') - { - } - } - - EOT - , [], ['type' => 'Foobar\Barfoo\Barfoo', 'symbol_type' => Symbol::VARIABLE, 'symbol_name' => 'barfoo'] - ]; - - yield 'It returns the type and value of a scalar method parameter' => [ - <<<'EOT' - arfoo = 'test') - { - } - } - - EOT - , [], ['type' => 'string'] - ]; - - yield 'It returns the value of a method parameter with a constant' => [ - <<<'EOT' - rfoo = 'test') - { - } - } - - EOT - , [], ['type' => 'string'] - ]; - - yield 'It returns the FQN of a method parameter in an interface' => [ - <<<'EOT' - ld); - } - - EOT - , [], ['type' => 'Foobar\Barfoo\World'] - ]; - - yield 'It returns the FQN of a method parameter in a trait' => [ - <<<'EOT' - World $world) - { - } - } - - EOT - , [], ['type' => 'Foobar\Barfoo\World', 'symbol_type' => Symbol::CLASS_, 'symbol_name' => 'World'] - ]; - - yield 'It returns the value of a method parameter' => [ - <<<'EOT' - barfoo = 'test') - { - } - } - - EOT - , [], ['type' => 'string'] - ]; - - yield 'Ignores parameter on anonymous class' => [ - <<<'EOT' - bar) {} }; - } - } - - EOT - , [], ['type' => '', 'symbol_type' => '', 'symbol_name' => 'Parameter'] - ]; - - yield 'It returns the FQN of a static call' => [ - <<<'EOT' - tory::create(); - - EOT - , [], ['type' => 'Acme\Factory', 'symbol_type' => Symbol::CLASS_] - ]; - - yield 'It returns the FQN of a method parameter' => [ - <<<'EOT' - orld $world) - { - } - } - - EOT - , [], ['type' => 'Foobar\Barfoo\World'] - ]; - - yield 'It resolves a anonymous function use' => [ - <<<'EOT' - oo) { - - } - - EOT - , [ 'foo' => TypeFactory::fromString('string') ], ['type' => 'string', 'symbol_type' => Symbol::VARIABLE, 'symbol_name' => 'foo'] - ]; - - yield 'It resolves an undeclared variable' => [ - <<<'EOT' - lah; - - EOT - , [], ['type' => '', 'symbol_type' => Symbol::VARIABLE, 'symbol_name' => 'blah'] - ]; - - yield 'It returns the FQN of variable assigned in frame' => [ - <<<'EOT' - orld; - } - } - - EOT - , [ 'world' => TypeFactory::fromString('World') ], ['type' => 'World', 'symbol_type' => Symbol::VARIABLE, 'symbol_name' => 'world'] - ]; - - yield 'It returns type for a call access expression' => [ - <<<'EOT' - foobar->type2()->type3(<>); - } - } - EOT - , [ - 'this' => TypeFactory::fromString('Foobar\Barfoo\Foobar'), - ], [ - 'type' => 'Foobar\Barfoo\Type3', - 'symbol_type' => Symbol::METHOD, - 'symbol_name' => 'type3', - 'container_type' => 'Foobar\Barfoo\Type2', - ], - ]; - - yield 'It returns type for a method which returns an interface type' => [ - <<<'EOT' - hello()->foo(<>); - } - } - EOT - , [ - 'this' => TypeFactory::fromString('Foobar'), - ], [ - 'type' => 'string', - 'symbol_type' => Symbol::METHOD, - 'symbol_name' => 'foo', - 'container_type' => 'Barfoo', - ], - ]; - - yield 'It returns class type for parent class for parent method' => [ - <<<'EOT' - type3(<>); - } - } - EOT - , [ - 'this' => TypeFactory::fromString('Foobar'), - ], [ - 'type' => 'Type3', - 'symbol_type' => Symbol::METHOD, - 'symbol_name' => 'type3', - 'container_type' => 'Foobar', - ], - ]; - - yield 'It returns type for a property access when class has method of same name' => [ - <<<'EOT' - foobar->asString(<>); - } - } - EOT - , [ - 'this' => TypeFactory::fromString('Foobar'), - ], ['type' => 'string'], - ]; - - yield 'It returns type for a new instantiation' => [ - <<<'EOT' - Bar(); - EOT - , [], ['type' => 'Bar'], - ]; - - yield 'It returns type for a new instantiation from a variable' => [ - <<<'EOT' - foobar; - EOT - , [ - 'foobar' => TypeFactory::fromString('Foobar'), - ], ['type' => 'Foobar'], - ]; - - yield 'It returns type for string literal' => [ - <<<'EOT' - '; - EOT - , [], ['type' => '"bar"', 'symbol_type' => Symbol::STRING ] - ]; - - yield 'It returns type for float' => [ - <<<'EOT' - 2; - EOT - , [], ['type' => '1.2', 'symbol_type' => Symbol::NUMBER], - ]; - - yield 'It returns type for integer' => [ - <<<'EOT' - ; - EOT - , [], ['type' => '12', 'symbol_type' => Symbol::NUMBER], - ]; - - yield 'It returns type for octal integer' => [ - <<<'EOT' - ; - EOT - , [], ['type' => '012', 'symbol_type' => Symbol::NUMBER], - ]; - - yield 'It returns type for hexadecimal integer' => [ - <<<'EOT' - ; - EOT - , [], ['type' => '0x1A', 'symbol_type' => Symbol::NUMBER], - ]; - - yield 'It returns type for binary integer' => [ - <<<'EOT' - ; - EOT - , [], ['type' => '0b11', 'symbol_type' => Symbol::NUMBER], - ]; - - yield 'It returns type for bool true' => [ - <<<'EOT' - ue; - EOT - , [], ['type' => 'true', 'symbol_type' => Symbol::BOOLEAN], - ]; - - yield 'It returns type for bool false' => [ - <<<'EOT' - false; - EOT - , [], ['type' => 'false', 'symbol_type' => Symbol::BOOLEAN], - ]; - - yield 'It returns type null' => [ - <<<'EOT' - ull; - EOT - , [], ['type' => 'null', ] ]; - - yield 'It returns type null case insensitive' => [ - <<<'EOT' - ULL; - EOT - , [], ['type' => 'null', ] ]; - - yield 'It returns type and value for an array' => [ - <<<'EOT' - 'two', 'three' => 3 <>]; - EOT - , [], ['type' => 'array{one:"two",three:3}'], - ]; - - yield 'Empty array' => [ - <<<'EOT' - ]; - EOT - , [], ['type' => 'array{}']]; - - yield 'It type for a class constant' => [ - <<<'EOT' - O; - - class Foobar - { - const HELLO = 'string'; - } - EOT - , [], ['type' => '"string"'], - ]; - - yield 'Static method access' => [ - <<<'EOT' - r(); - - class Hello - { - } - EOT - , [], ['type' => 'Hello'], - ]; - - yield 'Static constant access' => [ - <<<'EOT' - CONSTANT; - - class Foobar - { - const HELLO_CONSTANT = 'hello'; - } - EOT - , [], ['type' => '"hello"'], - ]; - - yield 'Static property access' => [ - <<<'EOT' - Property; - - class Foobar - { - /** @var string */ - public static $myProperty = 'hello'; - } - EOT - , [], [ - 'type' => 'string', - 'symbol_type' => Symbol::PROPERTY, - 'symbol_name' => 'myProperty', - 'container_type' => 'Foobar', - ], - ]; - - yield 'Static property access 2' => [ - <<<'EOT' - Property = 5; - } - } - EOT - , [], [ - 'type' => 'string', - 'symbol_type' => Symbol::PROPERTY, - 'symbol_name' => 'myProperty', - 'container_type' => 'Foobar', - ], - ]; - - yield 'Static property access instance)' => [ - <<<'EOT' - Property = 5; - EOT - , [ - 'foobar' => TypeFactory::fromString('Foobar') - ], [ - 'type' => 'string', - 'symbol_type' => Symbol::PROPERTY, - 'symbol_name' => 'myProperty', - 'container_type' => 'Foobar', - ], - ]; - - yield 'Member access with variable' => [ - <<<'EOT' - $barfoo(<>); - - class Foobar - { - } - EOT - , [], ['type' => ''], - ]; - - yield 'Member access with valued variable' => [ - <<<'EOT' - $barfoo(<>); - EOT - , [ - 'foobar' => TypeFactory::fromString('Foobar'), - 'barfoo' => NodeContext::for( - Symbol::fromTypeNameAndPosition(Symbol::STRING, 'barfoo', ByteOffsetRange::fromInts(0, 0)) - )->withType(TypeFactory::stringLiteral('hello')) - ], ['type' => 'string'], - ]; - - yield 'It returns type of property' => [ - <<<'EOT' - Class; - } - EOT - , [], ['type' => 'stdClass', 'symbol_name' => 'stdClass'], - ]; - - yield 'It returns type for parenthesised new object' => [ - <<<'EOT' - ; - EOT - , [], ['type' => 'stdClass', 'symbol_name' => 'stdClass'], - ]; - - yield 'It resolves a clone expression' => [ - <<<'EOT' - ; - EOT - , [], ['type' => 'stdClass', 'symbol_name' => 'stdClass'], - ]; - - yield 'It returns the FQN of variable assigned in frame 2' => [ - <<<'EOT' - bar instanceof Factory); - - $this->ba<>r - } - } - - EOT - , [ - 'this' => TypeFactory::class('Foobar\Barfoo\Foobar'), - 'bar' => NodeContext::for(Symbol::fromTypeNameAndPosition( - Symbol::PROPERTY, - 'bar', - ByteOffsetRange::fromInts(0, 0), - )) - ->withContainerType(TypeFactory::class('Foobar\Barfoo\Foobar')) - ->withType(TypeFactory::class('Acme\Factory')), - ], [ - 'types' => [ - TypeFactory::class('Acme\Factory'), - ], - 'symbol_type' => Symbol::PROPERTY, - 'symbol_name' => 'bar', - ] - ]; - } - - public static function provideValues() - { - yield 'It returns type for self' => [ - <<<'EOT' - f:: - } - } - EOT - , [], ['type' => 'Foobar'] - ]; - - yield 'It returns type for static' => [ - <<<'EOT' - ic:: - } - } - EOT - , [], ['type' => 'Foobar'] - ]; - - yield 'It returns type for parent' => [ - <<<'EOT' - nt:: - } - } - EOT - , [], ['type' => 'ParentClass'] - ]; - - yield 'It assumes true for ternary expressions' => [ - <<<'EOT' - 'foobar' : 'barfoo'; - EOT - , [], ['type' => '"foobar"', ] - ]; - - yield 'It uses condition value if ternery "if" is empty' => [ - <<<'EOT' - new \stdClass(); - EOT - , [], ['type' => '"string"', ] - ]; - - yield 'It shows the symbol name for a method declartion' => [ - <<<'EOT' - thod() - { - } - } - EOT - , [], [ - 'symbol_type' => Symbol::METHOD, - 'symbol_name' => 'method', - 'container_type' => 'Foobar', - ] - ]; - - yield 'Class name' => [ - <<<'EOT' - obar - { - } - EOT - , [], ['type' => 'Foobar', 'symbol_type' => Symbol::CLASS_, 'symbol_name' => 'Foobar'], - ]; - - yield 'Property name' => [ - <<<'EOT' - aa = 'asd'; - } - EOT - , [], ['type' => '', 'symbol_type' => Symbol::PROPERTY, 'symbol_name' => 'aaa', 'container_type' => 'Foobar'], - ]; - - yield 'Constant name' => [ - <<<'EOT' - A = 'aaa'; - } - EOT - , [], [ - 'type' => '', - 'symbol_type' => Symbol::CONSTANT, - 'symbol_name' => 'AAA', - 'container_type' => 'Foobar' - ], - ]; - - yield 'Enum case name' => [ - <<<'EOT' - A = 'aaa'; - } - EOT - , [], [ - 'type' => '', - 'symbol_type' => Symbol::CASE, - 'symbol_name' => 'AAA', - 'container_type' => 'Foobar' - ], - ]; - - yield 'Enum const' => [ - <<<'EOT' - A = 'aaa'; - } - EOT - , [], [ - 'type' => '', - 'symbol_type' => Symbol::CONSTANT, - 'symbol_name' => 'AAA', - 'container_type' => 'Foobar' - ], - ]; - - yield 'Function name' => [ - <<<'EOT' - oobar() - { - } - EOT - , [], ['symbol_type' => Symbol::FUNCTION, 'symbol_name' => 'foobar'], - ]; - - - yield 'Function call' => [ - <<<'EOT' - lo(); - EOT - , [], ['type' => 'string', 'symbol_type' => Symbol::FUNCTION, 'symbol_name' => 'hello'], - ]; - - yield 'Trait name' => [ - <<<'EOT' - bar - { - } - EOT - , [], ['symbol_type' => 'class', 'symbol_name' => 'Barbar', 'type' => 'Barbar' ], - ]; - } - - public static function provideNotResolvableClass() - { - yield 'Calling property method for non-existing class' => [ - <<<'EOT' - hello->foobar(<>); - } - } - EOT - ]; - - yield 'Class extends non-existing class' => [ - <<<'EOT' - foobar(<>); - } - } - EOT - ]; - - yield 'Method returns non-existing class' => [ - <<<'EOT' - hai()->foo(<>); - } - } - EOT - ]; - - yield 'Method returns class which extends non-existing class' => [ - <<<'EOT' - hai()->foo(<>); - } - } - - class Hai extends NonExisting - { - } - EOT - ]; - - - yield 'Static method returns non-existing class' => [ - <<<'EOT' - foo(<>); - - class Foobar - { - public static function hai(): Foo - { - } - } - EOT - ]; - } - - public function testAttachesScope(): void - { - $source = <<<'EOT' - o; - EOT - ; - $context = $this->resolveNodeAtOffset( - LocalAssignments::create(), - PropertyAssignments::create(), - $source, - ); - $this->assertCount(2, $context->scope()->nameImports()); - } - - private function resolveNodeAtOffset( - LocalAssignments $locals, - PropertyAssignments $properties, - string $source - ): NodeContext { - $frame = new ConcreteFrame($locals, $properties); - - [$source, $offset] = ExtractOffset::fromSource($source); - $node = $this->parseSource($source)->getDescendantNodeAtPosition($offset); - - $reflector = $this->createReflector($source); - $nameResolver = new NodeToTypeConverter($reflector, $this->logger()); - $resolver = new NodeContextResolver( - $reflector, - new DocblockParserFactory($reflector), - $this->logger(), - new StaticCache(), - (new DefaultResolverFactory( - $reflector, - $nameResolver, - new GenericMapResolver($reflector), - new NodeContextFromMemberAccess( - new GenericMapResolver($reflector), - [] - ) - ))->createResolvers(), - ); - - return $resolver->resolveNode($frame, $node); - } - - private function assertExpectedInformation(array $expectedInformation, NodeContext $information): void - { - foreach ($expectedInformation as $name => $value) { - switch ($name) { - case 'type': - $this->assertEquals($value, (string) $information->type(), $name); - continue 2; - case 'types': - $this->assertEquals( - Type::fromTypes(...$value)->__toString(), - $information->type()->__toString(), - $name, - ); - continue 2; - case 'symbol_type': - $this->assertEquals($value, $information->symbol()->symbolType(), $name); - continue 2; - case 'symbol_name': - $this->assertEquals($value, $information->symbol()->name(), $name); - continue 2; - case 'container_type': - $this->assertEquals($value, (string) $information->containerType(), $name); - continue 2; - case 'log': - $this->assertStringContainsString($value, implode(' ', $this->logger->messages()), $name); - continue 2; - default: - throw new RuntimeException(sprintf('Do not know how to test symbol information attribute "%s"', $name)); - } - } - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/SourceCodeLocator/StubSourceLocatorTest.php b/lib/WorseReflection/Tests/Integration/Core/SourceCodeLocator/StubSourceLocatorTest.php deleted file mode 100644 index b3ae46096e..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/SourceCodeLocator/StubSourceLocatorTest.php +++ /dev/null @@ -1,63 +0,0 @@ -workspace()->reset(); - - $locator = new StringSourceLocator(TextDocumentBuilder::create('')->build()); - $reflector = ReflectorBuilder::create()->addLocator($locator)->build(); - $this->workspace()->mkdir('stubs')->mkdir('cache'); - - $this->sourceLocator = new StubSourceLocator( - $reflector, - $this->workspace()->path('stubs'), - $this->workspace()->path('cache') - ); - } - - public function testCanLocateClasses(): void - { - $this->workspace()->put('stubs/Stub.php', 'sourceLocator->locate(ClassName::fromString('StubOne')); - $this->assertStringContainsString('class StubOne', (string) $code); - } - - public function testCanLocateFunctions(): void - { - $this->workspace()->put('stubs/Stub.php', 'sourceLocator->locate(Name::fromString('hello_world')); - $this->assertStringContainsString('function hello_world()', (string) $code); - } - - public function testDoesNotParseNonPhpFiles(): void - { - $this->workspace()->put('stubs/Stub.xml', 'workspace()->put('stubs/Stub.php', 'sourceLocator->locate(Name::fromString('hello_world')); - $this->fail('Non PHP file parsed'); - } catch (NotFound) { - $this->addToAssertionCount(1); - return; - } - - $code = $this->sourceLocator->locate(Name::fromString('goodbye_world')); - $this->assertStringContainsString('function goodbye_world()', (string) $code); - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/SourceReflectorTest.php b/lib/WorseReflection/Tests/Integration/Core/SourceReflectorTest.php deleted file mode 100644 index 7365ffc142..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/SourceReflectorTest.php +++ /dev/null @@ -1,84 +0,0 @@ -expectException(ClassNotFound::class); - $this->expectExceptionMessage($expectedErrorMessage); - - $this->createReflector($source)->$method($class); - } - - /** - * @return Generator - */ - public static function provideReflectClassNotCorrectType(): Generator - { - yield 'Class' => [ - ' [ - ' [ - 'createReflector($source)->reflectOffset($source, 27); - $this->assertEquals('"Hello"', (string) $offset->nodeContext()->type()); - } - - #[TestDox('It reflects the value at an offset.')] - public function testReflectOffsetRedeclared(): void - { - $source = <<<'EOT' - ar; - EOT - ; - - [$source, $offset] = ExtractOffset::fromSource($source); - - $source = TextDocumentBuilder::fromUnknown($source); - $offset = $this->createReflector($source)->reflectOffset($source, (int)$offset); - $this->assertEquals('1234', (string) $offset->nodeContext()->type()); - } -} diff --git a/lib/WorseReflection/Tests/Integration/Core/Util/OriginalMethodResolverTest.php b/lib/WorseReflection/Tests/Integration/Core/Util/OriginalMethodResolverTest.php deleted file mode 100644 index ed46d8a5e4..0000000000 --- a/lib/WorseReflection/Tests/Integration/Core/Util/OriginalMethodResolverTest.php +++ /dev/null @@ -1,104 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest(implode("\n", $manifest)); - $source = $this->workspace()->getContents('test.php'); - [$source, $offset] = ExtractOffset::fromSource($source); - - $reflector = $this->createWorkspaceReflector($source); - - $member = $reflector->reflectClassLike($containerType)->members()->byMemberType($memberType)->get($memberName); - - $member = (new OriginalMethodResolver($reflector))->resolveOriginalMember($member); - - self::assertEquals($expectedType, $member->declaringClass()->name()->__toString()); - } - - /** - * @return Generator - */ - public static function provideResolve(): Generator - { - yield 'declaring container type' => [ - ["// File: test.php\n [ - [ - "// File: test.php\n [ - [ - "// File: test.php\n [ - [ - "// File: test.php\n [ - [ - "// File: test.php\n [ - [ - "// File: test.php\nlogger = new ArrayLogger(); - } - - public function createBuilder(TextDocument|string $source): ReflectorBuilder - { - return ReflectorBuilder::create() - ->addSource($source) - ->addMemberProvider(new DocblockMemberProvider()) - ->addFrameWalker(new TestAssertWalker($this)) - ->withLogger($this->logger()); - } - - public function createReflector(string $source): Reflector - { - return $this->createBuilder($source)->build(); - } - - public function createWorkspaceReflector(string $source): Reflector - { - return ReflectorBuilder::create() - ->addLocator(new StubSourceLocator( - ReflectorBuilder::create()->build(), - $this->workspace()->path('/'), - $this->workspace()->path('/') - )) - ->addMemberProvider(new DocblockMemberProvider()) - ->withLogger($this->logger())->build(); - } - - protected function logger(): ArrayLogger - { - return $this->logger; - } - - protected function workspace(): Workspace - { - return new Workspace(__DIR__ . '/../Workspace'); - } - - protected function parseSource(string $source): SourceFileNode - { - $parser = new TolerantAstProvider(); - - return $parser->parseString($source); - } -} diff --git a/lib/WorseReflection/Tests/Smoke/smoke_test.php b/lib/WorseReflection/Tests/Smoke/smoke_test.php deleted file mode 100755 index b2bf0e550a..0000000000 --- a/lib/WorseReflection/Tests/Smoke/smoke_test.php +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env php - '.*\.php$', - 'offset' => 0, - 'limit' => null -], getopt('', [ - 'pattern:', - 'offset:', - 'limit:', -])); - -if (isset($argv[1])) { - $pattern = $argv[1]; -} - -$reflector = ReflectorBuilder::create() - ->enableCache() - ->addLocator(new ComposerSourceLocator($autoload)) - ->addLocator(new StubSourceLocator( - ReflectorBuilder::create()->build(), - __DIR__ . '/../../vendor/jetbrains/phpstorm-stubs', - __DIR__ . '/../Workspace/smoke-cache' - )) - ->build(); - -$files = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::SELF_FIRST -); - -$files = new RegexIterator($files, '{.*' . $opts['pattern'] . '.*}'); -$exceptions = []; -$count = 0; - -echo 'Legend: N = Not found, E = Error' . "\n" . "\n"; - -/** @var SplFileInfo $file */ -foreach ($files as $file) { - if ($count < $opts['offset']) { - $count++; - continue; - } - - if (null !== $opts['limit'] && $count > $opts['limit']) { - break; - } - - echo $count++ . ' ' . Path::makeRelative($file->getPathname(), getcwd()) . "\n"; - $message = $file->getPathname(); - try { - $source = TextDocumentBuilder::create(file_get_contents($file->getPathname()))->uri($file->getPathname())->build(); - $classes = $reflector->reflectClassesIn($source); - - /** @var ReflectionClass $class */ - foreach ($classes as $class) { - /** @var ReflectionMethod $method */ - foreach ($class->methods() as $method) { - $time = microtime(true); - $method->frame(); - - $time = microtime(true) - $time; - - if ($time > $slowThreshold) { - fwrite($logHandle, sprintf('%s#%s (%ss)', $class->name()->full(), $method->name(), number_format($time, 2)) . "\n"); - echo 'S'; - } - } - } - } catch (NotFound $e) { - fwrite($logHandle, sprintf('%s %s %s: ', 'NOT FOUND', Path::makeRelative($file->getPathname(), getcwd()), $e->getMessage()). "\n"); - echo 'N'; - } catch (Exception $e) { - echo 'E'; - fwrite($logHandle, sprintf('%s %s [%s] %s', 'ERROR', $message, get_class($e), $e->getMessage())."\n"); - ; - $exceptions[] = $e; - } finally { - } -} - -fclose($logHandle); diff --git a/lib/WorseReflection/Tests/Unit/Bridge/Composer/ComposerSourceLocatorTest.php b/lib/WorseReflection/Tests/Unit/Bridge/Composer/ComposerSourceLocatorTest.php deleted file mode 100644 index d6693b7cc8..0000000000 --- a/lib/WorseReflection/Tests/Unit/Bridge/Composer/ComposerSourceLocatorTest.php +++ /dev/null @@ -1,19 +0,0 @@ -locate(Name::fromString(ComposerSourceLocatorTest::class)); - $this->assertEquals(Path::canonicalize(__FILE__), $sourceCode->uri()->path()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Bridge/Phpactor/DocblockParser/DocblockParserFactoryTest.php b/lib/WorseReflection/Tests/Unit/Bridge/Phpactor/DocblockParser/DocblockParserFactoryTest.php deleted file mode 100644 index 174a95f950..0000000000 --- a/lib/WorseReflection/Tests/Unit/Bridge/Phpactor/DocblockParser/DocblockParserFactoryTest.php +++ /dev/null @@ -1,428 +0,0 @@ -parseDocblock($docblock); - - if (is_string($expected)) { - self::assertEquals($expected, $docblock->returnType()->__toString()); - return; - } - - self::assertInstanceOf(get_class($expected), $docblock->returnType()); - self::assertEquals($expected->__toString(), $docblock->returnType()->__toString()); - } - - /** - * @return Generator - */ - public static function provideResolveType(): Generator - { - yield [ - '/** @return string */', - new StringType() - ]; - - yield [ - '/** @return int */', - new IntType() - ]; - - yield [ - '/** @return float */', - new FloatType() - ]; - - yield [ - '/** @return mixed */', - new MixedType() - ]; - - yield [ - '/** @return array */', - new ArrayType(new MissingType()) - ]; - - yield [ - '/** @return array|string */', - new UnionType(new ArrayType(new MissingType()), new StringType()) - ]; - - yield [ - '/** @return array&string */', - new IntersectionType(new ArrayType(new MissingType()), new StringType()) - ]; - - yield [ - '/** @return array */', - new ArrayType(new StringType()) - ]; - - yield [ - '/** @return bool */', - new BooleanType() - ]; - - yield [ - '/** @return null */', - new NullType() - ]; - - yield [ - '/** @return callable */', - new CallableType([], new MissingType()) - ]; - - yield [ - '/** @return callable(): string */', - new CallableType([], new StringType()) - ]; - yield [ - '/** @return callable(string,bool): string */', - new CallableType([ - new StringType(), - new BooleanType(), - ], new StringType()) - ]; - yield [ - '/** @return iterable */', - new PseudoIterableType(), - ]; - yield [ - '/** @return object */', - new ObjectType(), - ]; - yield [ - '/** @return resource */', - new ResourceType(), - ]; - - yield [ - '/** @return void */', - new VoidType(), - ]; - yield [ - '/** @return array */', - new ArrayType(new IntType(), new StringType()) - ]; - yield [ - '/** @return array> */', - new ArrayType(new IntType(), new ArrayType(new StringType(), new BooleanType())) - ]; - - yield 'nullable' => [ - '/** @return ?string */', - '?string', - ]; - - yield [ - '/** @return T */', - 'T', - ]; - - yield [ - '/** @return \IteratorAggregate */', - 'IteratorAggregate', - ]; - - yield [ - '/** @return array{} */', - 'array{}', - ]; - - yield 'arrayshape with keys' => [ - '/** @return array{foo:int,bar:string} */', - 'array{foo:int,bar:string}', - ]; - - yield 'parenthesized' => [ - '/** @return null|(callable(int):string)|string|int */', - 'null|(callable(int): string)|string|int', - ]; - - yield 'multiline array shape' => [ - <<<'EOT' - /** - * @return array{ - * foo:int, - * bar:string - * } - */ - EOT - , - 'array{foo:int,bar:string}', - ]; - - yield 'literals' => [ - '/** @return null|"foo"|123|123.3 */', - 'null|"foo"|123|123.3', - ]; - - yield 'list' => [ - '/** @return list */', - TypeFactory::list(), - ]; - - yield 'list with type' => [ - '/** @return list */', - TypeFactory::list(TypeFactory::string()), - ]; - - yield 'never' => [ - '/** @return never */', - TypeFactory::never(), - ]; - - yield 'false' => [ - '/** @return false */', - TypeFactory::false(), - ]; - yield 'union false' => [ - '/** @return false|int */', - TypeFactory::union(TypeFactory::false(), TypeFactory::int()) - ]; - - yield 'psalm prefix' => [ - '/** @psalm-return int */', - TypeFactory::int(), - ]; - - yield 'conditional type' => [ - '/** @return ($foo is true ? string : int) */', - TypeFactory::parenthesized( - new ConditionalType( - '$foo', - TypeFactory::boolLiteral(true), - TypeFactory::string(), - TypeFactory::int() - ) - ) - ]; - - yield 'class string generic' => [ - '/** @return class-string */', - TypeFactory::classString('T'), - ]; - - yield 'int range max' => [ - '/** @return int<12, max> */', - TypeFactory::intRange( - new IntLiteralType(12), - new IntMaxType(PHP_INT_MAX) - ) - ]; - - yield 'int range' => [ - '/** @return int<12, 23> */', - TypeFactory::intRange( - new IntLiteralType(12), - new IntLiteralType(23), - ) - ]; - - yield 'int positive' => [ - '/** @return positive-int */', - TypeFactory::intPositive() - ]; - - yield 'int negative' => [ - '/** @return negative-int */', - TypeFactory::intNegative() - ]; - } - - public function testClassConstant(): void - { - $source = <<<'EOT' - addSource($source)->build(); - $source = TextDocumentBuilder::fromUnknown($source); - $class = $reflector->reflectClassesIn( - $source - )->first(); - $docblock = $this->parseDocblockWithClass($reflector, $class, '/** @return self::BAR */'); - self::assertEquals('self::BAR', $docblock->returnType()->__toString()); - } - - public function testClassConstantGlob(): void - { - $source = <<<'EOT' - addSource($source)->build(); - $source = TextDocumentBuilder::fromUnknown($source); - $class = $reflector->reflectClassesIn($source)->first(); - $docblock = $this->parseDocblockWithClass($reflector, $class, '/** @return Foo::BA* */'); - self::assertEquals('Foo::BA*', $docblock->returnType()->__toString()); - } - - public function testClassConstantGlobInArrayShape(): void - { - $source = <<<'EOT' - addSource($source)->build(); - $source = TextDocumentBuilder::fromUnknown($source); - $class = $reflector->reflectClassesIn($source)->first(); - $docblock = $this->parseDocblockWithClass($reflector, $class, '/** @return array{string,Foo::*} */'); - self::assertEquals('array{string,Foo::*}', $docblock->returnType()->__toString()); - } - - public function testMethods(): void - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, '/** @method Barfoo foobar() */'); - $methods = $docblock->methods($reflector->reflectClass('Bar\Foobar')); - - self::assertEquals('foobar', $methods->first()->name()); - self::assertEquals('Barfoo', $methods->first()->type()); - } - - public function testStaticMethods(): void - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, '/** @method static Barfoo foobar() */'); - $methods = $docblock->methods($reflector->reflectClass('Bar\Foobar')); - - self::assertEquals('foobar', $methods->first()->name()); - self::assertEquals('Barfoo', $methods->first()->type()); - self::assertTrue($methods->first()->isStatic()); - } - - public function testMethodsWithParams(): void - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, '/** @method Barfoo foobar(string $foobar, int $barfoo) */'); - $methods = $docblock->methods($reflector->reflectClass('Bar\Foobar')); - - self::assertEquals('foobar', $methods->first()->name()); - self::assertEquals('Barfoo', $methods->first()->type()); - self::assertEquals('foobar', $methods->first()->parameters()->first()->name()); - self::assertEquals('string', $methods->first()->parameters()->first()->type()); - self::assertEquals('barfoo', $methods->first()->parameters()->get('barfoo')->name()); - self::assertEquals('int', $methods->first()->parameters()->get('barfoo')->type()); - } - - public function testProperties(): void - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, '/** @property Barfoo $foobar */'); - $methods = $docblock->properties($reflector->reflectClass('Bar\Foobar')); - - self::assertEquals('foobar', $methods->first()->name()); - self::assertEquals('Barfoo', $methods->first()->type()->__toString()); - } - - public function testVars(): void - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, '/** @var Barfoo */'); - $vars = $docblock->vars(); - self::assertEquals('Barfoo', $vars->type()); - } - - public function testVarsWithName(): void - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, '/** @var Barfoo $foo */'); - $vars = iterator_to_array($docblock->vars()); - self::assertCount(1, $vars); - self::assertEquals('Barfoo', $vars[0]->type()); - self::assertEquals('foo', $vars[0]->name()); - } - - public function testParameterType(): void - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, '/** @param Barfoo $foobar */'); - $type = $docblock->parameterType('foobar'); - self::assertEquals('Barfoo', $type->__toString()); - } - - public function testPropertyType(): void - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, '/** @property Barfoo $foobar */'); - $type = $docblock->propertyType('foobar'); - self::assertEquals('Barfoo', $type->__toString()); - } - - private function parseDocblock(string $docblock): DocBlock - { - $reflector = $this->createReflector('parseDocblockWithReflector($reflector, $docblock); - } - - private function parseDocblockWithReflector(Reflector $reflector, string $docblock): DocBlock - { - $scope = $this->prophesize(ReflectionScope::class); - $scope->resolveFullyQualifiedName(Argument::any())->will(fn ($args) => $args[0]); - - return (new DocblockParserFactory($reflector))->create($docblock, $scope->reveal()); - } - - private function parseDocblockWithClass(Reflector $reflector, ReflectionClassLike $classLike, string $docblock): DocBlock - { - return (new DocblockParserFactory($reflector))->create($docblock, $classLike->scope()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Bridge/TolerantParser/Parser/CachedParserTest.php b/lib/WorseReflection/Tests/Unit/Bridge/TolerantParser/Parser/CachedParserTest.php deleted file mode 100644 index e4655f7492..0000000000 --- a/lib/WorseReflection/Tests/Unit/Bridge/TolerantParser/Parser/CachedParserTest.php +++ /dev/null @@ -1,47 +0,0 @@ -createParser(); - $node1 = $parser->get(TextDocumentBuilder::create(file_get_contents(__FILE__))->build()); - $node2 = $parser->get(TextDocumentBuilder::create(file_get_contents(__FILE__))->build()); - - $this->assertSame($node1, $node2); - } - - public function testUsesUriInKey(): void - { - $parser = $this->createParser(); - $node1 = $parser->get(TextDocumentBuilder::create(file_get_contents(__FILE__))->build()); - $node2 = $parser->get(TextDocumentBuilder::fromUri(__FILE__)->build()); - - $this->assertNotSame($node1, $node2); - } - - public function testReturnsDifferentResultsForDifferentSourceCodes(): void - { - $parser = $this->createParser(); - $node1 = $parser->get(TextDocumentBuilder::create(file_get_contents(__FILE__))->build()); - $node2 = $parser->get(TextDocumentBuilder::create('Foobar' . file_get_contents(__FILE__))->build()); - - $this->assertNotSame($node1, $node2); - } - - private function createParser(): CachedAstProvider - { - return new CachedAstProvider( - new TolerantAstProvider(), - new TtlCache() - ); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Bridge/TolerantParser/Reflection/Collection/ReflectionClassCollectionTest.php b/lib/WorseReflection/Tests/Unit/Bridge/TolerantParser/Reflection/Collection/ReflectionClassCollectionTest.php deleted file mode 100644 index 3280a09bdb..0000000000 --- a/lib/WorseReflection/Tests/Unit/Bridge/TolerantParser/Reflection/Collection/ReflectionClassCollectionTest.php +++ /dev/null @@ -1,45 +0,0 @@ -serviceLocator = $this->prophesize(ServiceLocator::class); - $this->reflection1 = $this->prophesize(ReflectionClass::class); - $this->reflection2 = $this->prophesize(ReflectionClass::class); - $this->reflection3 = $this->prophesize(ReflectionClass::class); - } - - #[TestDox('It returns only concrete classes.')] - public function testConcrete(): void - { - $this->reflection1->isConcrete()->willReturn(false); - $this->reflection2->isConcrete()->willReturn(true); - $this->reflection3->isConcrete()->willReturn(false); - - $collection = ReflectionClassLikeCollection::fromReflections([ - $this->reflection1->reveal(), $this->reflection2->reveal(), $this->reflection3->reveal() - ]); - - $this->assertCount(1, $collection->concrete()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Cache/StaticCacheTest.php b/lib/WorseReflection/Tests/Unit/Core/Cache/StaticCacheTest.php deleted file mode 100644 index 18a8d5a670..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Cache/StaticCacheTest.php +++ /dev/null @@ -1,38 +0,0 @@ -getOrSet('foobar', fn () => $counter++); - self::assertEquals(0, $counter); - self::assertEquals(0, $value); - $value = $cache->getOrSet('foobar', fn () => $counter++); - self::assertEquals(0, $counter); - self::assertEquals(0, $value); - } - - public function testGetHasSet(): void - { - $cache = new StaticCache(); - $counter = 0; - - self::assertNull($cache->get('foo')); - - $cache->set('foo', 'bar'); - - self::assertNotNull($cache->get('foo')); - self::assertEquals('bar', $cache->get('foo')->scalar()); - - $cache->remove('foo'); - - self::assertNull($cache->get('foo')); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Cache/TtlCacheTest.php b/lib/WorseReflection/Tests/Unit/Core/Cache/TtlCacheTest.php deleted file mode 100644 index 04680f667b..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Cache/TtlCacheTest.php +++ /dev/null @@ -1,79 +0,0 @@ -get('foobar')); - self::assertEquals(1234, $cache->getOrSet('foobar', function () { - return 1234; - })); - self::assertNotNull($cache->get('foobar')); - } - - public function testGetExpire(): void - { - // 0.5ms - $cache = new TtlCache(0.0005); - $count = 0; - - // cache should expire on every other iteration - for ($i = 0; $i < 10; $i++) { - if (null === $cache->get('foobar')) { - $cache->set('foobar', ++$count); - } - - // 0.25 milliseconds - usleep(250); - } - - self::assertGreaterThan(4, $count); - self::assertLessThanOrEqual(6, $count); - } - - public function testRemove(): void - { - $cache = new TtlCache(1); - $count = 0; - - $cache->set('foobar', 'hello'); - self::assertNotNull($cache->get('foobar')); - $cache->remove('foobar'); - self::assertNull($cache->get('foobar')); - } - - public function testCallbackIsOnlyCalledOnce(): void - { - $cache = new TtlCache(); - $count = 0; - for ($i = 0; $i < 5; $i++) { - $cache->getOrSet('foobar', function () use (&$count) { - $count++; - return 1234; - }); - } - self::assertEquals(1, $count); - } - - public function testDiscardsEntryIfExpired(): void - { - $cache = new TtlCache(0.0001); - $count = 0; - - for ($i = 0; $i < 5; $i++) { - $cache->getOrSet('foobar', function () use (&$count) { - $count++; - return 1234; - }); - usleep(50); - } - - self::assertLessThanOrEqual(5, $count); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/CacheForDocumentTest.php b/lib/WorseReflection/Tests/Unit/Core/CacheForDocumentTest.php deleted file mode 100644 index 6714faa793..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/CacheForDocumentTest.php +++ /dev/null @@ -1,28 +0,0 @@ - new StaticCache()); - $result = $cache->getOrSet(TextDocumentUri::fromString('file:///foo'), 'bar', function () { - return 'bar'; - }); - self::assertEquals('bar', $result); - $result = $cache->getOrSet(TextDocumentUri::fromString('file:///foo'), 'bar', function () { - return 'boo'; - }); - self::assertEquals('bar', $result); - $result = $cache->getOrSet(TextDocumentUri::fromString('file:///baz'), 'bar', function () { - return 'boo'; - }); - self::assertEquals('boo', $result); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/ClassHierarchyResolverTest.php b/lib/WorseReflection/Tests/Unit/Core/ClassHierarchyResolverTest.php deleted file mode 100644 index afd623e3d7..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/ClassHierarchyResolverTest.php +++ /dev/null @@ -1,71 +0,0 @@ - $definitions - * @param list $expected - */ - #[DataProvider('provideClassHierarchy')] - public function testClassHierarchy(array $definitions, array $expected): void - { - $reflector = ReflectorBuilder::create()->addSource(implode("\n", $definitions))->build(); - $resolver = new ClassHierarchyResolver(); - $hierarchy =$resolver->resolve($reflector->reflectClassLike('Foobar')); - $names = array_map(fn (ReflectionClassLike $class) => $class->name()->__toString(), $hierarchy); - - self::assertEquals($expected, array_values($names)); - } - - public static function provideClassHierarchy(): Generator - { - yield [ - [ - 'assertSame($givenClass, $className); - } - - public function testFromUnknownString(): void - { - $className = ClassName::fromUnknown(self::CLASS_NAME); - - $this->assertEquals(ClassName::fromString(self::CLASS_NAME), $className); - } - - public function testFromUnknownInvalid(): void - { - $this->expectExceptionMessage('Do not know how to create class'); - ClassName::fromUnknown(new stdClass()); - } - - public function testFromUnknownClassName(): void - { - $className1 = ClassName::fromString('Foobar'); - $className2 = ClassName::fromUnknown($className1); - - $this->assertSame($className1, $className2); - } - - public function testPrepend(): void - { - $className1 = ClassName::fromString('Foobar'); - $className2 = ClassName::fromString('Barfoo'); - - $className3 = $className1->prepend($className2); - - $this->assertEquals('Barfoo\\Foobar', (string) $className3); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/DefaultValueTest.php b/lib/WorseReflection/Tests/Unit/Core/DefaultValueTest.php deleted file mode 100644 index 89f200a9cb..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/DefaultValueTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertFalse($value->isDefined()); - } - - #[TestDox('It represents a value')] - public function testValue(): void - { - $value = DefaultValue::fromValue(42); - $this->assertEquals(42, $value->value()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/DocBlock/PlainDocblockTest.php b/lib/WorseReflection/Tests/Unit/Core/DocBlock/PlainDocblockTest.php deleted file mode 100644 index df410c9cca..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/DocBlock/PlainDocblockTest.php +++ /dev/null @@ -1,47 +0,0 @@ -createDocblock('')->isDefined()); - self::assertTrue($this->createDocblock('foo')->isDefined()); - } - - public function testInherits(): void - { - self::assertFalse($this->createDocblock('')->inherits()); - self::assertTrue($this->createDocblock('@inheritDoc')->inherits()); - } - - public function testFormatted(): void - { - self::assertEquals("hello world\ngoodbye world", $this->createDocblock( - <<<'EOT' - /** - * hello world - * goodbye world - */ - - EOT - )->formatted()); - } - - public function testSingle(): void - { - self::assertEquals('hello world', $this->createDocblock( - '/** hello world */' - )->formatted()); - } - - private function createDocblock(string $string): DocBlock - { - return new PlainDocblock($string); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Inference/AssignmentstTestCase.php b/lib/WorseReflection/Tests/Unit/Core/Inference/AssignmentstTestCase.php deleted file mode 100644 index f22b07d159..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Inference/AssignmentstTestCase.php +++ /dev/null @@ -1,98 +0,0 @@ -assignments(); - $this->assertCount(0, $assignments->byName('hello')); - - $information = NodeContext::for( - Symbol::fromTypeNameAndPosition( - Symbol::VARIABLE, - 'hello', - ByteOffsetRange::fromInts(0, 0) - ) - ); - - $assignments->set(Variable::fromSymbolContext($information)); - - $this->assertEquals('hello', $assignments->byName('hello')->first()->name()); - } - - public function testLessThanEqualTo(): void - { - $assignments = $this->assignments(); - - $assignments->set($this->createVariable('hello', 0, 5)); - $assignments->set($this->createVariable('hello', 5, 10)); - $assignments->set($this->createVariable('hello', 10, 15)); - - $this->assertCount(2, $assignments->byName('hello')->lessThanOrEqualTo(5)); - } - - public function testLessThan(): void - { - $assignments = $this->assignments(); - - $assignments->set($this->createVariable('hello', 0, 5)); - $assignments->set($this->createVariable('hello', 5, 10)); - $assignments->set($this->createVariable('hello', 10, 15)); - - $this->assertCount(1, $assignments->byName('hello')->lessThan(5)); - } - - public function testGreaterThanOrEqualTo(): void - { - $assignments = $this->assignments(); - - $assignments->set($this->createVariable('hello', 0, 5)); - $assignments->set($this->createVariable('hello', 5, 10)); - $assignments->set($this->createVariable('hello', 10, 15)); - - $this->assertCount(2, $assignments->byName('hello')->greaterThanOrEqualTo(5)); - } - - public function testGreaterThan(): void - { - $assignments = $this->assignments(); - - $assignments->set($this->createVariable('hello', 0, 5)); - $assignments->set($this->createVariable('hello', 5, 10)); - $assignments->set($this->createVariable('hello', 10, 15)); - - $this->assertCount(1, $assignments->byName('hello')->greaterThan(5)); - } - - public function testThrowsExceptionIfIndexNotExist(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('No variable at index "5"'); - $assignments = $this->assignments(); - - $assignments->set($this->createVariable('hello', 0, 5)); - - $this->assertCount(1, $assignments->atIndex(5)); - } - - abstract protected function assignments(): Assignments; - - private function createVariable(string $name, int $start, int $end): Variable - { - return Variable::fromSymbolContext(NodeContext::for(Symbol::fromTypeNameAndPosition( - Symbol::VARIABLE, - $name, - ByteOffsetRange::fromInts($start, $end) - ))); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Inference/FrameTest.php b/lib/WorseReflection/Tests/Unit/Core/Inference/FrameTest.php deleted file mode 100644 index 58f50c36be..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Inference/FrameTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertInstanceOf(LocalAssignments::class, $frame->locals()); - $this->assertInstanceOf(PropertyAssignments::class, $frame->properties()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Inference/LocalAssignmentsTest.php b/lib/WorseReflection/Tests/Unit/Core/Inference/LocalAssignmentsTest.php deleted file mode 100644 index 83474eb3fa..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Inference/LocalAssignmentsTest.php +++ /dev/null @@ -1,14 +0,0 @@ -expectException(CouldNotResolveNode::class); - $this->expectExceptionMessage('Did not know how'); - $frame = new ConcreteFrame(); - $locator = $this->prophesize(ServiceLocator::class); - $nodeReflector = new NodeReflector($locator->reveal()); - - $nodeReflector->reflectNode($frame, new SourceFileNode()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Inference/ProblemsTest.php b/lib/WorseReflection/Tests/Unit/Core/Inference/ProblemsTest.php deleted file mode 100644 index b6a218f558..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Inference/ProblemsTest.php +++ /dev/null @@ -1,30 +0,0 @@ -add($s1); - $p1->add($s2); - - $p2 = Problems::create(); - $p2->add($s3); - $p2->add($s4); - - $p3 = $p2->merge($p1); - - $this->assertEquals([ $s3, $s4, $s1, $s2 ], $p3->toArray()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Inference/SymbolFactoryTest.php b/lib/WorseReflection/Tests/Unit/Core/Inference/SymbolFactoryTest.php deleted file mode 100644 index 6f5df505c9..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Inference/SymbolFactoryTest.php +++ /dev/null @@ -1,66 +0,0 @@ -factory = new NodeContextFactory(); - $this->node = $this->prophesize(Node::class); - } - - public function testInformationInvalidKeys(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Invalid keys "asd"'); - $this->factory->create('hello', 10, 20, [ 'asd' => 'asd' ]); - } - - public function testInformation(): void - { - $information = $this->factory->create('hello', 10, 20); - $this->assertInstanceOf(NodeContext::class, $information); - $symbol = $information->symbol(); - - $this->assertEquals('hello', $symbol->name()); - $this->assertEquals(10, $symbol->position()->start()->toInt()); - $this->assertEquals(20, $symbol->position()->end()->toInt()); - } - - public function testInformationOptions(): void - { - $containerType = TypeFactory::fromString('container'); - $type = TypeFactory::fromString('type'); - - $information = $this->factory->create('hello', 10, 20, [ - 'symbol_type' => Symbol::ARRAY, - 'container_type' => $containerType, - 'type' => $type, - ]); - - $this->assertInstanceOf(NodeContext::class, $information); - $this->assertSame($information->type(), $type); - $this->assertSame($information->containerType(), $containerType); - $this->assertEquals(Symbol::ARRAY, $information->symbol()->symbolType()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Inference/TypeAssertionsTest.php b/lib/WorseReflection/Tests/Unit/Core/Inference/TypeAssertionsTest.php deleted file mode 100644 index 24e1517674..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Inference/TypeAssertionsTest.php +++ /dev/null @@ -1,107 +0,0 @@ -or(new TypeAssertions([$b])); - $assertion = $assertions->variables()->firstForName('foo'); - - self::assertEquals($expected->__toString(), $assertion->apply($type)->__toString()); - self::assertEquals($negated->__toString(), $assertion->negate()->apply($type)->__toString()); - } - - public static function provideOr(): Generator - { - yield [ - TypeFactory::mixed(), - - // assert foo is STRING positively and NULL negatively - TypeAssertion::variable( - 'foo', - 0, - fn (Type $t) => $t->addType(TypeFactory::string()), - fn (Type $t) => $t->addType(TypeFactory::null()), - ), - - // assert foo is STRING positively and int NULL negatively - TypeAssertion::variable( - 'foo', - 0, - fn (Type $t) => $t->addType(TypeFactory::int()), - fn (Type $t) => $t->addType(TypeFactory::float()), - ), - - // it's either mixed, int or string - TypeFactory::union( - TypeFactory::mixed(), - TypeFactory::string(), - TypeFactory::int() - ), - - // it's either mixed, int or string - TypeFactory::union( - TypeFactory::mixed(), - TypeFactory::null(), - TypeFactory::float() - ), - ]; - } - - #[DataProvider('provideAnd')] - public function testAnd(Type $type, TypeAssertion $a, TypeAssertion $b, Type $expected, Type $negated): void - { - $assertions = new TypeAssertions([$a]); - $assertions = $assertions->and(new TypeAssertions([$b])); - $assertion = $assertions->variables()->firstForName('foo'); - - self::assertEquals($expected->__toString(), $assertion->apply($type)->__toString()); - self::assertEquals($negated->__toString(), $assertion->negate()->apply($type)->__toString()); - } - - public static function provideAnd(): Generator - { - yield [ - TypeFactory::mixed(), - - // both assertions should be applied on positive - TypeAssertion::variable( - 'foo', - 0, - fn (Type $t) => $t->addType(TypeFactory::string()), - fn (Type $t) => $t->addType(TypeFactory::null()), - ), - - // both assertions should be applied on negative - TypeAssertion::variable( - 'foo', - 0, - fn (Type $t) => $t->addType(TypeFactory::int()), - fn (Type $t) => $t->addType(TypeFactory::float()), - ), - - TypeFactory::union( - TypeFactory::mixed(), - TypeFactory::string(), - TypeFactory::int(), - ), - TypeFactory::union( - TypeFactory::mixed(), - TypeFactory::null(), - TypeFactory::float(), - ), - ]; - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Inference/TypeCombinatorTest.php b/lib/WorseReflection/Tests/Unit/Core/Inference/TypeCombinatorTest.php deleted file mode 100644 index 9d946d83e9..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Inference/TypeCombinatorTest.php +++ /dev/null @@ -1,213 +0,0 @@ -__toString() - ); - } - - /** - * @return Generator - */ - public function provideNarrow(): Generator - { - yield 'cannot narrow from smaller to wider (e.g. string to mixed)' => [ - TypeFactory::union( - TypeFactory::string(), - ), - [ - TypeFactory::mixed(), - ], - '' - ]; - - yield 'mixed narrows to int' => [ - TypeFactory::union( - TypeFactory::mixed(), - ), - [ - TypeFactory::int(), - ], - 'int' - ]; - - yield 'mixed and string narrows to int' => [ - TypeFactory::union( - TypeFactory::mixed(), - TypeFactory::string(), - ), - [ - TypeFactory::int(), - ], - 'int' - ]; - - $classTypes = $this->classTypes( - ' [ - TypeFactory::union( - $classTypes[0], - $classTypes[1], - ), - [ - $classTypes[1], - ], - 'Barfoo', - ]; - - yield 'narrow abstract class to concerete with other types' => [ - TypeFactory::union(...array_merge( - [ - $classTypes[0], - $classTypes[1], - ], - [ - TypeFactory::string(), - ], - )), - [ - $classTypes[1], - ], - 'Barfoo', - ]; - - $classTypes = $this->classTypes( - ' [ - TypeFactory::union( - $classTypes[0], - $classTypes[1], - ), - [ - $classTypes[2], - ], - '(Foobar&Bar)|(Barfoo&Bar)', - ]; - $classTypes = $this->classTypes( - ' [ - TypeFactory::union( - $classTypes[0], - $classTypes[1], - $classTypes[2], - ), - [ - $classTypes[1], - ], - 'Barfoo', - ]; - - yield 'strips unknown types' => [ - TypeFactory::union( - TypeFactory::unknown(), - TypeFactory::string(), - ), - [ - TypeFactory::string(), - ], - 'string', - ]; - - $classTypes = $this->classTypes( - ' [ - TypeFactory::union( - $classTypes[0], - $classTypes[1], - ), - [ - $classTypes[1], - ], - 'Bar', - ]; - - yield 'narrow intersection to unknown type ' => [ - TypeFactory::intersection( - $classTypes[0], - TypeFactory::class('Car'), - ), - [ - $classTypes[1], - ], - 'Foo&Car&Bar', - ]; - - yield 'narrow parenthesized intersection to unknown type ' => [ - TypeFactory::parenthesized( - TypeFactory::intersection( - $classTypes[0], - TypeFactory::class('Car'), - ) - ), - [ - $classTypes[1], - ], - 'Foo&Car&Bar', - ]; - - yield 'narrow parenthesized intersection to intersection type ' => [ - TypeFactory::parenthesized( - TypeFactory::intersection( - $classTypes[0], - TypeFactory::class('Car'), - ) - ), - [ - TypeFactory::parenthesized( - TypeFactory::intersection( - $classTypes[1], - TypeFactory::class('Dar'), - ) - ), - ], - 'Foo&Car&Bar&Dar', - ]; - } - - /** - * @return Type[] - */ - private function classTypes(string $string, string ...$classNames): array - { - $reflector = ReflectorBuilder::create()->addSource($string)->build(); - return array_values(array_map(function (string $className) use ($reflector) { - return TypeFactory::reflectedClass($reflector, $className); - }, $classNames)); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/NameImportsTest.php b/lib/WorseReflection/Tests/Unit/Core/NameImportsTest.php deleted file mode 100644 index 12f3e63a04..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/NameImportsTest.php +++ /dev/null @@ -1,86 +0,0 @@ - Name::fromString('Foobar\\Barfoo'), - ]); - - $this->assertTrue($imports->hasAlias('Barfoo')); - $this->assertEquals( - Name::fromString('Foobar\\Barfoo'), - $imports->getByAlias('Barfoo') - ); - } - - public function testResolveAliasedLocalName(): void - { - $imports = NameImports::fromNames([ - 'Baz' => Name::fromString('Foobar\\Barfoo'), - ]); - - $this->assertEquals( - Name::fromString('Baz'), - $imports->resolveLocalName(Name::fromString('Foobar\\Barfoo')) - ); - } - - public function testResolveRelativeAliasedLocalName(): void - { - $imports = NameImports::fromNames([ - 'Baz' => Name::fromString('Foobar\\Barfoo'), - ]); - - $this->assertEquals( - Name::fromString('Baz\\Zoz'), - $imports->resolveLocalName( - Name::fromString('Foobar\\Barfoo\\Zoz') - ) - ); - } - - public function testResolveRelativeAliasedLocalName2(): void - { - $imports = NameImports::fromNames([ - 'Baz' => Name::fromString('Foobar\\Barfoo'), - ]); - - $this->assertEquals( - Name::fromString('Baz\\Zoz\\Foo'), - $imports->resolveLocalName( - Name::fromString('Foobar\\Barfoo\\Zoz\\Foo') - ) - ); - } - - public function testLocalNameIfNoImport(): void - { - $imports = NameImports::fromNames([ - ]); - - $this->assertEquals( - Name::fromString('Foo'), - $imports->resolveLocalName( - Name::fromString('Foobar\\Barfoo\\Zoz\\Foo') - ) - ); - } - - public function testAliasNotFound(): void - { - $this->expectException(RuntimeException::class); - - $imports = NameImports::fromNames([]); - - $imports->getByAlias('Barfoo'); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/NameTest.php b/lib/WorseReflection/Tests/Unit/Core/NameTest.php deleted file mode 100644 index 0a89c4e5e4..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/NameTest.php +++ /dev/null @@ -1,27 +0,0 @@ -assertEquals('Foo', (string) $name->head()); - } - - public function testTail(): void - { - $name = Name::fromString('Foo\\Bar\\Baz'); - $this->assertEquals('Bar\\Baz', (string) $name->tail()); - } - - public function testIsFullyQualified(): void - { - $name = Name::fromString('\\Foo\\Bar\\Baz'); - $this->assertTrue($name->wasFullyQualified()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/PositionTest.php b/lib/WorseReflection/Tests/Unit/Core/PositionTest.php deleted file mode 100644 index c58b04e5b6..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/PositionTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertEquals(15, $position->start()->toInt()); - $this->assertEquals(35, $position->end()->toInt()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Reflection/Collection/ChainReflectionMemberCollectionTest.php b/lib/WorseReflection/Tests/Unit/Core/Reflection/Collection/ChainReflectionMemberCollectionTest.php deleted file mode 100644 index e9107cf455..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Reflection/Collection/ChainReflectionMemberCollectionTest.php +++ /dev/null @@ -1,260 +0,0 @@ - - */ - private ObjectProphecy $collection3; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $collection4; - - public function setUp(): void - { - $this->collection1 = $this->prophesize(ReflectionMemberCollection::class); - $this->collection2 = $this->prophesize(ReflectionMemberCollection::class); - $this->collection3 = $this->prophesize(ReflectionMemberCollection::class); - $this->collection4 = $this->prophesize(ReflectionMemberCollection::class); - - $this->member1 = $this->prophesize(ReflectionMember::class); - } - - public function testIsIterable(): void - { - $collection = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal() - ]); - - $this->collection1->getIterator()->willReturn(new ArrayIterator([1])); - $this->collection2->getIterator()->willReturn(new ArrayIterator([2])); - - $this->assertInstanceOf(Traversable::class, $collection->getIterator()); - $iterator = $collection->getIterator(); - $this->assertEquals(1, $iterator->current()); - $iterator->next(); - $this->assertEquals(2, $iterator->current()); - } - - public function testItReturnsTheCount(): void - { - $collection = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal() - ]); - - $this->collection1->count()->willReturn(1); - $this->collection2->count()->willReturn(2); - $this->assertCount(3, $collection); - } - - public function testItMergesAnotherCollection(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal() - ]); - $collection2 = $collection1->merge($this->collection2->reveal()); - - $this->collection1->count()->willReturn(1); - $this->collection2->count()->willReturn(2); - - $this->assertCount(1, $collection1); - $this->assertCount(3, $collection2); - $this->assertNotSame($collection1, $collection2); - } - - public function testGetsItemByName(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal() - ]); - - $this->collection1->count()->willReturn(1); - $this->collection2->count()->willReturn(3); - - $this->collection1->get('foobar')->willReturn($this->member1->reveal()); - $this->collection1->has('foobar')->willReturn(true); - $this->collection1->keys()->willReturn([]); - $this->collection2->keys()->willReturn([]); - - - $item = $collection1->get('foobar'); - $this->assertSame($this->member1->reveal(), $item); - } - - public function testThrowsExceptionIfItemDoesNotExistOnGet(): void - { - $this->expectException(ItemNotFound::class); - - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal() - ]); - - $this->collection1->getIterator()->willReturn(new ArrayIterator([1])); - $this->collection2->getIterator()->willReturn(new ArrayIterator([2,3])); - - $this->collection1->has('foobar')->willReturn(false); - $this->collection2->has('foobar')->willReturn(false); - - $this->collection1->keys()->willReturn([]); - $this->collection2->keys()->willReturn([]); - - - $item = $collection1->get('foobar'); - $this->assertSame($this->member1->reveal(), $item); - } - - public function testReturnsFirstItem(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal() - ]); - - $this->collection1->first()->willReturn($this->member1->reveal()); - $this->collection1->count()->willReturn(1); - - $member = $collection1->first(); - $this->assertSame($this->member1->reveal(), $member); - } - - public function testReturnsLastItem(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal() - ]); - - $this->collection1->last()->willReturn($this->member1->reveal()); - - $member = $collection1->last(); - $this->assertSame($this->member1->reveal(), $member); - } - - public function testThrowsExceptionIfNoFirstItem(): void - { - $this->expectException(ItemNotFound::class); - $collection1 = ChainReflectionMemberCollection::fromCollections([]); - - $collection1->first(); - } - - public function testThrowsExceptionIfNoLastItem(): void - { - $this->expectException(ItemNotFound::class); - $collection1 = ChainReflectionMemberCollection::fromCollections([]); - - $collection1->last(); - } - - public function testHas(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal(), - ]); - - $this->collection1->has('foo')->willReturn(true); - $this->collection2->has('foo')->shouldNotBeCalled(); - $this->collection1->has('bar')->willReturn(false); - $this->collection2->has('bar')->willReturn(false); - - $this->assertTrue($collection1->has('foo')); - $this->assertFalse($collection1->has('bar')); - } - - public function testReturnByVisibilities(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal(), - ]); - - $visibilties = [ Visibility::protected() ]; - $this->collection1->byVisibilities($visibilties)->willReturn($this->collection3->reveal()); - $this->collection2->byVisibilities($visibilties)->willReturn($this->collection4->reveal()); - $this->collection1->count()->willReturn(1); - $this->collection2->count()->willReturn(1); - $this->collection3->count()->willReturn(1); - $this->collection4->count()->willReturn(1); - - $collection = $collection1->byVisibilities($visibilties); - $this->assertCount(2, $collection); - } - - public function testReturnBelongingTo(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal(), - ]); - - $className = ClassName::fromString('Foo'); - $this->collection1->belongingTo($className)->willReturn($this->collection3->reveal()); - $this->collection2->belongingTo($className)->willReturn($this->collection4->reveal()); - - $this->assertEquals(ChainReflectionMemberCollection::fromCollections([ - $this->collection3->reveal(), - $this->collection4->reveal() - ]), $collection1->belongingTo($className)); - } - - public function testReturnsItemsAtOffset(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal(), - ]); - - $name = 1; - $this->collection1->atOffset($name)->willReturn($this->collection3->reveal()); - $this->collection2->atOffset($name)->willReturn($this->collection4->reveal()); - - $this->assertEquals(ChainReflectionMemberCollection::fromCollections([ - $this->collection3->reveal(), - $this->collection4->reveal() - ]), $collection1->atOffset($name)); - } - - public function testReturnsMembersByName(): void - { - $collection1 = ChainReflectionMemberCollection::fromCollections([ - $this->collection1->reveal(), - $this->collection2->reveal(), - ]); - - $name = 'name'; - $this->collection1->byName($name)->willReturn($this->collection3->reveal()); - $this->collection2->byName($name)->willReturn($this->collection4->reveal()); - - $this->assertEquals(ChainReflectionMemberCollection::fromCollections([ - $this->collection3->reveal(), - $this->collection4->reveal() - ]), $collection1->byName($name)); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Reflection/Collection/HomogeneousReflectionMemberCollectionTest.php b/lib/WorseReflection/Tests/Unit/Core/Reflection/Collection/HomogeneousReflectionMemberCollectionTest.php deleted file mode 100644 index aa08708c1f..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Reflection/Collection/HomogeneousReflectionMemberCollectionTest.php +++ /dev/null @@ -1,117 +0,0 @@ - - */ - private ObjectProphecy $member1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $member2; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $member3; - - public function setUp(): void - { - $this->member1 = $this->prophesize(ReflectionMember::class); - $this->member2 = $this->prophesize(ReflectionMember::class); - $this->member3 = $this->prophesize(ReflectionMember::class); - } - - public function testByVisibilities(): void - { - $collection = $this->create([ - $this->member1->reveal(), - $this->member2->reveal(), - $this->member3->reveal(), - ]); - - $this->member1->visibility()->willReturn(Visibility::public()); - $this->member2->visibility()->willReturn(Visibility::private()); - $this->member3->visibility()->willReturn(Visibility::public()); - - $collection = $collection->byVisibilities([Visibility::public()]); - $this->assertCount(2, $collection); - } - - public function testBelongingTo(): void - { - $collection = $this->create([ - $this->member1->reveal(), - $this->member2->reveal(), - $this->member3->reveal(), - ]); - - $class1 = $this->prophesize(ReflectionClass::class); - $class2 = $this->prophesize(ReflectionClass::class); - $class1->name()->willReturn(ClassName::fromString('foo')); - $class2->name()->willReturn(ClassName::fromString('bar')); - - $this->member1->declaringClass()->willReturn($class1->reveal()); - $this->member2->declaringClass()->willReturn($class2->reveal()); - $this->member3->declaringClass()->willReturn($class1->reveal()); - - $collection = $collection->belongingTo(ClassName::fromString('foo')); - $this->assertCount(2, $collection); - } - - public function testAtOffset(): void - { - $collection = $this->create([ - $this->member1->reveal(), - $this->member2->reveal(), - $this->member3->reveal(), - ]); - - $this->member1->position()->willReturn(ByteOffsetRange::fromInts(0, 10)); - $this->member2->position()->willReturn(ByteOffsetRange::fromInts(11, 11)); - $this->member3->position()->willReturn(ByteOffsetRange::fromInts(13, 16)); - - $collection = $collection->atOffset(11); - $this->assertCount(1, $collection); - } - - public function testByName(): void - { - $collection = $this->create([ - 'foo' => $this->member1->reveal(), - 'bar' => $this->member2->reveal() - ]); - - $this->member1->name()->willReturn('foo'); - - $collection = $collection->byName('foo'); - $this->assertCount(1, $collection); - - $collection = $collection->byName('bar'); - $this->assertCount(0, $collection); - } - - private function create(array $members): ReflectionMemberCollection - { - return HomogeneousReflectionMemberCollection::fromReflections( - $members - ); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Reflector/ClassReflector/MemonizedClassReflectorTest.php b/lib/WorseReflection/Tests/Unit/Core/Reflector/ClassReflector/MemonizedClassReflectorTest.php deleted file mode 100644 index bc6cd487d7..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Reflector/ClassReflector/MemonizedClassReflectorTest.php +++ /dev/null @@ -1,97 +0,0 @@ - - */ - private ObjectProphecy $innerClassReflector; - - /** - * @var MemonizedClassReflector - */ - private MemonizedReflector $reflector; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $innerFunctionReflector; - - private ClassName $className; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $innerConstantReflector; - - public function setUp(): void - { - $this->innerClassReflector = $this->prophesize(ClassReflector::class); - $this->innerFunctionReflector = $this->prophesize(FunctionReflector::class); - $this->innerConstantReflector = $this->prophesize(ConstantReflector::class); - - $this->reflector = new MemonizedReflector( - $this->innerClassReflector->reveal(), - $this->innerFunctionReflector->reveal(), - $this->innerConstantReflector->reveal(), - new TtlCache(10) - ); - $this->className = ClassName::fromString('Hello'); - } - - public function testReflectClass(): void - { - $this->innerClassReflector->reflectClass($this->className)->shouldBeCalledTimes(1); - $this->reflector->reflectClass($this->className); - $this->reflector->reflectClass($this->className); - $this->reflector->reflectClass($this->className); - } - - public function testReflectInterface(): void - { - $this->innerClassReflector->reflectInterface($this->className, [])->shouldBeCalledTimes(1); - $this->reflector->reflectInterface($this->className); - $this->reflector->reflectInterface($this->className); - $this->reflector->reflectInterface($this->className); - } - - public function testReflectTrait(): void - { - $this->innerClassReflector->reflectTrait($this->className, [])->shouldBeCalledTimes(1); - $this->reflector->reflectTrait($this->className); - $this->reflector->reflectTrait($this->className); - $this->reflector->reflectTrait($this->className); - } - - public function testReflectClassLike(): void - { - $this->innerClassReflector->reflectClassLike($this->className, [])->shouldBeCalledTimes(1); - $this->reflector->reflectClassLike($this->className); - $this->reflector->reflectClassLike($this->className); - $this->reflector->reflectClassLike($this->className); - } - - public function testReflectFunction(): void - { - $name = Name::fromString('Foo'); - $this->innerFunctionReflector->reflectFunction($name)->shouldBeCalledTimes(1); - $this->reflector->reflectFunction($name); - $this->reflector->reflectFunction($name); - $this->reflector->reflectFunction($name); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Reflector/SourceCode/ContextualSourceCodeReflectorTest.php b/lib/WorseReflection/Tests/Unit/Core/Reflector/SourceCode/ContextualSourceCodeReflectorTest.php deleted file mode 100644 index 46771899be..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Reflector/SourceCode/ContextualSourceCodeReflectorTest.php +++ /dev/null @@ -1,54 +0,0 @@ -locator = new TemporarySourceLocator(ReflectorBuilder::create()->build()); - - $this->reflector = new ContextualSourceCodeReflector( - ReflectorBuilder::create()->build(), - $this->locator - ); - - $this->code = TextDocumentBuilder::create(self::TEST_SOURCE_CODE)->build(); - } - - public function testReflectsClassesIn(): void - { - self::assertEquals(2, $this->reflector->reflectClassesIn(TextDocumentBuilder::fromUnknown('count()); - } - - public function testReflectOffset(): void - { - $offset = $this->reflector->reflectOffset(TextDocumentBuilder::fromUnknown(self::TEST_SOURCE_CODE), self::TEST_OFFSET); - self::assertInstanceOf(ReflectionOffset::class, $offset); - } - - public function testReflectMethodCall(): void - { - $call = $this->reflector->reflectMethodCall(TextDocumentBuilder::fromUnknown('bar();'), 59); - self::assertInstanceOf(ReflectionMethodCall::class, $call); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/ChainSourceLocatorTest.php b/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/ChainSourceLocatorTest.php deleted file mode 100644 index 4fe4f77b4f..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/ChainSourceLocatorTest.php +++ /dev/null @@ -1,90 +0,0 @@ -locator1 = $this->prophesize(SourceCodeLocator::class); - $this->locator2 = $this->prophesize(SourceCodeLocator::class); - } - - #[TestDox('It throws an exception if no loaders found.')] - public function testNoLocators(): void - { - $this->expectException(SourceNotFound::class); - $this->locate([], ClassName::fromString('as')); - } - - #[TestDox('It delegates to first loader.')] - public function testDelegateToFirst(): void - { - $expectedSource = TextDocumentBuilder::create('hello')->build(); - $class = ClassName::fromString('Foobar'); - $this->locator1->locate($class)->willReturn($expectedSource); - $this->locator2->locate($class)->shouldNotBeCalled(); - - $source = $this->locate([ - $this->locator1->reveal(), - $this->locator2->reveal() - ], $class); - - $this->assertSame($expectedSource, $source); - } - - #[TestDox('It delegates to second if first throws exception.')] - public function testDelegateToSecond(): void - { - $expectedSource = TextDocumentBuilder::create('hello')->build(); - $class = ClassName::fromString('Foobar'); - $this->locator1->locate($class)->willThrow(new SourceNotFound('Foo')); - $this->locator2->locate($class)->willReturn($expectedSource); - - $source = $this->locate([ - $this->locator1->reveal(), - $this->locator2->reveal() - ], $class); - - $this->assertSame($expectedSource, $source); - } - - #[TestDox('It throws an exception if all fail')] - public function testAllFail(): void - { - $this->expectException(SourceNotFound::class); - $this->expectExceptionMessage('Could not find source with "Foobar"'); - $expectedSource = TextDocumentBuilder::create('hello')->build(); - $class = ClassName::fromString('Foobar'); - $this->locator1->locate($class)->willThrow(new SourceNotFound('Foo')); - $this->locator2->locate($class)->willThrow(new SourceNotFound('Foo')); - - $this->locate([ - $this->locator1->reveal(), - $this->locator2->reveal() - ], $class); - } - - private function locate(array $locators, ClassName $className) - { - $locator = new ChainSourceLocator($locators); - return $locator->locate($className); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/NativeReflectionFunctionSourceLocatorTest.php b/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/NativeReflectionFunctionSourceLocatorTest.php deleted file mode 100644 index e20996fa44..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/NativeReflectionFunctionSourceLocatorTest.php +++ /dev/null @@ -1,45 +0,0 @@ -locator = new NativeReflectionFunctionSourceLocator(); - } - - public function testLocatesAFunction(): void - { - $location = $this->locator->locate(Name::fromString(__NAMESPACE__ . '\\test_function')); - $this->assertEquals(Path::canonicalize(__FILE__), $location->uri()->path()); - $this->assertEquals(file_get_contents(__FILE__), $location->__toString()); - } - - public function testThrowsExceptionWhenSourceNotFound(): void - { - $this->expectException(SourceNotFound::class); - $this->locator->locate(Name::fromString(__NAMESPACE__ . '\\not_existing')); - } - - public function testDoesNotLocateInternalFunctions(): void - { - $this->expectException(SourceNotFound::class); - $this->locator->locate(Name::fromString('assert')); - } -} - -function test_function(): void -{ -} diff --git a/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/StringSourceLocatorTest.php b/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/StringSourceLocatorTest.php deleted file mode 100644 index 21ad5086a7..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/StringSourceLocatorTest.php +++ /dev/null @@ -1,19 +0,0 @@ -build()); - $source = $locator->locate(ClassName::fromString('Foobar')); - - $this->assertEquals('Hello', (string) $source); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/TemporarySourceLocatorTest.php b/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/TemporarySourceLocatorTest.php deleted file mode 100644 index 6c7e893311..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/SourceCodeLocator/TemporarySourceLocatorTest.php +++ /dev/null @@ -1,60 +0,0 @@ -locator = new TemporarySourceLocator( - ReflectorBuilder::create()->build() - ); - } - - public function testThrowsExceptionWhenClassNotFound(): void - { - $this->expectException(SourceNotFound::class); - $this->expectExceptionMessage('Class "Foobar" not found'); - - $source = TextDocumentBuilder::create('build(); - $this->locator->pushSourceCode($source); - - $this->locator->locate(ClassName::fromString('Foobar')); - } - - public function testReturnsSourceIfClassIsInTheSource(): void - { - $code = 'locator->pushSourceCode(TextDocumentBuilder::create($code)->build()); - $source = $this->locator->locate(ClassName::fromString('Foobar')); - $this->assertEquals($code, (string) $source); - } - - public function testNewFilesOverridePreviousOnes(): void - { - $code1 = 'locator->pushSourceCode(TextDocumentBuilder::create($code1)->uri('file:///foo.php')->build()); - - $code2 = 'locator->pushSourceCode(TextDocumentBuilder::create($code2)->uri('file:///foo.php')->build()); - - $source = $this->locator->locate(ClassName::fromString('Boobar')); - $this->assertEquals($code2, (string) $source); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/TemplateMapTest.php b/lib/WorseReflection/Tests/Unit/Core/TemplateMapTest.php deleted file mode 100644 index 20f2b7938a..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/TemplateMapTest.php +++ /dev/null @@ -1,18 +0,0 @@ - TypeFactory::undefined(), 'TValue' => TypeFactory::unknown()]); - $mapped = $templateMap->mapArguments([TypeFactory::string(), TypeFactory::int()]); - - self::assertEquals(new TemplateMap(['TKey' => TypeFactory::string(), 'TValue' => TypeFactory::int()]), $mapped); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/AggregateTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/AggregateTypeTest.php deleted file mode 100644 index c2ae14d7c7..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/AggregateTypeTest.php +++ /dev/null @@ -1,203 +0,0 @@ -remove($remove)->__toString()); - } - - /** - * @return Generator - */ - public static function provideRemove(): Generator - { - yield [ - [ - ], - new MissingType(), - '' - ]; - - yield 'do not remove existing type' => [ - [ - TypeFactory::string(), - ], - new MissingType(), - 'string' - ]; - - yield 'remove union' => [ - [ - TypeFactory::string(), - TypeFactory::int(), - TypeFactory::class('Foo'), - TypeFactory::class('Bar'), - ], - TypeFactory::union( - TypeFactory::string(), - TypeFactory::class('Bar'), - ), - 'int|Foo', - ]; - } - - /** - * @param Type[] $types - */ - #[DataProvider('provideClean')] - public function testClean(array $types, string $expected): void - { - self::assertEquals($expected, TypeFactory::union(...$types)->clean()->__toString()); - } - - /** - * @return Generator - */ - public static function provideClean(): Generator - { - yield [[], '']; - yield [[TypeFactory::undefined()], '']; - yield [[TypeFactory::string()], 'string']; - - yield [ - [ - TypeFactory::string(), - TypeFactory::string(), - TypeFactory::string(), - TypeFactory::string(), - ], - 'string' - ]; - - yield [ - [ - TypeFactory::string(), - TypeFactory::int(), - TypeFactory::string(), - TypeFactory::string(), - ], - 'string|int' - ]; - } - - public function testFilter(): void - { - $types = TypeFactory::union( - TypeFactory::int(), - TypeFactory::float(), - )->filter(fn (Type $type) => $type instanceof FloatType); - - self::assertEquals(TypeFactory::union(TypeFactory::float()), $types); - } - - /** - * @param Type[] $types - */ - #[DataProvider('provideReduce')] - public function testReduce(array $types, string $expected): void - { - self::assertEquals($expected, TypeFactory::union(...$types)->reduce()->__toString()); - } - - /** - * @return Generator - */ - public static function provideReduce(): Generator - { - yield [[], '']; - yield [[TypeFactory::undefined()], '']; - yield [[TypeFactory::string(), ], 'string']; - - yield 'strips parenthesis' => [ - [ - TypeFactory::parenthesized(TypeFactory::string()), - ], - 'string' - ]; - } - - public function testDeduplicatesTypesOnConstruct(): void - { - self::assertEquals('One|Two', TypeFactory::union( - TypeFactory::class('One'), - TypeFactory::class('Two'), - TypeFactory::class('One'), - TypeFactory::class('Two'), - )->__toString()); - } - - public function testDedupesNullOnConstruct(): void - { - self::assertEquals('null|One|Two', TypeFactory::union( - TypeFactory::nullable(TypeFactory::class('One')), - TypeFactory::class('Two'), - TypeFactory::class('One'), - TypeFactory::null(), - TypeFactory::null(), - )->__toString()); - } - - public function testRemovesPointlessParenthesisForIntersection(): void - { - self::assertEquals('null|One|Two', TypeFactory::union( - TypeFactory::null(), - TypeFactory::intersection(TypeFactory::class('One')), - TypeFactory::class('Two') - )->__toString()); - } - - public function testAddsParenthesisForIntersection(): void - { - self::assertEquals('null|(One&string)|Two', TypeFactory::union( - TypeFactory::null(), - TypeFactory::intersection(TypeFactory::class('One'), TypeFactory::string()), - TypeFactory::class('Two') - )->__toString()); - } - - public function testMergeUnions(): void - { - self::assertEquals( - TypeFactory::union( - TypeFactory::class('Two'), - TypeFactory::class('One'), - TypeFactory::string(), - TypeFactory::int() - ), - TypeFactory::union( - TypeFactory::class('Two'), - TypeFactory::class('One'), - TypeFactory::union( - TypeFactory::string(), - TypeFactory::int() - ) - ) - ); - } - - public function testToPhpString(): void - { - self::assertEquals( - 'Foobar|array', - TypeFactory::union( - TypeFactory::class('Foobar'), - TypeFactory::array(TypeFactory::string()) - )->toPhpString() - ); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/ArrayLiteralTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/ArrayLiteralTypeTest.php deleted file mode 100644 index 0b52e8b493..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/ArrayLiteralTypeTest.php +++ /dev/null @@ -1,56 +0,0 @@ -generalize()->__toString()); - } - - /** - * @return Generator - */ - public static function provideGeneralize(): Generator - { - yield [ - // ['foo','bar'] - TypeFactory::arrayLiteral([ - TypeFactory::stringLiteral('foo'), - TypeFactory::stringLiteral('bar') - ]), - 'array', - ]; - yield [ - TypeFactory::arrayLiteral([ - TypeFactory::arrayLiteral([ - TypeFactory::stringLiteral('one'), - TypeFactory::stringLiteral('two'), - ]), - TypeFactory::arrayLiteral([ - TypeFactory::stringLiteral('one'), - TypeFactory::stringLiteral('two'), - ]), - ]), - 'array>', - ]; - yield [ - // ['foo','bar'] - TypeFactory::arrayLiteral([ - TypeFactory::arrayShape([ - 'foo' => TypeFactory::intLiteral(12), - 'bar' => TypeFactory::intLiteral(12), - ]) - ]), - 'array', - ]; - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/ArrayShapeTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/ArrayShapeTypeTest.php deleted file mode 100644 index 9ecae914bc..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/ArrayShapeTypeTest.php +++ /dev/null @@ -1,43 +0,0 @@ -generalize()->__toString()); - } - - /** - * @return Generator - */ - public static function provideGeneralize(): Generator - { - yield [ - TypeFactory::arrayShape([ - TypeFactory::stringLiteral('foo'), - TypeFactory::stringLiteral('bar') - ]), - 'array{string,string}', - ]; - - yield [ - TypeFactory::arrayShape([ - TypeFactory::stringLiteral('foo'), - TypeFactory::arrayShape([ - 'foo' => TypeFactory::intLiteral(12), - 'bar' => TypeFactory::intLiteral(12), - ]) - ]), - 'array{string,array{foo:int,bar:int}}', - ]; - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/ArrayTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/ArrayTypeTest.php deleted file mode 100644 index db2d843114..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/ArrayTypeTest.php +++ /dev/null @@ -1,38 +0,0 @@ -__toString()); - } - - /** - * @return Generator - */ - public static function provideToString(): Generator - { - yield [ - new ArrayType(new StringType()), - 'string[]', - ]; - yield [ - new ArrayType(null, new StringType()), - 'string[]', - ]; - yield [ - new ArrayType(new IntType(), new StringType()), - 'array', - ]; - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/CallableTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/CallableTypeTest.php deleted file mode 100644 index f3f4557607..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/CallableTypeTest.php +++ /dev/null @@ -1,35 +0,0 @@ -__toString()); - } - - public function testToStringWithReturnType(): void - { - self::assertEquals('callable(): string', (new CallableType([], TypeFactory::string()))->__toString()); - } - - public function testAllTypes(): void - { - $type = new CallableType([ - TypeFactory::string(), - TypeFactory::int(), - ], TypeFactory::string()); - - self::assertEquals(new Types([ - TypeFactory::string(), - TypeFactory::int(), - TypeFactory::string(), - ]), $type->allTypes()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/ClosureTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/ClosureTypeTest.php deleted file mode 100644 index 867352b462..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/ClosureTypeTest.php +++ /dev/null @@ -1,29 +0,0 @@ -build(); - $type = new ClosureType($reflector, [ - TypeFactory::string(), - TypeFactory::int(), - ], TypeFactory::string()); - - self::assertEquals(new Types([ - TypeFactory::reflectedClass($reflector, ClassName::fromString('Closure')), - TypeFactory::string(), - TypeFactory::int(), - TypeFactory::string(), - ]), $type->allTypes()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/GenericClassTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/GenericClassTypeTest.php deleted file mode 100644 index d5931517e1..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/GenericClassTypeTest.php +++ /dev/null @@ -1,45 +0,0 @@ -build(); - $type = new GenericClassType($reflector, ClassName::fromString('Foo'), [ - TypeFactory::string(), - TypeFactory::int(), - ]); - - self::assertEquals(new Types([ - TypeFactory::reflectedClass($reflector, ClassName::fromString('Foo')), - TypeFactory::string(), - TypeFactory::int(), - ]), $type->allTypes()); - } - - public function testAcceptsUnion(): void - { - $reflector = ReflectorBuilder::create()->addSource('build(); - $type1 = new GenericClassType($reflector, ClassName::fromString('Foo'), [ - TypeFactory::reflectedClass($reflector, ClassName::fromString('A')) - ]); - - $type2 = new GenericClassType($reflector, ClassName::fromString('Foo'), [ - TypeFactory::union( - TypeFactory::reflectedClass($reflector, ClassName::fromString('B')), - TypeFactory::reflectedClass($reflector, ClassName::fromString('C')) - ) - ]); - - self::assertTrue($type1->accepts($type2)->isTrue()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/IntersectionTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/IntersectionTypeTest.php deleted file mode 100644 index 86d576b605..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/IntersectionTypeTest.php +++ /dev/null @@ -1,54 +0,0 @@ -accepts($type)); - } - - /** - * @return Generator - */ - public static function provideAccepts(): Generator - { - yield 'does not accept non-class types' => [ - TypeFactory::intersection(TypeFactory::intLiteral(12), TypeFactory::string()), - TypeFactory::int(), - Trinary::false(), - ]; - - yield 'does not accept single type' => [ - TypeFactory::intersection(TypeFactory::class('Foobar'), TypeFactory::class('Barfoo')), - TypeFactory::class('Barfoo'), - Trinary::false(), - ]; - - yield 'accepts intersection' => [ - TypeFactory::intersection(TypeFactory::class('Foobar'), TypeFactory::class('Barfoo')), - TypeFactory::intersection(TypeFactory::class('Foobar'), TypeFactory::class('Barfoo')), - Trinary::true(), - ]; - - $reflector = ReflectorBuilder::create() - ->addSource('build(); - yield 'accepts class that implements intersection interface' => [ - TypeFactory::intersection(TypeFactory::class('B'), TypeFactory::class('C')), - TypeFactory::reflectedClass($reflector, 'A'), - Trinary::true(), - ]; - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/NumericTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/NumericTypeTest.php deleted file mode 100644 index 779dca1941..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/NumericTypeTest.php +++ /dev/null @@ -1,21 +0,0 @@ -divide(new IntLiteralType(0))->value()); - self::assertEquals(0, (new IntLiteralType(0))->divide(new IntLiteralType(1))->value()); - } - - public function testDivisionByNonLiteral(): void - { - self::assertEquals(1, (new IntLiteralType(1))->divide(new IntType())->value()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/ReflectedClassTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/ReflectedClassTypeTest.php deleted file mode 100644 index 66b78ae403..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/ReflectedClassTypeTest.php +++ /dev/null @@ -1,104 +0,0 @@ - [ - $this->createType( - 'accepts(TypeFactory::class('Bar'))->isTrue()); - } - ]; - yield 'accepts class which extends it' => [ - $this->createType( - 'accepts(TypeFactory::class('Foobar'))->isTrue()); - } - ]; - yield 'rejects class which implements it' => [ - $this->createType( - 'accepts(TypeFactory::class('Foobar'))->isTrue()); - } - ]; - yield 'rejects class which is not it' => [ - $this->createType( - 'accepts(TypeFactory::class('Foobar'))->isFalse()); - } - ]; - - yield 'interface accepts class which implements it' => [ - $this->createType( - 'accepts(TypeFactory::class('Foobar'))->isTrue()); - } - ]; - } - - public function testInstanceOf(): void - { - // is extends - self::assertTrinaryTrue($this->createType( - 'instanceof(TypeFactory::class('Bar'))); - - // is not instance of - self::assertTrinaryFalse($this->createType( - 'instanceof(TypeFactory::class('Baz'))); - - // is possibly instance of because we can't reflect the class - self::assertTrinaryMaybe($this->createType( - '', - 'Foobar' - )->instanceof(TypeFactory::class('Baz'))); - } - - private function createType(string $source, string $name): ReflectedClassType - { - return new ReflectedClassType( - ReflectorBuilder::create()->addSource($source)->build(), - ClassName::fromUnknown($name) - ); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/StringLiteralTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/StringLiteralTypeTest.php deleted file mode 100644 index 4e1e7b2815..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/StringLiteralTypeTest.php +++ /dev/null @@ -1,23 +0,0 @@ -value(), -3) - ); - $value = str_repeat('a', 356); - self::assertEquals( - '...', - substr((new StringLiteralType($value))->value(), -3) - ); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Type/UnionTypeTest.php b/lib/WorseReflection/Tests/Unit/Core/Type/UnionTypeTest.php deleted file mode 100644 index 120210106b..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Type/UnionTypeTest.php +++ /dev/null @@ -1,62 +0,0 @@ -accepts($type)); - } - - /** - * @return Generator - */ - public static function provideAccepts(): Generator - { - yield [ - TypeFactory::union(TypeFactory::int(), TypeFactory::string()), - TypeFactory::int(), - Trinary::true(), - ]; - yield [ - TypeFactory::union(TypeFactory::int(), TypeFactory::string()), - TypeFactory::class('Foobar'), - Trinary::false(), - ]; - yield 'int literal maybe accepts int' => [ - TypeFactory::union(TypeFactory::intLiteral(12), TypeFactory::string()), - TypeFactory::int(), - Trinary::maybe(), - ]; - yield 'string literal maybe string' => [ - TypeFactory::union(TypeFactory::stringLiteral('foo')), - TypeFactory::string(), - Trinary::maybe(), - ]; - yield 'bool literal maybe bool' => [ - TypeFactory::union(TypeFactory::boolLiteral(true)), - TypeFactory::bool(), - Trinary::maybe(), - ]; - yield 'float literal maybe float' => [ - TypeFactory::union(TypeFactory::floatLiteral(12.2)), - TypeFactory::float(), - Trinary::maybe(), - ]; - yield 'boolean true is not empty' => [ - TypeFactory::union(TypeFactory::unionEmpty()), - TypeFactory::boolLiteral(true), - Trinary::false(), - ]; - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/TypeFactoryTest.php b/lib/WorseReflection/Tests/Unit/Core/TypeFactoryTest.php deleted file mode 100644 index d73c743edc..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/TypeFactoryTest.php +++ /dev/null @@ -1,221 +0,0 @@ -assertEquals($toString, (string) $type, '__toString()'); - $this->assertEquals($phpType, $type->toPhpString(), 'phptype'); - } - - public static function provideToString(): Generator - { - $reflector = ReflectorBuilder::create()->build(); - yield [ - TypeFactory::fromString('string'), - 'string', - 'string', - ]; - - yield [ - TypeFactory::fromString('class-string'), - 'class-string', - 'string', - ]; - - yield [ - TypeFactory::fromString('float'), - 'float', - 'float', - ]; - - yield [ - TypeFactory::fromString('int'), - 'int', - 'int', - ]; - - yield [ - TypeFactory::fromString('bool'), - 'bool', - 'bool', - ]; - - yield [ - TypeFactory::fromString('array'), - 'array', - 'array', - ]; - - yield [ - TypeFactory::fromString('void'), - 'void', - 'void', - ]; - - yield [ - TypeFactory::fromString('Foobar'), - 'Foobar', - 'Foobar' - ]; - - yield [ - TypeFactory::fromString('mixed'), - 'mixed', - 'mixed' - ]; - - yield 'Collection' => [ - TypeFactory::collection($reflector, 'Foobar', TypeFactory::string()), - 'Foobar', - 'Foobar', - ]; - - yield 'Typed array' => [ - TypeFactory::array(TypeFactory::string()), - 'string[]', - 'array', - ]; - - yield 'Nullable string' => [ - TypeFactory::fromString('?string'), - '?string', - '?string', - ]; - - yield 'Nullable class' => [ - TypeFactory::fromString('?Foobar'), - '?Foobar', - '?Foobar', - ]; - - yield 'Nullable iterable class' => [ - TypeFactory::nullable(TypeFactory::collection($reflector, 'Foo', 'Bar')), - '?Foo', - '?Foo', - ]; - - yield 'callable' => [ - TypeFactory::fromString('callable'), - 'callable()', - 'callable' - ]; - - yield 'iterable' => [ - TypeFactory::fromString('iterable'), - 'iterable', - 'iterable' - ]; - - yield 'resource' => [ - TypeFactory::fromString('resource'), - 'resource', - 'resource' - ]; - - yield 'class-string' => [ - TypeFactory::fromString('class-string'), - 'class-string', - 'string', - ]; - - yield 'list' => [ - TypeFactory::fromString('class-string'), - 'class-string', - 'string', - ]; - - yield 'false' => [ - TypeFactory::fromString('false'), - 'false', - 'false', - ]; - } - - #[DataProvider('provideValues')] - public function testItCanBeCreatedFromAValue($value, Type $expectedType): void - { - $type = TypeFactory::fromValue($value); - $this->assertEquals($expectedType, $type); - } - - public static function provideValues(): Generator - { - yield [ - 'string', - TypeFactory::stringLiteral('string'), - ]; - - yield [ - 11, - TypeFactory::intLiteral(11), - ]; - - yield [ - 11.2, - TypeFactory::floatLiteral(11.2), - ]; - - yield [ - [], - TypeFactory::array(), - ]; - - yield [ - true, - TypeFactory::boolLiteral(true), - ]; - - yield [ - false, - TypeFactory::boolLiteral(false), - ]; - - yield [ - null, - TypeFactory::null(), - ]; - - yield [ - new stdClass(), - TypeFactory::class(ClassName::fromString('stdClass')), - ]; - - yield 'resource' => [ - \fopen(__FILE__, 'r'), - TypeFactory::resource(), - ]; - - yield 'callable' => [ - function (): void { - }, - TypeFactory::callable(), - ]; - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Virtual/StubMemberProviderTest.php b/lib/WorseReflection/Tests/Unit/Core/Virtual/StubMemberProviderTest.php deleted file mode 100644 index 32f83611f7..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Virtual/StubMemberProviderTest.php +++ /dev/null @@ -1,73 +0,0 @@ -createReflector($stubs); - - $classes = $reflector->reflectClassesIn( - TextDocumentBuilder::fromUri(__DIR__ . '/example/model.php.test')->build() - ); - - $reflection = $classes->get('Example\Model'); - self::assertEquals('static(Example\Model)|false', $reflection->methods()->get('findOne')->inferredType()->__toString()); - } - - public function testProviderExtended(): void - { - $stubs = [__DIR__ . '/example/model.stub']; - $reflector = $this->createReflector($stubs); - - $classes = $reflector->reflectClassesIn( - TextDocumentBuilder::fromUri(__DIR__ . '/example/model.php.test')->build() - ); - - $reflection = $classes->get('Example\Blog'); - self::assertEquals( - 'static(Example\Blog)|false', - $reflection->methods()->get( - 'findOne' - )->inferredType()->__toString() - ); - } - - public function testProvideVirtualMethodsFromStubs(): void - { - $stubs = [__DIR__ . '/example/model.stub']; - $reflector = $this->createReflector($stubs); - - $classes = $reflector->reflectClassesIn( - TextDocumentBuilder::fromUri(__DIR__ . '/example/model.php.test')->build() - ); - - $reflection = $classes->get('Example\Blog'); - self::assertEquals( - 'string', - $reflection->properties()->get('virtualString')->inferredType()->__toString() - ); - } - - /** - * @param string[] $stubs - */ - private function createReflector(array $stubs): Reflector - { - $reflector = ReflectorBuilder::create() - ->enableContextualSourceLocation() - ->addMemberProvider( - new StubFileMemberProvider($stubs) - ) - ->build(); - return $reflector; - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionMemberTestCase.php b/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionMemberTestCase.php deleted file mode 100644 index 6f6434fa73..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionMemberTestCase.php +++ /dev/null @@ -1,99 +0,0 @@ -position = ByteOffsetRange::fromInts(0, 0); - $this->declaringClass = $this->prophesize(ReflectionClass::class); - $this->class = $this->prophesize(ReflectionClass::class); - $this->name = 'test_name'; - $this->frame = $this->prophesize(Frame::class); - $this->docblock = $this->prophesize(DocBlock::class); - $this->scope = $this->prophesize(ReflectionScope::class); - $this->visibility = Visibility::public(); - $this->type = TypeFactory::unknown(); - } - - abstract public function member(): ReflectionMember; - - public function testPosition(): void - { - $this->assertSame($this->position, $this->member()->position()); - } - - public function testDeclaringClass(): void - { - $this->assertSame($this->declaringClass->reveal(), $this->member()->declaringClass()); - } - - public function testClass(): void - { - $this->assertSame($this->class->reveal(), $this->member()->class()); - } - - public function testName(): void - { - $this->assertEquals($this->name, $this->member()->name()); - } - - public function testFrame(): void - { - $this->assertEquals($this->frame->reveal(), $this->member()->frame()); - } - - public function testDocblock(): void - { - $this->assertEquals($this->docblock->reveal(), $this->member()->docblock()); - } - - public function testScope(): void - { - $this->assertEquals($this->scope->reveal(), $this->member()->scope()); - } - - public function testVisibility(): void - { - $this->assertEquals($this->visibility, $this->member()->visibility()); - } - - public function testType(): void - { - $this->assertEquals($this->type, $this->member()->type()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionMethodTest.php b/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionMethodTest.php deleted file mode 100644 index a59afcde6a..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionMethodTest.php +++ /dev/null @@ -1,85 +0,0 @@ -parameters = ReflectionParameterCollection::empty(); - $this->body = NodeText::fromString('hello'); - $this->isAbstract = true; - $this->isStatic = true; - } - - /** - * @return ReflectionMethod - */ - public function member(): ReflectionMember - { - return new VirtualReflectionMethod( - $this->position, - $this->declaringClass->reveal(), - $this->class->reveal(), - $this->name, - $this->frame->reveal(), - $this->docblock->reveal(), - $this->scope->reveal(), - $this->visibility, - $this->type, - $this->type, - $this->parameters, - $this->body, - $this->isAbstract, - $this->isStatic, - new Deprecation(false) - ); - } - - public function testParameters(): void - { - $this->assertEquals($this->parameters, $this->member()->parameters()); - } - - public function testBody(): void - { - $this->assertEquals($this->body, $this->member()->body()); - } - - public function testIsAbstract(): void - { - $this->assertEquals($this->isAbstract, $this->member()->isAbstract()); - } - - public function testIsStatic(): void - { - $this->assertEquals($this->isStatic, $this->member()->isStatic()); - } - - public function testVirtual(): void - { - $this->assertTrue($this->member()->isStatic()); - } - - public function testReturnType(): void - { - $this->assertEquals(TypeFactory::unknown(), $this->member()->returnType()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionParameterTest.php b/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionParameterTest.php deleted file mode 100644 index 79dfdbebb9..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Virtual/VirtualReflectionParameterTest.php +++ /dev/null @@ -1,81 +0,0 @@ - */ - private ObjectProphecy $class; - - private string $name; - - /** @var ObjectProphecy */ - private ObjectProphecy $scope; - - private Type $type; - - /** @var ObjectProphecy */ - private ObjectProphecy $method; - - private DefaultValue $defaults; - - private bool $byReference; - - public function setUp(): void - { - $this->position = ByteOffsetRange::fromInts(0, 0); - $this->class = $this->prophesize(ReflectionClass::class); - $this->name = 'test_name'; - $this->scope = $this->prophesize(ReflectionScope::class); - $this->type = TypeFactory::unknown(); - $this->method = $this->prophesize(ReflectionMethod::class); - $this->defaults = DefaultValue::fromValue(1234); - $this->byReference = false; - } - - public function parameter(): ReflectionParameter - { - return new VirtualReflectionParameter( - $this->name, - $this->method->reveal(), - $this->type, - $this->type, - $this->defaults, - $this->byReference, - $this->scope->reveal(), - $this->position, - 0 - ); - } - - public function testAccess(): void - { - $parameter = $this->parameter(); - $this->assertEquals($this->name, $parameter->name()); - $this->assertEquals($this->method->reveal(), $parameter->functionLike()); - $this->assertEquals($this->type, $parameter->inferredType()); - $this->assertEquals($this->type, $parameter->type()); - $this->assertEquals($this->defaults, $parameter->default()); - $this->assertEquals($this->byReference, $parameter->byReference()); - $this->assertEquals($this->scope->reveal(), $parameter->scope()); - $this->assertEquals($this->position, $parameter->position()); - $this->assertEquals(0, $parameter->index()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/Core/Virtual/example/model.php.test b/lib/WorseReflection/Tests/Unit/Core/Virtual/example/model.php.test deleted file mode 100644 index a9bbc1cf0e..0000000000 --- a/lib/WorseReflection/Tests/Unit/Core/Virtual/example/model.php.test +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public static function query(\Phalcon\DiInterface $dependencyInjector = null); - /** - * @return static|false - */ - public static function findOne() - { - } -} - diff --git a/lib/WorseReflection/Tests/Unit/ReflectorBuilderTest.php b/lib/WorseReflection/Tests/Unit/ReflectorBuilderTest.php deleted file mode 100644 index 9fa53048af..0000000000 --- a/lib/WorseReflection/Tests/Unit/ReflectorBuilderTest.php +++ /dev/null @@ -1,131 +0,0 @@ -build(); - $this->assertInstanceOf(Reflector::class, $reflector); - } - - public function testReplacesLogger(): void - { - $logger = $this->prophesize(LoggerInterface::class); - $reflector = ReflectorBuilder::create() - ->withLogger($logger->reveal()) - ->build(); - - $this->assertInstanceOf(Reflector::class, $reflector); - } - - public function testHasOneLocator(): void - { - $locator = $this->prophesize(SourceCodeLocator::class); - $reflector = ReflectorBuilder::create() - ->addLocator($locator->reveal()) - ->build(); - - $this->assertInstanceOf(Reflector::class, $reflector); - } - - public function testHasManyLocators(): void - { - $locator = $this->prophesize(SourceCodeLocator::class); - $reflector = ReflectorBuilder::create() - ->addLocator($locator->reveal()) - ->addLocator($locator->reveal()) - ->build(); - - $this->assertInstanceOf(Reflector::class, $reflector); - } - - public function testHighestPriorityLocatorWins(): void - { - $locator1 = $this->prophesize(SourceCodeLocator::class); - $locator2 = $this->prophesize(SourceCodeLocator::class); - $locator3 = $this->prophesize(SourceCodeLocator::class); - - $reflector = ReflectorBuilder::create() - ->addLocator($locator1->reveal(), 0) - ->addLocator($locator2->reveal(), 10) - ->addLocator($locator3->reveal(), -10) - ->build(); - - $locator1->locate(Argument::any())->shouldNotBeCalled(); - $locator2->locate(Argument::any())->willReturn(TextDocumentBuilder::create(file_get_contents(__FILE__))->build()); - $locator3->locate(Argument::any())->shouldNotBeCalled(); - - $this->assertInstanceOf(Reflector::class, $reflector); - $reflector->reflectClass(__CLASS__); - } - - public function testWithSource(): void - { - $reflector = ReflectorBuilder::create() - ->addSource('build(); - - $class = $reflector->reflectClass('Foobar'); - $this->assertEquals('Foobar', $class->name()->__toString()); - $this->assertInstanceOf(Reflector::class, $reflector); - } - - public function testInternalLocatorGetsHighestPriority(): void - { - $reflector = ReflectorBuilder::create() - ->addLocator(new StringSourceLocator( - TextDocumentBuilder::create('build() - ), 100) - ->build(); - - $class = $reflector->reflectInterface('BackedEnum'); - $this->assertEquals('BackedEnum', $class->name()->__toString()); - $this->assertStringContainsString('InternalStubs', $class->sourceCode()->uri()->path()); - } - - public function testEnableCache(): void - { - $reflector = ReflectorBuilder::create() - ->enableCache() - ->build(); - - $this->assertInstanceOf(Reflector::class, $reflector); - } - - public function testEnableContextualSourceLocation(): void - { - $reflector = ReflectorBuilder::create() - ->enableContextualSourceLocation() - ->build(); - - $this->assertInstanceOf(Reflector::class, $reflector); - } - - public function testContextualSourceLocationLocatesFunctions(): void - { - $reflector = ReflectorBuilder::create() - ->enableContextualSourceLocation() - ->build(); - - $source = TextDocumentBuilder::create( - 'build(); - $reflector->reflectFunctionsIn($source); - - $this->assertEquals('Foobar\barfoo', $reflector->reflectFunction('Foobar\barfoo')->name()->__toString()); - } -} diff --git a/lib/WorseReflection/Tests/Unit/TypeUtilTest.php b/lib/WorseReflection/Tests/Unit/TypeUtilTest.php deleted file mode 100644 index 9fa7bbd6dc..0000000000 --- a/lib/WorseReflection/Tests/Unit/TypeUtilTest.php +++ /dev/null @@ -1,160 +0,0 @@ -addSource($source)->build(); - $class = $reflector->reflectClassLike('Foo'); - self::assertEquals( - $expected, - (string)$type->toLocalType($class->scope()) - ); - } - - public static function provideToLocalType(): Generator - { - $reflector = ReflectorBuilder::create()->build(); - yield [ - '', - ]; - } - - #[DataProvider('provideShort')] - public function testShort(Type $type, string $expected): void - { - self::assertEquals( - $expected, - $type->short(), - ); - } - - public static function provideShort(): Generator - { - yield 'scalar' => [ - TypeFactory::string(), - 'string', - ]; - - yield 'Root class' => [ - TypeFactory::class('Foo'), - 'Foo', - ]; - yield 'Namespaced class' => [ - TypeFactory::class('\Foo\Bar'), - 'Bar', - ]; - yield 'Union' => [ - TypeFactory::union( - TypeFactory::class('\Foo\Bar'), - ), - 'Bar', - ]; - yield 'Union with two elements' => [ - TypeFactory::union( - TypeFactory::class('\Foo\Bar'), - TypeFactory::class('\Foo\Baz'), - ), - 'Bar|Baz', - ]; - } - - #[DataProvider('provideShortenClassTypes')] - public function testShortenClassTypes(Type $type, string $expected): void - { - self::assertEquals( - $expected, - TypeUtil::shortenClassTypes($type)->__toString() - ); - } - - public static function provideShortenClassTypes(): Generator - { - yield 'scalar' => [ - TypeFactory::string(), - 'string', - ]; - - yield 'Root class' => [ - TypeFactory::class('Foo'), - 'Foo', - ]; - yield 'Namespaced class' => [ - TypeFactory::class('\Foo\Bar'), - 'Bar', - ]; - yield 'Union' => [ - TypeFactory::union( - TypeFactory::class('\Foo\Bar'), - ), - 'Bar', - ]; - yield 'Union with two elements' => [ - TypeFactory::union( - TypeFactory::class('\Foo\Bar'), - TypeFactory::class('\Foo\Baz'), - ), - 'Bar|Baz', - ]; - yield 'Static' => [ - TypeFactory::static( - TypeFactory::class('\Foo\Bar'), - ), - 'static(Bar)', - ]; - yield 'This' => [ - TypeFactory::this( - TypeFactory::class('\Foo\Bar'), - ), - '$this(Bar)', - ]; - yield 'Nullable' => [ - TypeFactory::nullable( - TypeFactory::class('\Foo\Bar'), - ), - '?Bar', - ]; - } -} diff --git a/lib/WorseReflection/TypeUtil.php b/lib/WorseReflection/TypeUtil.php deleted file mode 100644 index cd77ba989f..0000000000 --- a/lib/WorseReflection/TypeUtil.php +++ /dev/null @@ -1,135 +0,0 @@ -isDefined()) { - return $type; - } - } - - return $type; - } - - /** - * @return mixed - */ - public static function valueOrNull(Type $type) - { - if ($type instanceof Literal) { - return $type->value(); - } - - return null; - } - - public static function toBool(Type $type): BooleanType - { - if ($type instanceof Literal) { - return new BooleanLiteralType((bool)$type->value()); - } - if ($type instanceof NullType) { - return new BooleanLiteralType(false); - } - if ($type instanceof BooleanType) { - return $type; - } - - return new BooleanType(); - } - - public static function toNumber(Type $type): NumericType - { - if ($type instanceof NumericType) { - return $type; - } - if ($type instanceof Literal && $type instanceof ScalarType) { - $value = (string)$type->value(); - return TypeFactory::fromNumericString($value); - } - return new IntType(); - } - - public static function trinaryToBoolean(Trinary $trinary): BooleanType - { - if ($trinary->isTrue()) { - return new BooleanLiteralType(true); - } - if ($trinary->isFalse()) { - return new BooleanLiteralType(false); - } - - return new BooleanType(); - } - - public static function shortenClassTypes(Type $type): Type - { - return $type->map(function (Type $type) { - if ($type instanceof ClassType) { - return TypeFactory::class($type->name()->short()); - } - - return $type; - }); - } - - /** - * @param Type[] $types - */ - public static function generalTypeFromTypes(array $types): Type - { - $valueType = null; - foreach ($types as $type) { - $type = $type->generalize(); - if ($valueType === null) { - $valueType = $type; - continue; - } - - if ($valueType != $type) { - return new MixedType(); - } - } - - return $valueType ?: new MissingType(); - } - - public static function contains(string $string, Type $type): bool - { - if ($type instanceof $string) { - return true; - } - if ($type instanceof AggregateType) { - foreach ($type->expandTypes() as $type) { - if ($type instanceof $string) { - return true; - } - } - } - - return false; - } -} diff --git a/phpbench.json b/phpbench.json deleted file mode 100644 index 63f832d019..0000000000 --- a/phpbench.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "$schema": "vendor/phpbench/phpbench/phpbench.schema.json", - "runner.file_pattern": "*Bench.php", - "runner.php_config": { - "memory_limit": -1 - }, - "runner.annotation_import_use": false, - "runner.path": [ - "lib/**/Tests/Benchmark", - "lib/**/Tests/Benchmarks", - "tests/Benchmark" - ], - "runner.bootstrap": "vendor/autoload.php", - "core.extensions": [ - "PhpBench\\Extensions\\XDebug\\XDebugExtension" - ], - "report.generators": { - "github-action-benchmark": { - "cols": [ - "name", - "unit", - "value", - "range", - "extra" - ], - "expressions": { - "name": "format('%s::%s%s', first(benchmark_name),first(subject_name),if(first(variant_name) != false, format(' (%s)', first(variant_name)), ''))", - "unit": "coalesce(time_unit(first(subject_time_unit), true),'µs')", - "value": "time_convert(mode(result_time_avg), 'us', time_unit(first(subject_time_unit)))", - "range": "format('± %.2f%%', rstdev(result_time_avg))", - "extra": "format('%d iterations, %d revs', first(variant_iterations), first(variant_revs))" - }, - "aggregate": [ - "suite_tag", - "benchmark_class", - "subject_name", - "variant_index" - ], - "generator": "expression" - } - } -} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon deleted file mode 100644 index 3f2ee08479..0000000000 --- a/phpstan-baseline.neon +++ /dev/null @@ -1,15559 +0,0 @@ -parameters: - ignoreErrors: - - - message: '#^Parameter \#1 \$args of class Phpactor\\Amp\\Process\\ProcessBuilder constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Amp/Process/ProcessBuilder.php - - - - message: '#^Cannot call method dump\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Application.php - - - - message: '#^Cannot call method error\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Application.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Application.php - - - - message: '#^Cannot call method pushHandler\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Application.php - - - - message: '#^Parameter \#1 \$commandLoader of method Symfony\\Component\\Console\\Application\:\:setCommandLoader\(\) expects Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Application.php - - - - message: '#^Access to an undefined property Microsoft\\PhpParser\\Node\\Expression\\BracedExpression\|Microsoft\\PhpParser\\Node\\Expression\\Variable\|Microsoft\\PhpParser\\Token\:\:\$start\.$#' - identifier: property.notFound - count: 1 - path: lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberFinder.php - - - - message: '#^Parameter \#1 \$memberName of method Phpactor\\ClassMover\\Domain\\Model\\ClassMemberQuery\:\:matchesMemberName\(\) expects string, bool\|string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberFinder.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\:\:has\(\) expects string, Phpactor\\ClassMover\\Domain\\Name\\MemberName\|null given\.$#' - identifier: argument.type - count: 2 - path: lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberFinder.php - - - - message: '#^Property Phpactor\\ClassMover\\Adapter\\WorseTolerant\\WorseTolerantMemberFinder\:\:\$reflector \(Phpactor\\WorseReflection\\Reflector\) does not accept Phpactor\\WorseReflection\\Reflector\|Phpactor\\WorseReflection\\ReflectorBuilder\.$#' - identifier: assign.propertyType - count: 1 - path: lib/ClassMover/Adapter/WorseTolerant/WorseTolerantMemberFinder.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/ClassMover/Domain/Name/Label.php - - - - message: '#^Strict comparison using \=\=\= between int\<1, max\> and 0 will always evaluate to false\.$#' - identifier: identical.alwaysFalse - count: 1 - path: lib/ClassMover/Domain/Name/Namespace_.php - - - - message: '#^Parameter \#1 \$parts of class Phpactor\\ClassMover\\Domain\\Name\\QualifiedName constructor expects non\-empty\-array\, array\, non\-falsy\-string\> given\.$#' - identifier: argument.type - count: 1 - path: lib/ClassMover/Domain/Name/QualifiedName.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 3 - path: lib/ClassMover/Domain/Name/QualifiedName.php - - - - message: '#^Cannot call method end\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/ClassMover/Tests/Adapter/WorseTolerant/WorseTolerantMemberFinderTest.php - - - - message: '#^Cannot call method position\(\) on mixed\.$#' - identifier: method.nonObject - count: 6 - path: lib/ClassMover/Tests/Adapter/WorseTolerant/WorseTolerantMemberFinderTest.php - - - - message: '#^Cannot call method start\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/ClassMover/Tests/Adapter/WorseTolerant/WorseTolerantMemberFinderTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\ClassMover\\\\FoundReferences'' and Phpactor\\ClassMover\\FoundReferences will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/ClassMover/Tests/Unit/ClassMoverTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\ClassMover\\\\ClassMover'' and Phpactor\\ClassMover\\ClassMover will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/ClassMover/Tests/Unit/Extension/ClassMoverExtensionTest.php - - - - message: '#^Access to an undefined property Microsoft\\PhpParser\\Node\:\:\$name\.$#' - identifier: property.notFound - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php - - - - message: '#^Cannot call method getText\(\) on Microsoft\\PhpParser\\Node\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php - - - - message: '#^Cannot call method getText\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php - - - - message: '#^PHPDoc tag @var has invalid value \(\$namespaceNode NamespaceDefinition\)\: Unexpected token "\$namespaceNode", expected type at offset 9 on line 1$#' - identifier: phpDoc.parseError - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php - - - - message: '#^Parameter \#1 \$node of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Edits\:\:after\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token, Microsoft\\PhpParser\\Node\|null given\.$#' - identifier: argument.type - count: 4 - path: lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php - - - - message: '#^Parameter \#2 \$prototype of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\TolerantUpdater\:\:updateClasses\(\) expects Phpactor\\CodeBuilder\\Domain\\Prototype\\SourceCode, Phpactor\\CodeBuilder\\Domain\\Prototype\\Prototype given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php - - - - message: '#^Parameter \#2 \$prototype of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\TolerantUpdater\:\:updateNamespace\(\) expects Phpactor\\CodeBuilder\\Domain\\Prototype\\SourceCode, Phpactor\\CodeBuilder\\Domain\\Prototype\\Prototype given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php - - - - message: '#^Parameter \#2 \$prototype of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\UseStatementUpdater\:\:updateUseStatements\(\) expects Phpactor\\CodeBuilder\\Domain\\Prototype\\SourceCode, Phpactor\\CodeBuilder\\Domain\\Prototype\\Prototype given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php - - - - message: '#^Access to an undefined property TMembersNodeType of Microsoft\\PhpParser\\Node\:\:\$openBrace\.$#' - identifier: property.notFound - count: 2 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php - - - - message: '#^Instanceof between Phpactor\\CodeBuilder\\Domain\\Prototype\\Parameter and Phpactor\\CodeBuilder\\Domain\\Prototype\\Parameter will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php - - - - message: '#^Parameter \#1 \$node of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Edits\:\:after\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token, mixed given\.$#' - identifier: argument.type - count: 4 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php - - - - message: '#^Parameter \#3 \$bodyNode of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\AbstractMethodUpdater\\:\:appendLinesToMethod\(\) expects Microsoft\\PhpParser\\Node, Microsoft\\PhpParser\\Node\\Statement\\CompoundStatementNode\|Microsoft\\PhpParser\\Token given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php - - - - message: '#^Ternary operator condition is always true\.$#' - identifier: ternary.alwaysTrue - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/AbstractMethodUpdater.php - - - - message: '#^Access to an undefined property Microsoft\\PhpParser\\Node\:\:\$openBrace\.$#' - identifier: property.notFound - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassLikeUpdater.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\ClassLikeUpdater\:\:getInsertPlace\(\) should return Microsoft\\PhpParser\\Token but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassLikeUpdater.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\ClassLikeUpdater\:\:resolvePropertyName\(\) should return string\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassLikeUpdater.php - - - - message: '#^Parameter \#1 \$property of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\ClassLikeUpdater\:\:resolvePropertyName\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassLikeUpdater.php - - - - message: '#^Access to an undefined property Microsoft\\PhpParser\\Node\:\:\$classMemberDeclarations\.$#' - identifier: property.notFound - count: 2 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Access to an undefined property Microsoft\\PhpParser\\Node\:\:\$openBrace\.$#' - identifier: property.notFound - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Cannot call method getElements\(\) on Microsoft\\PhpParser\\Node\\DelimitedList\\QualifiedNameList\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Cannot call method getName\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Cannot call method getText\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Instanceof between Phpactor\\CodeBuilder\\Domain\\Prototype\\Constant and Phpactor\\CodeBuilder\\Domain\\Prototype\\Constant will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Instanceof between Phpactor\\CodeBuilder\\Domain\\Prototype\\ImplementsInterfaces and Phpactor\\CodeBuilder\\Domain\\Prototype\\ImplementsInterfaces will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\ClassUpdater\:\:memberDeclarations\(\) should return array\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Parameter \#1 \$array of function next expects array\|object, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Parameter \#1 \$array of function prev expects array\|object, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Parameter \#1 \$node of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Edits\:\:after\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php - - - - message: '#^Access to an undefined property Microsoft\\PhpParser\\ClassLike\:\:\$interfaceMembers\.$#' - identifier: property.notFound - count: 2 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceMethodUpdater.php - - - - message: '#^Cannot access property \$interfaceMemberDeclarations on mixed\.$#' - identifier: property.nonObject - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceMethodUpdater.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\InterfaceMethodUpdater\:\:memberDeclarations\(\) should return array\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceMethodUpdater.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\InterfaceMethodUpdater\:\:memberDeclarationsNode\(\) should return Microsoft\\PhpParser\\Node\\InterfaceMembers but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceMethodUpdater.php - - - - message: '#^Property Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\InterfaceUpdater\:\:\$renderer is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/InterfaceUpdater.php - - - - message: '#^Access to an undefined property Microsoft\\PhpParser\\Node\:\:\$traitMemberDeclarations\.$#' - identifier: property.notFound - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/TraitUpdater.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\TraitUpdater\:\:memberDeclarations\(\) should return array\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/TraitUpdater.php - - - - message: '#^Cannot call method getStartPosition\(\) on Microsoft\\PhpParser\\Node\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/UseStatementUpdater.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\UseStatementUpdater\:\:filterExisting\(\) should return list\ but returns array\\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/UseStatementUpdater.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\UseStatementUpdater\:\:filterSameNamespace\(\) should return list\ but returns array\\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/UseStatementUpdater.php - - - - message: '#^Parameter \#1 \$node of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Edits\:\:after\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token, Microsoft\\PhpParser\\Node\|null given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/UseStatementUpdater.php - - - - message: '#^Parameter \#2 \$lastNode of method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Updater\\UseStatementUpdater\:\:resolveUseStatements\(\) expects Microsoft\\PhpParser\\Node, Microsoft\\PhpParser\\Node\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Updater/UseStatementUpdater.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 3 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php - - - - message: '#^Class Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Util\\ImportedNames implements generic interface IteratorAggregate but does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Util\\ImportedNames\:\:classNames\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Util\\ImportedNames\:\:classNamesFromNode\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Util\\ImportedNames\:\:functionNames\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php - - - - message: '#^Property Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Util\\ImportedNames\:\:\$table type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/ImportedNames.php - - - - message: '#^Cannot call method getNameParts\(\) on Microsoft\\PhpParser\\ResolvedName\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Util\\NodeHelper\:\:resolvedShortName\(\) should return string but returns bool\|string\|null\.$#' - identifier: return.type - count: 2 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Util\\NodeHelper\:\:resolvedShortName\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php - - - - message: '#^Parameter \#1 \$array of function array_pop expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php - - - - message: '#^Parameter \#1 \$array of function reset expects array\|object, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/CodeBuilder/Adapter/TolerantParser/Util/NodeHelper.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/CodeBuilder/Adapter/WorseReflection/WorseBuilderFactory.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionProperty and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionProperty will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeBuilder/Adapter/WorseReflection/WorseBuilderFactory.php - - - - message: '#^Parameter \#1 \$classBuilder of method Phpactor\\CodeBuilder\\Adapter\\WorseReflection\\WorseBuilderFactory\:\:buildMethod\(\) expects Phpactor\\CodeBuilder\\Domain\\Builder\\ClassLikeBuilder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/WorseReflection/WorseBuilderFactory.php - - - - message: '#^Parameter \#1 \$classBuilder of method Phpactor\\CodeBuilder\\Adapter\\WorseReflection\\WorseBuilderFactory\:\:buildProperty\(\) expects Phpactor\\CodeBuilder\\Domain\\Builder\\ClassLikeBuilder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Adapter/WorseReflection/WorseBuilderFactory.php - - - - message: '#^Argument of an invalid type \$this\(Phpactor\\CodeBuilder\\Domain\\Builder\\AbstractBuilder\) supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/CodeBuilder/Domain/Builder/AbstractBuilder.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/CodeBuilder/Domain/Builder/AbstractBuilder.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/CodeBuilder/Domain/Builder/AbstractBuilder.php - - - - message: '#^Property Phpactor\\CodeBuilder\\Domain\\Builder\\AbstractBuilder\:\:\$originalProperties has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/CodeBuilder/Domain/Builder/AbstractBuilder.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Builder\\ClassBuilder\:\:constant\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeBuilder/Domain/Builder/ClassBuilder.php - - - - message: '#^Parameter \#1 \$constants of static method Phpactor\\CodeBuilder\\Domain\\Prototype\\Constants\:\:fromConstants\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Builder/ClassBuilder.php - - - - message: '#^Parameter \#1 \$types of static method Phpactor\\CodeBuilder\\Domain\\Prototype\\ExtendsInterfaces\:\:fromTypes\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Builder/InterfaceBuilder.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Builder\\ParameterBuilder\:\:defaultValue\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeBuilder/Domain/Builder/ParameterBuilder.php - - - - message: '#^Parameter \#1 \$classes of static method Phpactor\\CodeBuilder\\Domain\\Prototype\\Classes\:\:fromClasses\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Builder/SourceCodeBuilder.php - - - - message: '#^Parameter \#1 \$enums of static method Phpactor\\CodeBuilder\\Domain\\Prototype\\Enums\:\:fromEnums\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Builder/SourceCodeBuilder.php - - - - message: '#^Parameter \#1 \$traits of static method Phpactor\\CodeBuilder\\Domain\\Prototype\\Traits\:\:fromTraits\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Builder/SourceCodeBuilder.php - - - - message: '#^Parameter \#1 \$useStatements of static method Phpactor\\CodeBuilder\\Domain\\Prototype\\UseStatements\:\:fromUseStatements\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Builder/SourceCodeBuilder.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Builder\\TraitBuilder\:\:constant\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeBuilder/Domain/Builder/TraitBuilder.php - - - - message: '#^Parameter \#1 \$constants of static method Phpactor\\CodeBuilder\\Domain\\Prototype\\Constants\:\:fromConstants\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Builder/TraitBuilder.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Cases.php - - - - message: '#^Parameter \#1 \$items of class Phpactor\\CodeBuilder\\Domain\\Prototype\\Cases constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Cases.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Classes.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Classes.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Prototype\\Collection\:\:in\(\) has parameter \$names with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Collection.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Prototype\\Collection\:\:notIn\(\) has parameter \$names with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Collection.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 2 - path: lib/CodeBuilder/Domain/Prototype/Collection.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Constants.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Constants.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Enums.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Enums.php - - - - message: '#^Cannot access an offset on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Prototype\\ImplementsInterfaces\:\:fromTypes\(\) has parameter \$types with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php - - - - message: '#^Parameter \#2 \$array of function implode expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/ImplementsInterfaces.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Prototype\\Interfaces\:\:fromInterfaces\(\) has parameter \$interfaces with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Interfaces.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Interfaces.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Methods.php - - - - message: '#^Parameter \#1 \$items of class Phpactor\\CodeBuilder\\Domain\\Prototype\\Methods constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Methods.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Properties.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Properties.php - - - - message: '#^Cannot call method applyUpdate\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Prototype.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Prototype\\Prototype\:\:applyUpdate\(\) should return bool but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Prototype.php - - - - message: '#^Property Phpactor\\CodeBuilder\\Domain\\Prototype\\Prototype\:\:\$updatePolicy has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Prototype.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/QualifiedName.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Traits.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Traits.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Prototype\\Value\:\:__construct\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Value.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Prototype\\Value\:\:fromValue\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Value.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Domain\\Prototype\\Value\:\:value\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Value.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 1 - path: lib/CodeBuilder/Domain/Prototype/Value.php - - - - message: '#^Class Phpactor\\CodeBuilder\\Domain\\TemplatePathResolver\\FilterPhpVersionDirectoryIterator extends generic class FilterIterator but does not specify its types\: TKey, TValue, TIterator$#' - identifier: missingType.generics - count: 1 - path: lib/CodeBuilder/Domain/TemplatePathResolver/FilterPhpVersionDirectoryIterator.php - - - - message: '#^Call to an undefined method Phpactor\\CodeBuilder\\Domain\\Builder\\ClassLikeBuilder\:\:property\(\)\.$#' - identifier: method.notFound - count: 2 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method build\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method constant\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method defaultValue\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method docblock\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method end\(\) on mixed\.$#' - identifier: method.nonObject - count: 18 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method interface\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method method\(\) on mixed\.$#' - identifier: method.nonObject - count: 5 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method parameter\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method property\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method trait\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method type\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Cannot call method visibility\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Parameter \#1 \$prototype of method Phpactor\\CodeBuilder\\Domain\\Renderer\:\:render\(\) expects Phpactor\\CodeBuilder\\Domain\\Prototype\\Prototype, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeBuilder/Tests/Adapter/GeneratorTestCase.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Adapter\\Twig\\TwigGeneratorTest\:\:renderer\(\) should return Phpactor\\CodeBuilder\\Domain\\Renderer but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Tests/Adapter/Twig/TwigGeneratorTest.php - - - - message: '#^Call to an undefined method Phpactor\\CodeBuilder\\Domain\\Builder\\ClassLikeBuilder\:\:constant\(\)\.$#' - identifier: method.notFound - count: 1 - path: lib/CodeBuilder/Tests/Adapter/UpdaterTestCase.php - - - - message: '#^Call to an undefined method Phpactor\\CodeBuilder\\Domain\\Builder\\ClassLikeBuilder\:\:property\(\)\.$#' - identifier: method.notFound - count: 2 - path: lib/CodeBuilder/Tests/Adapter/UpdaterTestCase.php - - - - message: '#^Cannot call method build\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/CodeBuilder/Tests/Adapter/UpdaterTestCase.php - - - - message: '#^Cannot call method end\(\) on mixed\.$#' - identifier: method.nonObject - count: 6 - path: lib/CodeBuilder/Tests/Adapter/UpdaterTestCase.php - - - - message: '#^Generator expects value type array\{string, Phpactor\\CodeBuilder\\Domain\\Prototype\\SourceCode, string\}, array\{"class Aardvark\\n\{\\n\}", mixed, "class Aardvark\\n\{\\n …"\} given\.$#' - identifier: generator.valueType - count: 2 - path: lib/CodeBuilder/Tests/Adapter/UpdaterTestCase.php - - - - message: '#^Generator expects value type array\{string, Phpactor\\CodeBuilder\\Domain\\Prototype\\SourceCode, string\}, array\{"trait Aardvark\\n\{\\n\}", mixed, "trait Aardvark\\n\{\\n …"\} given\.$#' - identifier: generator.valueType - count: 1 - path: lib/CodeBuilder/Tests/Adapter/UpdaterTestCase.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\CodeBuilder\\\\Domain\\\\Prototype\\\\SourceCode'' and Phpactor\\CodeBuilder\\Domain\\Prototype\\SourceCode will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/CodeBuilder/Tests/Adapter/WorseReflection/WorseBuilderFactoryTest.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 2 - path: lib/CodeBuilder/Tests/Adapter/WorseReflection/WorseBuilderFactoryTest.php - - - - message: '#^Unreachable statement \- code above always terminates\.$#' - identifier: deadCode.unreachable - count: 1 - path: lib/CodeBuilder/Tests/Adapter/WorseReflection/WorseBuilderFactoryTest.php - - - - message: '#^Parameter \#2 \$type of static method Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\Util\\NodeHelper\:\:resolvedShortName\(\) expects Microsoft\\PhpParser\\Node\\QualifiedName\|Microsoft\\PhpParser\\Token\|null, Microsoft\\PhpParser\\Node given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Tests/Functional/Adapter/TolerantParser/Util/NodeHelperTest.php - - - - message: '#^Argument of an invalid type list\\|false supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/CodeBuilder/Tests/IntegrationTestCase.php - - - - message: '#^Cannot call method visibility\(\) on Phpactor\\CodeBuilder\\Domain\\Prototype\\Parameter\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/MethodBuilderTest.php - - - - message: '#^Call to an undefined method Phpactor\\CodeBuilder\\Domain\\Builder\\ClassLikeBuilder\:\:build\(\)\.$#' - identifier: method.notFound - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Call to an undefined method Phpactor\\CodeBuilder\\Domain\\Builder\\ClassLikeBuilder\:\:property\(\)\.$#' - identifier: method.notFound - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\CodeBuilder\\\\Domain\\\\Prototype\\\\SourceCode'' and Phpactor\\CodeBuilder\\Domain\\Prototype\\SourceCode will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method __toString\(\) on Phpactor\\CodeBuilder\\Domain\\Prototype\\UseStatement\|null\.$#' - identifier: method.nonObject - count: 3 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method __toString\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method body\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method build\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method end\(\) on mixed\.$#' - identifier: method.nonObject - count: 6 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method extendsClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method first\(\) on mixed\.$#' - identifier: method.nonObject - count: 6 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method implementsInterfaces\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method isAbstract\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method isStatic\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method lines\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method method\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method methods\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method name\(\) on Phpactor\\CodeBuilder\\Domain\\Prototype\\ClassPrototype\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method name\(\) on Phpactor\\CodeBuilder\\Domain\\Prototype\\TraitPrototype\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method properties\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method type\(\) on Phpactor\\CodeBuilder\\Domain\\Prototype\\UseStatement\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Parameter \#2 \$haystack of method PHPUnit\\Framework\\Assert\:\:assertCount\(\) expects Countable\|iterable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Builder/SourceCodeBuilderTest.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Prototype/CollectionTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\Prototype\\TestCollection\:\:fromArray\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Prototype/CollectionTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\Prototype\\TestCollection\:\:fromArray\(\) has parameter \$items with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Prototype/CollectionTest.php - - - - message: '#^Parameter \#1 \$items of class Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\Prototype\\TestCollection constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Prototype/CollectionTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\Prototype\\DefaultValueTest\:\:testExportValues\(\) has parameter \$expected with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Prototype/DefaultValueTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\Prototype\\DefaultValueTest\:\:testExportValues\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/Prototype/DefaultValueTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\TemplatePathResolver\\FilterPhpVersionDirectoryIteratorTest\:\:provideDirectoriesToFilter\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/FilterPhpVersionDirectoryIteratorTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\TemplatePathResolver\\FilterPhpVersionDirectoryIteratorTest\:\:testThatItKeepsOnlyDirectoriesOfInferiorOrEqualVersion\(\) has parameter \$expectedFilteredDirectories with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/FilterPhpVersionDirectoryIteratorTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\TemplatePathResolver\\PhpVersionPathResolverTest\:\:testResolvePaths\(\) has parameter \$expectedPaths with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\TemplatePathResolver\\PhpVersionPathResolverTest\:\:testResolvePaths\(\) has parameter \$fullTemplatePaths with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Tests\\Unit\\Domain\\TemplatePathResolver\\PhpVersionPathResolverTest\:\:testResolvePaths\(\) has parameter \$templatePaths with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(string\)\: string given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\TestUtils\\Workspace\:\:mkdir\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php - - - - message: '#^Parameter \#1 \$paths of method Phpactor\\CodeBuilder\\Domain\\TemplatePathResolver\\PhpVersionPathResolver\:\:resolve\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Tests/Unit/Domain/TemplatePathResolver/PhpVersionPathResolverTest.php - - - - message: '#^Cannot call method apply\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php - - - - message: '#^Cannot call method render\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php - - - - message: '#^Cannot call method reveal\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php - - - - message: '#^Cannot call method willReturn\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php - - - - message: '#^Property Phpactor\\CodeBuilder\\Tests\\Unit\\SourceBuilderTest\:\:\$builder has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php - - - - message: '#^Property Phpactor\\CodeBuilder\\Tests\\Unit\\SourceBuilderTest\:\:\$generator has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php - - - - message: '#^Property Phpactor\\CodeBuilder\\Tests\\Unit\\SourceBuilderTest\:\:\$prototype has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/CodeBuilder/Tests/Unit/SourceBuilderTest.php - - - - message: '#^Binary operation "\." between string and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/CodeBuilder/Util/TextFormat.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Util\\TextFormat\:\:indentRemove\(\) should return string but returns string\|null\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Util/TextFormat.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Util\\TextFormat\:\:indentReplace\(\) has parameter \$text with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeBuilder/Util/TextFormat.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\CodeBuilder\\Util\\TextFormat\:\:indentRemove\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeBuilder/Util/TextFormat.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Util\\TextUtil\:\:lines\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeBuilder/Util/TextUtil.php - - - - message: '#^Method Phpactor\\CodeBuilder\\Util\\TextUtil\:\:lines\(\) should return array but returns list\\|false\.$#' - identifier: return.type - count: 1 - path: lib/CodeBuilder/Util/TextUtil.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Docblock and Phpactor\\DocblockParser\\Ast\\Docblock will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/DocblockParser/ParserDocblockUpdater.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantExtractExpression.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\Statement\\ExpressionStatement and Microsoft\\PhpParser\\Node\\Statement\\ExpressionStatement will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantExtractExpression.php - - - - message: '#^Cannot call method getFullyQualifiedNameText\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantImportName.php - - - - message: '#^Cannot use array destructuring on array\|null\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantImportName.php - - - - message: '#^Method Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantImportName\:\:findExistingImport\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantImportName.php - - - - message: '#^Parameter \#2 \$existingName of class Phpactor\\CodeTransform\\Domain\\Refactor\\ImportClass\\NameAlreadyImportedException constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantImportName.php - - - - message: '#^Parameter \#3 \$existingFQN of class Phpactor\\CodeTransform\\Domain\\Refactor\\ImportClass\\NameAlreadyImportedException constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantImportName.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\Parameter and Microsoft\\PhpParser\\Node\\Parameter will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantRenameVariable.php - - - - message: '#^Method Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantRenameVariable\:\:textEditsToRename\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantRenameVariable.php - - - - message: '#^Parameter \#1 \$textEdits of static method Phpactor\\TextDocument\\TextEdits\:\:fromTextEdits\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/TolerantParser/Refactor/TolerantRenameVariable.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Helper/WorseMissingMemberFinder.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Helper/WorseMissingMemberFinder.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node and Microsoft\\PhpParser\\Node will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractMethod.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractMethod.php - - - - message: '#^Variable \$stmt on left side of \?\? always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseExtractMethod.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseFillObject.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionParameter and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionParameter will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseFillObject.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateConstructor.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionArgument and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionArgument will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Refactor/WorseGenerateConstructor.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/AddMissingProperties.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/AddMissingProperties.php - - - - message: '#^Constant Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\AddMissingProperties\:\:LENGTH_OF_THIS_PREFIX is unused\.$#' - identifier: classConstant.unused - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/AddMissingProperties.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 3 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/CompleteConstructor.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/CompleteConstructor.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionParameter and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionParameter will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/CompleteConstructor.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/ImplementContracts.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/ImplementContracts.php - - - - message: '#^Method Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\ImplementContracts\:\:missingClassMethods\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/ImplementContracts.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod\)\: string given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/ImplementContracts.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Cannot call method message\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Cannot call method range\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Cannot call method start\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Cannot call method toInt\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Parameter \#1 \$pos of method Microsoft\\PhpParser\\Node\:\:getDescendantNodeAtPosition\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Parameter \#1 \$range of class Phpactor\\CodeTransform\\Domain\\Diagnostic constructor expects Phpactor\\TextDocument\\ByteOffsetRange, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Parameter \#2 \$message of class Phpactor\\CodeTransform\\Domain\\Diagnostic constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/RemoveUnusedImportsTransformer.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php - - - - message: '#^Cannot call method message\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php - - - - message: '#^Cannot call method range\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php - - - - message: '#^Parameter \#1 \$range of class Phpactor\\CodeTransform\\Domain\\Diagnostic constructor expects Phpactor\\TextDocument\\ByteOffsetRange, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php - - - - message: '#^Parameter \#2 \$message of class Phpactor\\CodeTransform\\Domain\\Diagnostic constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformer.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 4 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method __toString\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method allTypes\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method classLike\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method classType\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method methodName\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method paramName\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method paramType\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method range\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Cannot call method toLocalType\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Parameter \#1 \$className of method Phpactor\\WorseReflection\\Core\\Reflector\\ClassReflector\:\:reflectClassLike\(\) expects Phpactor\\WorseReflection\\Core\\Name\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Parameter \#1 \$name of class Phpactor\\CodeTransform\\Domain\\DocBlockUpdater\\ParamTagPrototype constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Parameter \#1 \$range of class Phpactor\\CodeTransform\\Domain\\Diagnostic constructor expects Phpactor\\TextDocument\\ByteOffsetRange, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Parameter \#1 \$use of method Phpactor\\CodeBuilder\\Domain\\Builder\\SourceCodeBuilder\:\:use\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Parameter \#2 \$type of class Phpactor\\CodeTransform\\Domain\\DocBlockUpdater\\ParamTagPrototype constructor expects Phpactor\\WorseReflection\\Core\\Type, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformer.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 3 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Cannot call method actualReturnType\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Cannot call method byClasses\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Cannot call method classType\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Cannot call method methodName\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Cannot call method range\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Parameter \#1 \$className of method Phpactor\\WorseReflection\\Core\\Reflector\\ClassReflector\:\:reflectClassLike\(\) expects Phpactor\\WorseReflection\\Core\\Name\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Parameter \#1 \$range of class Phpactor\\CodeTransform\\Domain\\Diagnostic constructor expects Phpactor\\TextDocument\\ByteOffsetRange, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformer.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 3 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Cannot call method class\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Cannot call method scope\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Cannot call method short\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Parameter \#1 \$method of method Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\UpdateReturnTypeTransformer\:\:returnType\(\) expects Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\CodeBuilder\\Domain\\Builder\\ClassLikeBuilder\:\:method\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\CodeBuilder\\Domain\\Builder\\SourceCodeBuilder\:\:class\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Parameter \#1 \$scope of method Phpactor\\WorseReflection\\Core\\Type\:\:toLocalType\(\) expects Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionScope, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformer.php - - - - message: '#^Method Phpactor\\CodeTransform\\CodeTransform\:\:transform\(\) has parameter \$transformations with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeTransform/CodeTransform.php - - - - message: '#^Method Phpactor\\CodeTransform\\Domain\\AbstractCollection\:\:names\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeTransform/Domain/AbstractCollection.php - - - - message: '#^Parameter \#1 \$string of function ltrim expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Domain/DocBlockUpdater/ParamTagPrototype.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/CodeTransform/Domain/Transformers.php - - - - message: '#^Cannot call method apply\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/CodeTransform/Domain/Transformers.php - - - - message: '#^Instanceof between Phpactor\\CodeTransform\\Domain\\Transformer and Phpactor\\CodeTransform\\Domain\\Transformer will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/CodeTransform/Domain/Transformers.php - - - - message: '#^Method Phpactor\\CodeTransform\\Domain\\Transformers\:\:in\(\) has parameter \$transformerNames with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeTransform/Domain/Transformers.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Domain/Transformers.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\CodeTransform\\Domain\\AbstractCollection\\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Domain/Transformers.php - - - - message: '#^Variable \$lines in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: lib/CodeTransform/Domain/Utils/TextUtils.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\AdapterTestCase\:\:renderer\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\AdapterTestCase\:\:sourceExpected\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\AdapterTestCase\:\:sourceExpected\(\) has parameter \$manifestPath with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\AdapterTestCase\:\:sourceExpectedAndOffset\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\AdapterTestCase\:\:sourceExpectedAndOffset\(\) has parameter \$manifestPath with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\AdapterTestCase\:\:updater\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Parameter \#1 \$filename of function file_exists expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Parameter \#1 \$filename of function file_get_contents expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Parameter \#1 \$filename of function touch expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Parameter \#1 \$manifest of method Phpactor\\TestUtils\\Workspace\:\:loadManifest\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Parameter \#1 \$renderer of class Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\TolerantUpdater constructor expects Phpactor\\CodeBuilder\\Domain\\Renderer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Parameter \#1 \$source of static method Phpactor\\TestUtils\\ExtractOffset\:\:fromSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/AdapterTestCase.php - - - - message: '#^Parameter \#1 \$renderer of class Phpactor\\CodeTransform\\Adapter\\Native\\GenerateNew\\ClassGenerator constructor expects Phpactor\\CodeBuilder\\Domain\\Renderer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/Native/GenerateNew/ClassGeneratorTest.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\TolerantParser\\ClassToFile\\Transformer\\ClassNameFixerTransformerTest\:\:initComposer\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/ClassToFile/Transformer/ClassNameFixerTransformerTest.php - - - - message: '#^Parameter \#1 \$classLoader of class Phpactor\\ClassFileConverter\\Adapter\\Composer\\ComposerFileToClass constructor expects Composer\\Autoload\\ClassLoader, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/ClassToFile/Transformer/ClassNameFixerTransformerTest.php - - - - message: '#^Parameter \#1 \$directory of function chdir expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/ClassToFile/Transformer/ClassNameFixerTransformerTest.php - - - - message: '#^Parameter \#1 \$manifest of method Phpactor\\TestUtils\\Workspace\:\:loadManifest\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/ClassToFile/Transformer/ClassNameFixerTransformerTest.php - - - - message: '#^Property Phpactor\\CodeTransform\\Tests\\Adapter\\TolerantParser\\ClassToFile\\Transformer\\ClassNameFixerTransformerTest\:\:\$composerAutoload has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/ClassToFile/Transformer/ClassNameFixerTransformerTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/AbstractTolerantImportNameCase.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\TolerantParser\\Refactor\\AbstractTolerantImportNameCase\:\:importNameFromTestFile\(\) should return array\{string, string\} but returns array\{mixed, string\}\.$#' - identifier: return.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/AbstractTolerantImportNameCase.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\TolerantParser\\Refactor\\AbstractTolerantImportNameCase\:\:importName\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/AbstractTolerantImportNameCase.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/AbstractTolerantImportNameCase.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Tests\\Adapter\\TolerantParser\\Refactor\\AbstractTolerantImportNameCase\:\:importName\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/AbstractTolerantImportNameCase.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantChangeVisiblityTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantChangeVisiblityTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantChangeVisiblityTest.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantChangeVisiblity\:\:changeVisiblity\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantChangeVisiblityTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php - - - - message: '#^Parameter \#2 \$offsetStart of method Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantExtractExpression\:\:extractExpression\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php - - - - message: '#^Parameter \#3 \$offsetEnd of method Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantExtractExpression\:\:extractExpression\(\) expects int\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantExtractExpressionTest.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\TolerantParser\\Refactor\\TolerantImportNameOnlyTest\:\:importName\(\) has parameter \$source with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantImportNameOnlyTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantImportNameOnlyTest.php - - - - message: '#^Parameter \#1 \$updater of class Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantImportName constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantImportNameOnlyTest.php - - - - message: '#^Parameter \#1 \$updater of class Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantImportName constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantImportNameTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\TolerantParser\\Refactor\\TolerantRenameVariableTest\:\:testRenameVariable\(\) has parameter \$name with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantRenameVariable\:\:renameVariable\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php - - - - message: '#^Parameter \#3 \$newName of method Phpactor\\CodeTransform\\Adapter\\TolerantParser\\Refactor\\TolerantRenameVariable\:\:renameVariable\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/TolerantParser/Refactor/TolerantRenameVariableTest.php - - - - message: '#^Parameter \#2 \$renderer of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\GenerateFromExisting\\InterfaceFromExistingGenerator constructor expects Phpactor\\CodeBuilder\\Domain\\Renderer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/GenerateFromExisting/InterfaceFromExistingGeneratorTest.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Helper\\WorseInterestingOffsetFinderTest\:\:provideFindSomethingInterestingWhen\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Helper/WorseInterestingOffsetFinderTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\WorseTestCase\:\:reflectorForWorkspace\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseReplaceQualifierWithImport\:\:getTextEdits\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php - - - - message: '#^Parameter \#3 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseReplaceQualifierWithImport constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/ReplaceQualifierWithImportTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\WorseTestCase\:\:reflectorForWorkspace\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseExtractConstant\:\:extractConstant\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseExtractConstant constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php - - - - message: '#^Parameter \#2 \$offsetStart of method Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseExtractMethod\:\:extractMethod\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php - - - - message: '#^Parameter \#2 \$string of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:fromPathAndString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php - - - - message: '#^Parameter \#3 \$offsetEnd of method Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseExtractMethod\:\:extractMethod\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php - - - - message: '#^Parameter \#3 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseExtractMethod constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractMethodTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Refactor\\WorseFillMatchArmsTest\:\:createRefactor\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillMatchArmsTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Refactor\\WorseFillObjectTest\:\:createFillObject\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php - - - - message: '#^Parameter \#3 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseFillObject constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseFillObjectTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\WorseTestCase\:\:reflectorForWorkspace\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseGenerateAccessor constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php - - - - message: '#^Parameter \#3 \$offset of method Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseGenerateAccessor\:\:generate\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateAccessorTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Refactor\\WorseGenerateConstructorTest\:\:generator\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php - - - - message: '#^Parameter \#3 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseGenerateConstructor constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateConstructorTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateDecoratorTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateDecoratorTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\WorseTestCase\:\:reflectorForWorkspace\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateDecoratorTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateDecoratorTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseGenerateDecorator constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateDecoratorTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMemberTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Refactor\\WorseGenerateMemberTest\:\:generateMember\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMemberTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMemberTest.php - - - - message: '#^Parameter \#2 \$start of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Refactor\\WorseGenerateMemberTest\:\:generateMember\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMemberTest.php - - - - message: '#^Parameter \#3 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseGenerateMember constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMemberTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\WorseTestCase\:\:reflectorForWorkspace\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseGenerateMutator constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php - - - - message: '#^Parameter \#3 \$offset of method Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseGenerateMutator\:\:generate\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseGenerateMutatorTest.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Refactor\\WorseOverrideMethodTest\:\:testOverrideMethod\(\) has parameter \$methodName with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Refactor\\WorseOverrideMethodTest\:\:overrideMethod\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php - - - - message: '#^Parameter \#3 \$methodName of method Phpactor\\CodeTransform\\Tests\\Adapter\\WorseReflection\\Refactor\\WorseOverrideMethodTest\:\:overrideMethod\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php - - - - message: '#^Parameter \#3 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Refactor\\WorseOverrideMethod constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseOverrideMethodTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\AddMissingProperties constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/AddMissingPropertiesTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\CompleteConstructor constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 4 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/CompleteConstructorTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\ImplementContracts constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/ImplementContractsTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\UpdateDocblockGenericTransformer constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockGenericTransformerTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\UpdateDocblockParamsTransformer constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockParamsTransformerTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\UpdateDocblockReturnTransformer constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateDocblockReturnTransformerTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\Transformer\\UpdateReturnTypeTransformer constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/CodeTransform/Tests/Adapter/WorseReflection/Transformer/UpdateReturnTypeTransformerTest.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Unit\\CodeTransformTest\:\:create\(\) has parameter \$transformers with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/CodeTransform/Tests/Unit/CodeTransformTest.php - - - - message: '#^Method Phpactor\\CodeTransform\\Tests\\Unit\\Domain\\Utils\\TextUtilsTest\:\:provideRemoveIndentation\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/CodeTransform/Tests/Unit/Domain/Utils/TextUtilsTest.php - - - - message: '#^Method Phpactor\\Completion\\Bridge\\TolerantParser\\ChainTolerantCompletor\:\:filterNonQualifyingClasses\(\) is unused\.$#' - identifier: method.unused - count: 1 - path: lib/Completion/Bridge/TolerantParser/ChainTolerantCompletor.php - - - - message: '#^Method Phpactor\\Completion\\Bridge\\TolerantParser\\ChainTolerantCompletor\:\:filterNonQualifyingClasses\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Bridge/TolerantParser/ChainTolerantCompletor.php - - - - message: '#^Cannot call method getEndPosition\(\) on Microsoft\\PhpParser\\Node\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Completion/Bridge/TolerantParser/CompletionContext.php - - - - message: '#^Cannot access property \$parent on Microsoft\\PhpParser\\Node\|null\.$#' - identifier: property.nonObject - count: 2 - path: lib/Completion/Bridge/TolerantParser/DebugTolerantCompletor.php - - - - message: '#^Match expression does not handle remaining values\: \(class\-string\&literal\-string\)\|\(class\-string\&literal\-string\)\|\(class\-string\&literal\-string\)\|\(class\-string\&literal\-string\)\|\(class\-string\&literal\-string\)$#' - identifier: match.unhandled - count: 1 - path: lib/Completion/Bridge/TolerantParser/ReferenceFinder/AttributeCompletor.php - - - - message: '#^Parameter \#2 \$imports of method Phpactor\\Completion\\Bridge\\TolerantParser\\SourceCodeFilesystem\\ScfClassCompletor\:\:getClassNameForImport\(\) expects array\\>, array\\> given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletor.php - - - - message: '#^Binary operation "\." between ''\$'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/DocblockCompletor.php - - - - message: '#^Method Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\DoctrineAnnotationCompletor\:\:complete\(\) should return bool but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/DoctrineAnnotationCompletor.php - - - - message: '#^Method Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\Helper\\VariableCompletionHelper\:\:orderedVariablesUntilOffset\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/Helper/VariableCompletionHelper.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Type&Phpactor\\WorseReflection\\Core\\Type\\ClassLikeType and Phpactor\\WorseReflection\\Core\\Type\\ClassLikeType will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/WorseClassMemberCompletor.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php - - - - message: '#^Method Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseFunctionCompletor\:\:definedNamesFor\(\) has parameter \$reflectedFunctions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php - - - - message: '#^Method Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseFunctionCompletor\:\:filterFunctions\(\) has parameter \$functions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php - - - - message: '#^Method Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseFunctionCompletor\:\:reflectedFunctions\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\WorseReflection\\Core\\Reflector\\FunctionReflector\:\:reflectFunction\(\) expects Phpactor\\WorseReflection\\Core\\Name\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php - - - - message: '#^Parameter \#1 \$string of static method Phpactor\\WorseReflection\\Core\\Name\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletor.php - - - - message: '#^Parameter \#1 \$iterator of function iterator_to_array expects iterable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Bridge/WorseReflection/Completor/ContextSensitiveCompletor.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Completion/Bridge/WorseReflection/Formatter/InterfaceFormatter.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionInterface and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionInterface will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Completion/Bridge/WorseReflection/Formatter/InterfaceFormatter.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Completion/Bridge/WorseReflection/SnippetFormatter/NameSearchResultClassSnippetFormatter.php - - - - message: '#^Instanceof between Phpactor\\ReferenceFinder\\Search\\NameSearchResult and Phpactor\\ReferenceFinder\\Search\\NameSearchResult will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Completion/Bridge/WorseReflection/SnippetFormatter/NameSearchResultClassSnippetFormatter.php - - - - message: '#^Method Phpactor\\Completion\\Core\\ChainSignatureHelper\:\:__construct\(\) has parameter \$helpers with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Core/ChainSignatureHelper.php - - - - message: '#^Parameter \#1 \$helper of method Phpactor\\Completion\\Core\\ChainSignatureHelper\:\:add\(\) expects Phpactor\\Completion\\Core\\SignatureHelper, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Core/ChainSignatureHelper.php - - - - message: '#^Parameter \#5 \$options of method Phpactor\\Completion\\Core\\Completor\\NameSearcherCompletor\:\:createSuggestion\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Core/Completor/NameSearcherCompletor.php - - - - message: '#^Method Phpactor\\Completion\\Core\\Range\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Core/Range.php - - - - message: '#^Method Phpactor\\Completion\\Core\\SignatureHelp\:\:activeSignature\(\) should return int but returns int\|null\.$#' - identifier: return.type - count: 1 - path: lib/Completion/Core/SignatureHelp.php - - - - message: '#^Method Phpactor\\Completion\\Core\\SignatureInformation\:\:__construct\(\) has parameter \$parameters with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Core/SignatureInformation.php - - - - message: '#^Method Phpactor\\Completion\\Core\\SignatureInformation\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Core/SignatureInformation.php - - - - message: '#^Parameter \#1 \$parameter of method Phpactor\\Completion\\Core\\SignatureInformation\:\:add\(\) expects Phpactor\\Completion\\Core\\ParameterInformation, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Core/SignatureInformation.php - - - - message: '#^Method Phpactor\\Completion\\Core\\Suggestion\:\:documentation\(\) should return string\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Completion/Core/Suggestion.php - - - - message: '#^Method Phpactor\\Completion\\Core\\Suggestion\:\:shortDescription\(\) should return string\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Completion/Core/Suggestion.php - - - - message: '#^Binary operation "\." between literal\-string&non\-falsy\-string and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Cannot access offset ''source'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Benchmark\\CompletorBenchCase\:\:benchComplete\(\) has parameter \$params with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Benchmark\\CompletorBenchCase\:\:provideComplete\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Benchmark\\CompletorBenchCase\:\:setUp\(\) has parameter \$params with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Parameter \#1 \$source of static method Phpactor\\TestUtils\\ExtractOffset\:\:fromSource\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Property Phpactor\\Completion\\Tests\\Benchmark\\CompletorBenchCase\:\:\$offset has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Property Phpactor\\Completion\\Tests\\Benchmark\\CompletorBenchCase\:\:\$source has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/Completion/Tests/Benchmark/CompletorBenchCase.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\DoctrineAnnotationCompletorTest\:\:testComplete\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/DoctrineAnnotationCompletorTest.php - - - - message: '#^@dataProvider provideCouldComplete related method not found\.$#' - identifier: phpunit.dataProviderMethod - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/Qualifier/TolerantQualifierTestCase.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\SourceCodeFilesystem\\ScfClassCompletorTest\:\:testComplete\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\SourceCodeFilesystem\\ScfClassCompletorTest\:\:testImportClass\(\) has parameter \$expected with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\SourceCodeFilesystem\\ScfClassCompletorTest\:\:testImportClass\(\) has parameter \$source with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletorTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\Completion\\Tests\\Integration\\CompletorTestCase\:\:assertComplete\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletorTest.php - - - - message: '#^Parameter \#2 \$expected of method Phpactor\\Completion\\Tests\\Integration\\CompletorTestCase\:\:assertComplete\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/SourceCodeFilesystem/ScfClassCompletorTest.php - - - - message: '#^Class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseConstantCompletor does not have a constructor and must be instantiated without any parameters\.$#' - identifier: new.noConstructor - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstantCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseConstantCompletorTest\:\:testComplete\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstantCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseConstructorCompletorTest\:\:provideCompleteStaticClassParameter\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstructorCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseConstructorCompletorTest\:\:testCompleteMethodParameter\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseConstructorCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseDeclaredClassCompletorTest\:\:testComplete\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseDeclaredClassCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseFunctionCompletorTest\:\:testComplete\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseFunctionCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseLocalVariableCompletorTest\:\:testComplete\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseLocalVariableCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseNamedParameterCompletorTest\:\:testComplete\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseNamedParameterCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseParameterCompletorTest\:\:testCompleteFunctionParameter\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseParameterCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseParameterCompletorTest\:\:testCompleteMethodParameter\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseParameterCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\Bridge\\TolerantParser\\WorseReflection\\WorseParameterCompletorTest\:\:testCompleteStaticClassParameter\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/Bridge/TolerantParser/WorseReflection/WorseParameterCompletorTest.php - - - - message: '#^Binary operation "\." between ''got unexpected…'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Completion/Tests/Integration/CompletorTestCase.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Integration\\CompletorTestCase\:\:assertComplete\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Integration/CompletorTestCase.php - - - - message: '#^Parameter \#1 \$subset of method Phpactor\\Completion\\Tests\\Integration\\CompletorTestCase\:\:assertArraySubset\(\) expects array\\|ArrayAccess, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Integration/CompletorTestCase.php - - - - message: '#^Parameter \#2 \$array of function array_key_exists expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Integration/CompletorTestCase.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertNotEmpty\(\) with Closure will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 3 - path: lib/Completion/Tests/Unit/Adapter/WorseReflection/SuggestionDocumentor/WorseSuggestionDocumentorTest.php - - - - message: '#^Cannot call method getText\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/ChainTolerantCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Unit\\Bridge\\TolerantParser\\ChainTolerantCompletorTest\:\:create\(\) has parameter \$completors with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/ChainTolerantCompletorTest.php - - - - message: '#^Parameter \#1 \$tolerantCompletors of class Phpactor\\Completion\\Bridge\\TolerantParser\\ChainTolerantCompletor constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/ChainTolerantCompletorTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Microsoft\\\\PhpParser\\\\Node\\\\Expression\\\\ObjectCreationExpression'' and Microsoft\\PhpParser\\Node\\Expression\\ObjectCreationExpression will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/Helper/NodeQueryTest.php - - - - message: '#^Parameter \#1 \$completor of class Phpactor\\Completion\\Bridge\\TolerantParser\\LimitingCompletor constructor expects Phpactor\\Completion\\Bridge\\TolerantParser\\TolerantCompletor, object given\.$#' - identifier: argument.type - count: 1 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/LimitingCompletorTest.php - - - - message: '#^Parameter \#1 \$node of method Phpactor\\Completion\\Bridge\\TolerantParser\\LimitingCompletor\:\:complete\(\) expects Microsoft\\PhpParser\\Node, object given\.$#' - identifier: argument.type - count: 4 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/LimitingCompletorTest.php - - - - message: '#^Property Phpactor\\Completion\\Tests\\Unit\\Bridge\\TolerantParser\\LimitingCompletorTest\:\:\$innerCompletor with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/LimitingCompletorTest.php - - - - message: '#^Property Phpactor\\Completion\\Tests\\Unit\\Bridge\\TolerantParser\\LimitingCompletorTest\:\:\$node with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/LimitingCompletorTest.php - - - - message: '#^Unreachable statement \- code above always terminates\.$#' - identifier: deadCode.unreachable - count: 1 - path: lib/Completion/Tests/Unit/Bridge/TolerantParser/LimitingCompletorTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Unit\\Bridge\\WorseReflection\\Formatter\\FunctionLikeSnippetFormatterTest\:\:provideReflectionToFormat\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Unit/Bridge/WorseReflection/Formatter/FunctionLikeSnippetFormatterTest.php - - - - message: '#^Cannot call method signatureHelp\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/Completion/Tests/Unit/Core/ChainSignatureHelperTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Unit\\Core\\ChainSignatureHelperTest\:\:create\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Completion/Tests/Unit/Core/ChainSignatureHelperTest.php - - - - message: '#^Method Phpactor\\Completion\\Tests\\Unit\\Core\\ChainSignatureHelperTest\:\:create\(\) has parameter \$helpers with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Completion/Tests/Unit/Core/ChainSignatureHelperTest.php - - - - message: '#^Method Phpactor\\ComposerInspector\\ComposerInspector\:\:parseFile\(\) should return array\ but returns array\\.$#' - identifier: return.type - count: 1 - path: lib/ComposerInspector/ComposerInspector.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Adapter\\Deserializer\\JsonDeserializer\:\:deserialize\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ConfigLoader/Adapter/Deserializer/JsonDeserializer.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Adapter\\Deserializer\\JsonDeserializer\:\:deserialize\(\) should return array but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/ConfigLoader/Adapter/Deserializer/JsonDeserializer.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Adapter\\Deserializer\\YamlDeserializer\:\:deserialize\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ConfigLoader/Adapter/Deserializer/YamlDeserializer.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Adapter\\Deserializer\\YamlDeserializer\:\:deserialize\(\) should return array but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/ConfigLoader/Adapter/Deserializer/YamlDeserializer.php - - - - message: '#^Cannot call method loader\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/ConfigLoader/Core/ConfigLoader.php - - - - message: '#^Cannot call method path\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/ConfigLoader/Core/ConfigLoader.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Core\\ConfigLoader\:\:load\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ConfigLoader/Core/ConfigLoader.php - - - - message: '#^Parameter \#1 \$extension of method Phpactor\\ConfigLoader\\Core\\Deserializers\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ConfigLoader/Core/ConfigLoader.php - - - - message: '#^Parameter \#1 \$filename of function file_exists expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ConfigLoader/Core/ConfigLoader.php - - - - message: '#^Parameter \#1 \$filename of function file_get_contents expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ConfigLoader/Core/ConfigLoader.php - - - - message: '#^Strict comparison using \=\=\= between null and array will always evaluate to false\.$#' - identifier: identical.alwaysFalse - count: 1 - path: lib/ConfigLoader/Core/ConfigLoader.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Core\\Deserializer\:\:deserialize\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ConfigLoader/Core/Deserializer.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Core\\Deserializers\:\:__construct\(\) has parameter \$deserializerMap with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ConfigLoader/Core/Deserializers.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Core\\Deserializers\:\:add\(\) has parameter \$deserializerExtension with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/ConfigLoader/Core/Deserializers.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Core\\Deserializers\:\:get\(\) should return Phpactor\\ConfigLoader\\Core\\Deserializer but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/ConfigLoader/Core/Deserializers.php - - - - message: '#^Parameter \#2 \$deserializer of method Phpactor\\ConfigLoader\\Core\\Deserializers\:\:add\(\) expects Phpactor\\ConfigLoader\\Core\\Deserializer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ConfigLoader/Core/Deserializers.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/ConfigLoader/Core/Deserializers.php - - - - message: '#^Property Phpactor\\ConfigLoader\\Core\\Deserializers\:\:\$deserializerMap type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ConfigLoader/Core/Deserializers.php - - - - message: '#^Class Phpactor\\ConfigLoader\\Core\\PathCandidates implements generic interface IteratorAggregate but does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: lib/ConfigLoader/Core/PathCandidates.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Core\\PathCandidates\:\:__construct\(\) has parameter \$candidates with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ConfigLoader/Core/PathCandidates.php - - - - message: '#^Parameter \#1 \$candidate of method Phpactor\\ConfigLoader\\Core\\PathCandidates\:\:add\(\) expects Phpactor\\ConfigLoader\\Core\\PathCandidate, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ConfigLoader/Core/PathCandidates.php - - - - message: '#^Parameter \#1 \$json of function json_decode expects string, string\|false given\.$#' - identifier: argument.type - count: 2 - path: lib/ConfigLoader/Tests/Benchmark/ConfigLoaderBench.php - - - - message: '#^Parameter \#1 \.\.\.\$arrays of function array_merge expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ConfigLoader/Tests/Benchmark/ConfigLoaderBench.php - - - - message: '#^Parameter \#2 \.\.\.\$arrays of function array_merge expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ConfigLoader/Tests/Benchmark/ConfigLoaderBench.php - - - - message: '#^Method Phpactor\\ConfigLoader\\Tests\\Integration\\ConfigLoaderTest\:\:createConfig\(\) has parameter \$array with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ConfigLoader/Tests/Integration/ConfigLoaderTest.php - - - - message: '#^Parameter \#1 \$changes of class Phpactor\\Configurator\\Model\\Changes constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Configurator/Model/Changes.php - - - - message: '#^Parameter \#1 \$from of class SebastianBergmann\\Diff\\Diff constructor expects non\-empty\-string, '''' given\.$#' - identifier: argument.type - count: 1 - path: lib/Diff/Tests/RangesForDiffTest.php - - - - message: '#^Parameter \#2 \$to of class SebastianBergmann\\Diff\\Diff constructor expects non\-empty\-string, '''' given\.$#' - identifier: argument.type - count: 1 - path: lib/Diff/Tests/RangesForDiffTest.php - - - - message: '#^Cannot call method length\(\) on Phpactor\\DocblockParser\\Ast\\Token\|null\.$#' - identifier: method.nonObject - count: 2 - path: lib/DocblockParser/Ast/Docblock.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/DocblockParser/Ast/Node.php - - - - message: '#^Call to function is_iterable\(\) with array\ will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/DocblockParser/Ast/Node.php - - - - message: '#^Generator expects value type Phpactor\\DocblockParser\\Ast\\Element, mixed given\.$#' - identifier: generator.valueType - count: 1 - path: lib/DocblockParser/Ast/Node.php - - - - message: '#^Parameter \#1 \$elements of method Phpactor\\DocblockParser\\Ast\\Node\:\:endOf\(\) expects iterable\\|Phpactor\\DocblockParser\\Ast\\Element\|null\>, array given\.$#' - identifier: argument.type - count: 1 - path: lib/DocblockParser/Ast/Node.php - - - - message: '#^Parameter \#1 \$nodes of method Phpactor\\DocblockParser\\Ast\\Node\:\:findTokens\(\) expects iterable\\|Phpactor\\DocblockParser\\Ast\\Element\>, array\\|\(iterable&Phpactor\\DocblockParser\\Ast\\Element\) given\.$#' - identifier: argument.type - count: 1 - path: lib/DocblockParser/Ast/Node.php - - - - message: '#^Parameter \#1 \$nodes of method Phpactor\\DocblockParser\\Ast\\Node\:\:traverseNodes\(\) expects iterable\\|Phpactor\\DocblockParser\\Ast\\Element\>, array\\|\(iterable&Phpactor\\DocblockParser\\Ast\\Element\) given\.$#' - identifier: argument.type - count: 1 - path: lib/DocblockParser/Ast/Node.php - - - - message: '#^Parameter \#1 \$list of class Phpactor\\DocblockParser\\Ast\\TypeList constructor expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: lib/DocblockParser/Parser.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\DocblockParser\\Ast\\Element\)\: string given\.$#' - identifier: argument.type - count: 1 - path: lib/DocblockParser/Printer/TestPrinter.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\DocblockParser\\\\Ast\\\\Type\\\\UnionNode'' and Phpactor\\DocblockParser\\Ast\\Type\\UnionNode will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTest.php - - - - message: '#^Cannot access property \$value on Phpactor\\DocblockParser\\Ast\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTest.php - - - - message: '#^Cannot call method text\(\) on Phpactor\\DocblockParser\\Ast\\Tag\\ParamTag\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTest.php - - - - message: '#^Cannot call method toString\(\) on Phpactor\\DocblockParser\\Ast\\ParameterList\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTest.php - - - - message: '#^Cannot call method toString\(\) on Phpactor\\DocblockParser\\Ast\\TextNode\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTest.php - - - - message: '#^Cannot call method toString\(\) on Phpactor\\DocblockParser\\Ast\\Token\|null\.$#' - identifier: method.nonObject - count: 4 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTest.php - - - - message: '#^Cannot call method toString\(\) on Phpactor\\DocblockParser\\Ast\\TypeNode\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTest.php - - - - message: '#^@dataProvider provideNode related method not found\.$#' - identifier: phpunit.dataProviderMethod - count: 3 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTestCase.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\DocblockParser\\\\Ast\\\\Element'' and Phpactor\\DocblockParser\\Ast\\Node will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTestCase.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertIsIterable\(\) with list\ will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/DocblockParser/Tests/Unit/Ast/NodeTestCase.php - - - - message: '#^Binary operation "\." between non\-falsy\-string and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Behat/Behat/BehatConfig.php - - - - message: '#^Cannot access offset ''imports'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Behat/Behat/BehatConfig.php - - - - message: '#^Parameter \#1 \$config of method Phpactor\\Extension\\Behat\\Behat\\BehatConfig\:\:parseContexts\(\) expects array\\|string\>\}\>\}\>, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/Behat/BehatConfig.php - - - - message: '#^Method Phpactor\\Extension\\Behat\\Behat\\Pattern\\TurnipPatternPolicy\:\:replaceTokensWithRegexCaptureGroups\(\) should return string but returns string\|null\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Behat/Behat/Pattern/TurnipPatternPolicy.php - - - - message: '#^Method Phpactor\\Extension\\Behat\\Behat\\Pattern\\TurnipPatternPolicy\:\:replaceTurnipOptionalEndingWithRegex\(\) should return string but returns string\|null\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Behat/Behat/Pattern/TurnipPatternPolicy.php - - - - message: '#^Parameter \#1 \$regex of method Phpactor\\Extension\\Behat\\Behat\\Pattern\\TurnipPatternPolicy\:\:removeEscapingOfAlternationSyntax\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/Behat/Pattern/TurnipPatternPolicy.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#1 \$config of class Phpactor\\Extension\\Behat\\Behat\\StepGenerator constructor expects Phpactor\\Extension\\Behat\\Behat\\BehatConfig, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#1 \$generator of class Phpactor\\Extension\\Behat\\Completor\\FeatureStepCompletor constructor expects Phpactor\\Extension\\Behat\\Behat\\StepGenerator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#1 \$generator of class Phpactor\\Extension\\Behat\\ReferenceFinder\\StepDefinitionLocator constructor expects Phpactor\\Extension\\Behat\\Behat\\StepGenerator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#1 \$path of class Phpactor\\Extension\\Behat\\Behat\\BehatConfig constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\Behat\\Adapter\\Worse\\WorseContextClassResolver constructor expects Phpactor\\WorseReflection\\Core\\Reflector\\ClassReflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\Behat\\Adapter\\Worse\\WorseStepFactory constructor expects Phpactor\\WorseReflection\\Core\\Reflector\\ClassReflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#1 \$xmlPath of class Phpactor\\Extension\\Behat\\Adapter\\Symfony\\SymfonyDiContextClassResolver constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#2 \$factory of class Phpactor\\Extension\\Behat\\Behat\\StepGenerator constructor expects Phpactor\\Extension\\Behat\\Behat\\StepFactory, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#2 \$parser of class Phpactor\\Extension\\Behat\\Completor\\FeatureStepCompletor constructor expects Phpactor\\Extension\\Behat\\Behat\\StepParser, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#2 \$parser of class Phpactor\\Extension\\Behat\\ReferenceFinder\\StepDefinitionLocator constructor expects Phpactor\\Extension\\Behat\\Behat\\StepParser, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#3 \$parser of class Phpactor\\Extension\\Behat\\Behat\\StepGenerator constructor expects Phpactor\\Extension\\Behat\\Behat\\StepParser, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/BehatExtension.php - - - - message: '#^Parameter \#1 \$steps of method Phpactor\\Extension\\Behat\\Behat\\StepScorer\:\:scoreSteps\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/Completor/FeatureStepCompletor.php - - - - message: '#^Parameter \#2 \$callback of function usort expects callable\(mixed, mixed\)\: int, Closure\(Phpactor\\Extension\\Behat\\Behat\\Step, Phpactor\\Extension\\Behat\\Behat\\Step\)\: int\<\-1, 1\> given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Behat/Completor/FeatureStepCompletor.php - - - - message: '#^Cannot call method matches\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Behat/ReferenceFinder/StepDefinitionLocator.php - - - - message: '#^Method Phpactor\\Extension\\Behat\\ReferenceFinder\\StepDefinitionLocator\:\:findSteps\(\) should return array\ but returns list\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Behat/ReferenceFinder/StepDefinitionLocator.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/Behat/Tests/Integration/Behat/BehatConfigTest.php - - - - message: '#^Instanceof between Phpactor\\Extension\\Behat\\Behat\\Context and Phpactor\\Extension\\Behat\\Behat\\Context will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/Behat/Tests/Integration/Behat/BehatConfigTest.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copy\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/ClassCopy.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copyClass\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/ClassCopy.php - - - - message: '#^Parameter \#2 \$code of class RuntimeException constructor expects int, null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassCopy.php - - - - message: '#^Result of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copyFile\(\) \(void\) is used\.$#' - identifier: method.void - count: 2 - path: lib/Extension/ClassMover/Application/ClassCopy.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences\:\:createQuery\(\) has parameter \$memberType with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ClassMover/Application/ClassMemberReferences.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences\:\:findOrReplaceReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/ClassMemberReferences.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences\:\:referencesInFile\(\) has parameter \$filePath with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ClassMover/Application/ClassMemberReferences.php - - - - message: '#^Parameter \#1 \$filename of function file_put_contents expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassMemberReferences.php - - - - message: '#^Parameter \#1 \$memberType of method Phpactor\\ClassMover\\Domain\\Model\\ClassMemberQuery\:\:withType\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassMemberReferences.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Filesystem\\Domain\\Filesystem\:\:getContents\(\) expects Phpactor\\Filesystem\\Domain\\FilePath\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassMemberReferences.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/Extension/ClassMover/Application/ClassMover.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:directoryMap\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/ClassMover.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:replaceThoseReferences\(\) has parameter \$files with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Application/ClassMover.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Filesystem\\Domain\\Filesystem\:\:createPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/ClassMover/Application/ClassMover.php - - - - message: '#^Parameter \#2 \$code of class RuntimeException constructor expects int, null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassMover.php - - - - message: '#^Parameter \#3 \$files of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:replaceThoseReferences\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassMover.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:fileReferences\(\) has parameter \$filePath with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ClassMover/Application/ClassReferences.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:findOrReplaceReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/ClassReferences.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:findReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/ClassReferences.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:replaceInSource\(\) has parameter \$replace with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ClassMover/Application/ClassReferences.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:replaceReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/ClassReferences.php - - - - message: '#^Parameter \#1 \$filename of function file_put_contents expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassReferences.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Filesystem\\Domain\\Filesystem\:\:getContents\(\) expects Phpactor\\Filesystem\\Domain\\FilePath\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassReferences.php - - - - message: '#^Parameter \#4 \$replace of method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:replaceReferencesInCode\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/ClassReferences.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Application/Finder/FileFinder.php - - - - message: '#^Variable \$member in PHPDoc tag @var does not match assigned variable \$private\.$#' - identifier: varTag.differentVariable - count: 1 - path: lib/Extension/ClassMover/Application/Finder/FileFinder.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\Logger\\ClassCopyLogger\:\:copying\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/Logger/ClassCopyLogger.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\Logger\\ClassCopyLogger\:\:replacing\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/Logger/ClassCopyLogger.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\Logger\\ClassMoverLogger\:\:moving\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/Logger/ClassMoverLogger.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Application\\Logger\\ClassMoverLogger\:\:replacing\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Application/Logger/ClassMoverLogger.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$classCopy of class Phpactor\\Extension\\ClassMover\\Rpc\\ClassCopyHandler constructor expects Phpactor\\Extension\\ClassMover\\Application\\ClassCopy, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$classFileNormalizer of class Phpactor\\Extension\\ClassMover\\Application\\ClassCopy constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$classFileNormalizer of class Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$classFileNormalizer of class Phpactor\\Extension\\ClassMover\\Application\\ClassMover constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$classFileNormalizerasd of class Phpactor\\Extension\\ClassMover\\Application\\ClassReferences constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$copier of class Phpactor\\Extension\\ClassMover\\Command\\ClassCopyCommand constructor expects Phpactor\\Extension\\ClassMover\\Application\\ClassCopy, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$memberReferences of class Phpactor\\Extension\\ClassMover\\Command\\ReferencesMemberCommand constructor expects Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$referenceFinder of class Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand constructor expects Phpactor\\Extension\\ClassMover\\Application\\ClassReferences, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\ClassMover\\Adapter\\WorseTolerant\\WorseTolerantMemberFinder constructor expects Phpactor\\WorseReflection\\Reflector\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#2 \$classReferences of class Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler constructor expects Phpactor\\Extension\\ClassMover\\Application\\ClassReferences, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\ClassMover\\Command\\ReferencesMemberCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#2 \$memberFinder of class Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences constructor expects Phpactor\\ClassMover\\Domain\\MemberFinder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#2 \$prompt of class Phpactor\\Extension\\ClassMover\\Command\\ClassCopyCommand constructor expects Phpactor\\Extension\\Core\\Console\\Prompt\\Prompt, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#2 \$prompt of class Phpactor\\Extension\\ClassMover\\Command\\ClassMoveCommand constructor expects Phpactor\\Extension\\Core\\Console\\Prompt\\Prompt, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#2 \$refFinder of class Phpactor\\Extension\\ClassMover\\Application\\ClassReferences constructor expects Phpactor\\ClassMover\\Domain\\ClassFinder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#3 \$classMemberReferences of class Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler constructor expects Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#3 \$filesystem of class Phpactor\\Extension\\ClassMover\\Application\\ClassCopy constructor expects Phpactor\\Filesystem\\Domain\\Filesystem, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#3 \$filesystemRegistry of class Phpactor\\Extension\\ClassMover\\Application\\ClassMover constructor expects Phpactor\\Filesystem\\Domain\\FilesystemRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#3 \$memberReplacer of class Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences constructor expects Phpactor\\ClassMover\\Domain\\MemberReplacer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#3 \$refReplacer of class Phpactor\\Extension\\ClassMover\\Application\\ClassReferences constructor expects Phpactor\\ClassMover\\Domain\\ClassReplacer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#4 \$filesystemRegistry of class Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences constructor expects Phpactor\\Filesystem\\Domain\\FilesystemRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#4 \$filesystemRegistry of class Phpactor\\Extension\\ClassMover\\Application\\ClassReferences constructor expects Phpactor\\Filesystem\\Domain\\FilesystemRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#4 \$pathFinder of class Phpactor\\Extension\\ClassMover\\Application\\ClassMover constructor expects Phpactor\\PathFinder\\PathFinder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#4 \$registry of class Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler constructor expects Phpactor\\Filesystem\\Domain\\FilesystemRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#5 \$reflector of class Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/ClassMoverExtension.php - - - - message: '#^Parameter \#2 \$default of method Phpactor\\Extension\\Core\\Console\\Prompt\\Prompt\:\:prompt\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassCopyCommand.php - - - - message: '#^Parameter \#2 \$src of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copy\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassCopyCommand.php - - - - message: '#^Parameter \#2 \$srcName of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copyClass\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassCopyCommand.php - - - - message: '#^Parameter \#2 \$srcPath of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copyFile\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassCopyCommand.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassCopyCommand.php - - - - message: '#^Parameter \#3 \$dest of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copy\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassCopyCommand.php - - - - message: '#^Parameter \#3 \$destName of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copyClass\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassCopyCommand.php - - - - message: '#^Parameter \#3 \$destPath of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copyFile\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassCopyCommand.php - - - - message: '#^Parameter \#2 \$default of method Phpactor\\Extension\\Core\\Console\\Prompt\\Prompt\:\:prompt\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#2 \$filesystemName of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:move\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#2 \$filesystemName of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:moveClass\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#2 \$filesystemName of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:moveFile\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#3 \$src of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:move\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#3 \$srcName of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:moveClass\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#3 \$srcPath of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:moveFile\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#4 \$dest of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:move\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#4 \$destName of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:moveClass\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Parameter \#4 \$destPath of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:moveFile\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ClassMoveCommand.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Cannot access offset ''file'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:addReferenceRow\(\) has parameter \$reference with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:findOrReplaceReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:findOrReplaceReferences\(\) has parameter \$class with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:findOrReplaceReferences\(\) has parameter \$dryRun with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:findOrReplaceReferences\(\) has parameter \$filesystem with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:findOrReplaceReferences\(\) has parameter \$replace with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:renderTable\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:renderTable\(\) has parameter \$results with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#1 \$filesystemName of method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:findReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#1 \$filesystemName of method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:replaceReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#1 \$line of static method Phpactor\\Extension\\Core\\Console\\Formatter\\Highlight\:\:highlightAtCol\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#2 \$class of method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:findReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#2 \$class of method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:replaceReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#2 \$data of method Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper\:\:dump\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#2 \$filePath of method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:addReferenceRow\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#2 \$results of method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:renderTable\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#2 \$subject of static method Phpactor\\Extension\\Core\\Console\\Formatter\\Highlight\:\:highlightAtCol\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#3 \$col of static method Phpactor\\Extension\\Core\\Console\\Formatter\\Highlight\:\:highlightAtCol\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#3 \$reference of method Phpactor\\Extension\\ClassMover\\Command\\ReferencesClassCommand\:\:addReferenceRow\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#3 \$replace of method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:replaceReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Parameter \#4 \$dryRun of method Phpactor\\Extension\\ClassMover\\Application\\ClassReferences\:\:replaceReferences\(\) expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesClassCommand.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Cannot access offset ''file'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Cannot access offset ''risky_references'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesMemberCommand\:\:addReferenceRow\(\) has parameter \$reference with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Command\\ReferencesMemberCommand\:\:renderTable\(\) has parameter \$results with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#1 \$array of function array_reduce expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#1 \$line of static method Phpactor\\Extension\\Core\\Console\\Formatter\\Highlight\:\:highlightAtCol\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#1 \$scope of method Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences\:\:findOrReplaceReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#2 \$class of method Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences\:\:findOrReplaceReferences\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#2 \$data of method Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper\:\:dump\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#2 \$filePath of method Phpactor\\Extension\\ClassMover\\Command\\ReferencesMemberCommand\:\:addReferenceRow\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#2 \$results of method Phpactor\\Extension\\ClassMover\\Command\\ReferencesMemberCommand\:\:renderTable\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#2 \$subject of static method Phpactor\\Extension\\Core\\Console\\Formatter\\Highlight\:\:highlightAtCol\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#3 \$col of static method Phpactor\\Extension\\Core\\Console\\Formatter\\Highlight\:\:highlightAtCol\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#3 \$memberName of method Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences\:\:findOrReplaceReferences\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#3 \$reference of method Phpactor\\Extension\\ClassMover\\Command\\ReferencesMemberCommand\:\:addReferenceRow\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#4 \$memberType of method Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences\:\:findOrReplaceReferences\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Parameter \#5 \$replace of method Phpactor\\Extension\\ClassMover\\Application\\ClassMemberReferences\:\:findOrReplaceReferences\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Command/ReferencesMemberCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ClassCopyHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassCopyHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ClassCopyHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassCopyHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassCopyHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\OpenFileResponse\:\:fromPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassCopyHandler.php - - - - message: '#^Parameter \#2 \$src of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copy\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassCopyHandler.php - - - - message: '#^Parameter \#3 \$default of static method Phpactor\\Extension\\Rpc\\Response\\Input\\TextInput\:\:fromNameLabelAndDefault\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassCopyHandler.php - - - - message: '#^Parameter \#3 \$dest of method Phpactor\\Extension\\ClassMover\\Application\\ClassCopy\:\:copy\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassCopyHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ClassMoveHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ClassMoveHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\CloseFileResponse\:\:fromPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\OpenFileResponse\:\:fromPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Parameter \#1 \$src of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:getRelatedFiles\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Parameter \#3 \$default of static method Phpactor\\Extension\\Rpc\\Response\\Input\\TextInput\:\:fromNameLabelAndDefault\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Parameter \#3 \$src of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:move\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Parameter \#4 \$dest of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:move\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Parameter \#5 \$moveRelatedFiles of method Phpactor\\Extension\\ClassMover\\Application\\ClassMover\:\:move\(\) expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ClassMoveHandler.php - - - - message: '#^Binary operation "\+\=" between float\|int\|TReturn and int\<0, max\> results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Binary operation "\-" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Cannot access offset ''references'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 3 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Cannot access offset ''risky_references'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 3 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^If condition is always true\.$#' - identifier: if.alwaysTrue - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:classReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:doPerformFindOrReplaceReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:echoMessage\(\) has parameter \$references with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:findReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:performFindOrReplaceReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:replaceReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:sortReferences\(\) has parameter \$fileReferences with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:sortReferences\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$array of function array_filter expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$array of function usort expects TArray of array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$array of static method Phpactor\\Extension\\Rpc\\Response\\FileReferencesResponse\:\:fromArray\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$fileReferences of method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:sortReferences\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$string1 of function strcmp expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$uri of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:uri\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 5 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(mixed\)\: bool\)\|null, Closure\(array\)\: bool given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#2 \$callback of function array_walk expects callable\(mixed, int\|string\)\: mixed, Closure\(array\)\: void given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#2 \$callback of function usort expects callable\(mixed, mixed\)\: int, Closure\(array, array\)\: int\<\-1, 1\> given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#2 \$filesystem of method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:findReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#2 \$filesystem of method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:replaceReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#2 \$string2 of function strcmp expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#3 \$newSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#3 \$replacement of method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:replaceReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#4 \$path of method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:replaceReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#4 \$references of method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:echoMessage\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Parameter \#5 \$source of method Phpactor\\Extension\\ClassMover\\Rpc\\ReferencesHandler\:\:replaceReferences\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassMover/Rpc/ReferencesHandler.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/Extension/ClassToFile/ClassToFileExtension.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ClassToFile/ClassToFileExtension.php - - - - message: '#^Parameter \#1 \$classLoader of class Phpactor\\ClassFileConverter\\Adapter\\Composer\\ComposerClassToFile constructor expects Composer\\Autoload\\ClassLoader, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFile/ClassToFileExtension.php - - - - message: '#^Parameter \#1 \$classLoader of class Phpactor\\ClassFileConverter\\Adapter\\Composer\\ComposerFileToClass constructor expects Composer\\Autoload\\ClassLoader, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFile/ClassToFileExtension.php - - - - message: '#^Parameter \#1 \$classToFile of class Phpactor\\ClassFileConverter\\Domain\\ClassToFileFileToClass constructor expects Phpactor\\ClassFileConverter\\Domain\\ClassToFile, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFile/ClassToFileExtension.php - - - - message: '#^Parameter \#1 \$cwd of class Phpactor\\ClassFileConverter\\Adapter\\Simple\\SimpleClassToFile constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFile/ClassToFileExtension.php - - - - message: '#^Parameter \#2 \$fileToClass of class Phpactor\\ClassFileConverter\\Domain\\ClassToFileFileToClass constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFile/ClassToFileExtension.php - - - - message: '#^Method Phpactor\\Extension\\ClassToFile\\Tests\\Unit\\ClassToFileExtensionTest\:\:create\(\) has parameter \$params with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassToFile/Tests/Unit/ClassToFileExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\ClassToFile\\Tests\\Unit\\ClassToFileExtensionTest\:\:createConverter\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassToFile/Tests/Unit/ClassToFileExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\ClassToFile\\Tests\\Unit\\ClassToFileExtensionTest\:\:createConverter\(\) should return Phpactor\\ClassFileConverter\\Domain\\ClassToFileFileToClass but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/ClassToFile/Tests/Unit/ClassToFileExtensionTest.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFile/Tests/Unit/ClassToFileExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\ClassToFileExtra\\Application\\FileInfo\:\:infoForFile\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassToFileExtra/Application/FileInfo.php - - - - message: '#^Parameter \#1 \$classToFileConverter of class Phpactor\\Extension\\ClassToFileExtra\\Application\\FileInfo constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/ClassToFileExtraExtension.php - - - - message: '#^Parameter \#1 \$fileInfo of class Phpactor\\Extension\\ClassToFileExtra\\Rpc\\FileInfoHandler constructor expects Phpactor\\Extension\\ClassToFileExtra\\Application\\FileInfo, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/ClassToFileExtraExtension.php - - - - message: '#^Parameter \#1 \$infoForOffset of class Phpactor\\Extension\\ClassToFileExtra\\Command\\FileInfoCommand constructor expects Phpactor\\Extension\\ClassToFileExtra\\Application\\FileInfo, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/ClassToFileExtraExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\ClassToFileExtra\\Command\\FileInfoCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/ClassToFileExtraExtension.php - - - - message: '#^Parameter \#2 \$filesystem of class Phpactor\\Extension\\ClassToFileExtra\\Application\\FileInfo constructor expects Phpactor\\Filesystem\\Domain\\Filesystem, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/ClassToFileExtraExtension.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/Command/FileInfoCommand.php - - - - message: '#^Parameter \#1 \$sourcePath of method Phpactor\\Extension\\ClassToFileExtra\\Application\\FileInfo\:\:infoForFile\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/Command/FileInfoCommand.php - - - - message: '#^Parameter \#2 \$data of method Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper\:\:dump\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/Command/FileInfoCommand.php - - - - message: '#^Method Phpactor\\Extension\\ClassToFileExtra\\Rpc\\FileInfoHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ClassToFileExtra/Rpc/FileInfoHandler.php - - - - message: '#^Method Phpactor\\Extension\\ClassToFileExtra\\Rpc\\FileInfoHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ClassToFileExtra/Rpc/FileInfoHandler.php - - - - message: '#^Parameter \#1 \$sourcePath of method Phpactor\\Extension\\ClassToFileExtra\\Application\\FileInfo\:\:infoForFile\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ClassToFileExtra/Rpc/FileInfoHandler.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Cannot access an offset on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Cannot access offset ''name'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Instanceof between Phpactor\\Extension\\Php\\Model\\PhpVersionResolver and Phpactor\\Extension\\Php\\Model\\PhpVersionResolver will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\CodeTransformExtension\:\:assertNameAttribute\(\) has parameter \$attrs with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\CodeTransformExtension\:\:assertNameAttribute\(\) has parameter \$serviceId with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(string\)\: mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$elements of static method Phpactor\\CodeTransform\\Domain\\AbstractCollection\\:\:fromArray\(\) expects array\, array given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$elements of static method Phpactor\\CodeTransform\\Domain\\AbstractCollection\\:\:fromArray\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$fileToClass of class Phpactor\\CodeTransform\\Adapter\\TolerantParser\\ClassToFile\\Transformer\\ClassNameFixerTransformer constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$generators of class Phpactor\\Extension\\CodeTransform\\Rpc\\ClassInflectHandler constructor expects Phpactor\\CodeTransform\\Domain\\Generators, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$generators of class Phpactor\\Extension\\CodeTransform\\Rpc\\ClassNewHandler constructor expects Phpactor\\CodeTransform\\Domain\\Generators, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$loader of class Twig\\Environment constructor expects Twig\\Loader\\LoaderInterface, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$paths of method Phpactor\\CodeBuilder\\Domain\\TemplatePathResolver\\PhpVersionPathResolver\:\:resolve\(\) expects list\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$phpVersion of class Phpactor\\CodeBuilder\\Domain\\TemplatePathResolver\\PhpVersionPathResolver constructor expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$phpVersion of method Phpactor\\CodeBuilder\\Adapter\\WorseReflection\\TypeRenderer\\WorseTypeRendererFactory\:\:rendererFor\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$renderer of class Phpactor\\CodeBuilder\\Adapter\\TolerantParser\\TolerantUpdater constructor expects Phpactor\\CodeBuilder\\Domain\\Renderer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$renderer of class Phpactor\\CodeTransform\\Adapter\\Native\\GenerateNew\\ClassGenerator constructor expects Phpactor\\CodeBuilder\\Domain\\Renderer, mixed given\.$#' - identifier: argument.type - count: 5 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#1 \$transformers of static method Phpactor\\CodeTransform\\CodeTransform\:\:fromTransformers\(\) expects Phpactor\\CodeTransform\\Domain\\Transformers, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#2 \$fileToClass of class Phpactor\\Extension\\CodeTransform\\Rpc\\ClassInflectHandler constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#2 \$fileToClass of class Phpactor\\Extension\\CodeTransform\\Rpc\\ClassNewHandler constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#2 \$renderer of class Phpactor\\CodeTransform\\Adapter\\WorseReflection\\GenerateFromExisting\\InterfaceFromExistingGenerator constructor expects Phpactor\\CodeBuilder\\Domain\\Renderer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#2 \$variant of class Phpactor\\CodeTransform\\Adapter\\Native\\GenerateNew\\ClassGenerator constructor expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 4 - path: lib/Extension/CodeTransform/CodeTransformExtension.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\AbstractClassGenerateHandler\:\:className\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\AbstractClassGenerateHandler\:\:generate\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\AbstractClassGenerateHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\AbstractClassGenerateHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\AbstractClassGenerateHandler\:\:writeFileContents\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#1 \$filename of function file_exists expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#1 \$filename of function file_put_contents expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#1 \$filename of function filesize expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#1 \$keys of function array_combine expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#1 \$path of function dirname expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\ReplaceFileSourceResponse\:\:fromPathAndSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#3 \$default of static method Phpactor\\Extension\\Rpc\\Response\\Input\\TextInput\:\:fromNameLabelAndDefault\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\ClassInflectHandler\:\:generate\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransform/Rpc/ClassInflectHandler.php - - - - message: '#^Parameter \#1 \$existingClass of method Phpactor\\CodeTransform\\Domain\\GenerateFromExisting\:\:generateFromExisting\(\) expects Phpactor\\CodeTransform\\Domain\\ClassName, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/ClassInflectHandler.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\CodeTransform\\Domain\\AbstractCollection\\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/ClassInflectHandler.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Extension\\CodeTransform\\Rpc\\AbstractClassGenerateHandler\:\:className\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransform/Rpc/ClassInflectHandler.php - - - - message: '#^Parameter \#2 \$targetName of method Phpactor\\CodeTransform\\Domain\\GenerateFromExisting\:\:generateFromExisting\(\) expects Phpactor\\CodeTransform\\Domain\\ClassName, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/ClassInflectHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\ClassNewHandler\:\:generate\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransform/Rpc/ClassNewHandler.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\CodeTransform\\Domain\\AbstractCollection\\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/ClassNewHandler.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Extension\\CodeTransform\\Rpc\\AbstractClassGenerateHandler\:\:className\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/ClassNewHandler.php - - - - message: '#^Parameter \#1 \$targetName of method Phpactor\\CodeTransform\\Domain\\GenerateNew\:\:generateNew\(\) expects Phpactor\\CodeTransform\\Domain\\ClassName, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/ClassNewHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\TransformHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\TransformHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Rpc\\TransformHandler\:\:transformerChoiceAction\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Parameter \#1 \$callbackAction of static method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:fromCallbackAndInputs\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Parameter \#1 \$keys of function array_combine expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Extension\\CodeTransform\\Rpc\\TransformHandler\:\:transformerChoiceAction\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Parameter \#2 \$source of method Phpactor\\Extension\\CodeTransform\\Rpc\\TransformHandler\:\:transformerChoiceAction\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Rpc/TransformHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransform\\Tests\\Unit\\Rpc\\AbstractClassGenerateHandler\:\:exampleNewPath\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Property Phpactor\\Extension\\CodeTransform\\Tests\\Unit\\Rpc\\AbstractClassGenerateHandler\:\:\$fileToClass with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/AbstractClassGenerateHandler.php - - - - message: '#^Parameter \#1 \$elements of class Phpactor\\CodeTransform\\Domain\\Generators constructor expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassInflectHandlerTest.php - - - - message: '#^Parameter \#1 \$filename of method PHPUnit\\Framework\\Assert\:\:assertFileExists\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassInflectHandlerTest.php - - - - message: '#^Parameter \#2 \$fileToClass of class Phpactor\\Extension\\CodeTransform\\Rpc\\ClassInflectHandler constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, object given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassInflectHandlerTest.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassInflectHandlerTest.php - - - - message: '#^Property Phpactor\\Extension\\CodeTransform\\Tests\\Unit\\Rpc\\ClassInflectHandlerTest\:\:\$generator with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassInflectHandlerTest.php - - - - message: '#^Parameter \#1 \$elements of class Phpactor\\CodeTransform\\Domain\\Generators constructor expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassNewHandlerTest.php - - - - message: '#^Parameter \#1 \$filename of method PHPUnit\\Framework\\Assert\:\:assertFileExists\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassNewHandlerTest.php - - - - message: '#^Parameter \#2 \$fileToClass of class Phpactor\\Extension\\CodeTransform\\Rpc\\ClassNewHandler constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, object given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassNewHandlerTest.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassNewHandlerTest.php - - - - message: '#^Property Phpactor\\Extension\\CodeTransform\\Tests\\Unit\\Rpc\\ClassNewHandlerTest\:\:\$generator with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/ClassNewHandlerTest.php - - - - message: '#^Parameter \#1 \$elements of class Phpactor\\CodeTransform\\Domain\\Transformers constructor expects array\, array\ given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/TransformHandlerTest.php - - - - message: '#^Property Phpactor\\Extension\\CodeTransform\\Tests\\Unit\\Rpc\\TransformHandlerTest\:\:\$codeTransform with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/TransformHandlerTest.php - - - - message: '#^Property Phpactor\\Extension\\CodeTransform\\Tests\\Unit\\Rpc\\TransformHandlerTest\:\:\$transformer with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/CodeTransform/Tests/Unit/Rpc/TransformHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Application\\AbstractClassGenerator\:\:availableGenerators\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Application/AbstractClassGenerator.php - - - - message: '#^Call to an undefined method Phpactor\\CodeTransform\\Domain\\Generator\:\:generateFromExisting\(\)\.$#' - identifier: method.notFound - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassInflect.php - - - - message: '#^Cannot call method withPath\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassInflect.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassInflect.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect\:\:doGenerateFromExisting\(\) should return Phpactor\\CodeTransform\\Domain\\SourceCode but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassInflect.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect\:\:generateFromExisting\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassInflect.php - - - - message: '#^Call to an undefined method Phpactor\\CodeTransform\\Domain\\Generator\:\:generateNew\(\)\.$#' - identifier: method.notFound - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassNew.php - - - - message: '#^Cannot call method withPath\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassNew.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassNew.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassNew\:\:generate\(\) should return Phpactor\\CodeTransform\\Domain\\SourceCode but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/CodeTransformExtra/Application/ClassNew.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Application\\Transformer\:\:transform\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Application/Transformer.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Application\\Transformer\:\:transform\(\) has parameter \$source with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/CodeTransformExtra/Application/Transformer.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Application\\Transformer\:\:transform\(\) has parameter \$transformations with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Application/Transformer.php - - - - message: '#^PHPDoc tag @var with type string is not subtype of native type non\-empty\-string\|false\.$#' - identifier: varTag.nativeType - count: 1 - path: lib/Extension/CodeTransformExtra/Application/Transformer.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Application/Transformer.php - - - - message: '#^Parameter \#1 \$filePath of method Phpactor\\Extension\\Core\\Application\\Helper\\FilesystemHelper\:\:contentsFromFileOrStdin\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Application/Transformer.php - - - - message: '#^Parameter \#1 \$filename of function file_exists expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Application/Transformer.php - - - - message: '#^Parameter \#1 \$path of static method Symfony\\Component\\Filesystem\\Path\:\:makeAbsolute\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Application/Transformer.php - - - - message: '#^Parameter \#1 \$classInflect of class Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassInflectCommand constructor expects Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#1 \$classNew of class Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassNewCommand constructor expects Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassNew, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#1 \$handler of class Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ImportMissingClassesHandler constructor expects Phpactor\\Extension\\Rpc\\RequestHandler, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#1 \$normalizer of class Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#1 \$normalizer of class Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassNew constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#1 \$transformer of class Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassTransformCommand constructor expects Phpactor\\Extension\\CodeTransformExtra\\Application\\Transformer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#2 \$classSearch of class Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ImportClassHandler constructor expects Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassInflectCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassNewCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#2 \$generators of class Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect constructor expects Phpactor\\CodeTransform\\Domain\\Generators, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#2 \$generators of class Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassNew constructor expects Phpactor\\CodeTransform\\Domain\\Generators, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#2 \$reflector of class Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ImportMissingClassesHandler constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#2 \$reflector of class Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Parameter \#3 \$propertyAccessGenerator of class Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler constructor expects Phpactor\\CodeTransform\\Domain\\Refactor\\PropertyAccessGenerator, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/CodeTransformExtraExtension.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassInflectCommand\:\:execute\(\) should return int but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassInflectCommand\:\:listGenerators\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassInflectCommand\:\:process\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Parameter \#1 \$srcPath of method Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect\:\:generateFromExisting\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Parameter \#2 \$data of method Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper\:\:dump\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Parameter \#2 \$dest of method Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect\:\:generateFromExisting\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Parameter \#3 \$variant of method Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect\:\:generateFromExisting\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Parameter \#4 \$overwrite of method Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassInflect\:\:generateFromExisting\(\) expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassInflectCommand.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassNewCommand\:\:process\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php - - - - message: '#^Parameter \#1 \$src of method Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassNewCommand\:\:generateSourceCode\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php - - - - message: '#^Parameter \#2 \$data of method Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper\:\:dump\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php - - - - message: '#^Parameter \#2 \$variant of method Phpactor\\Extension\\CodeTransformExtra\\Command\\ClassNewCommand\:\:generateSourceCode\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php - - - - message: '#^Parameter \#3 \$overwrite of method Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassNew\:\:generate\(\) expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassNewCommand.php - - - - message: '#^PHPDoc tag @var for variable \$transformations has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassTransformCommand.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassTransformCommand.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Phpactor\:\:normalizePath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassTransformCommand.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassTransformCommand.php - - - - message: '#^Parameter \#2 \$to of method SebastianBergmann\\Diff\\Differ\:\:diff\(\) expects array\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Command/ClassTransformCommand.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ChangeVisiblityHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ChangeVisiblityHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Domain\\Refactor\\ChangeVisiblity\:\:changeVisiblity\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ExtractConstantHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ExtractConstantHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractConstant\:\:extractConstant\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#3 \$constantName of method Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractConstant\:\:extractConstant\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Parameter \#3 \$default of static method Phpactor\\Extension\\Rpc\\Response\\Input\\TextInput\:\:fromNameLabelAndDefault\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractConstantHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ExtractExpressionHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ExtractExpressionHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Parameter \#2 \$offsetStart of method Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractExpression\:\:extractExpression\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Parameter \#3 \$offsetEnd of method Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractExpression\:\:extractExpression\(\) expects int\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Parameter \#4 \$variableName of method Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractExpression\:\:extractExpression\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ExtractMethodHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ExtractMethodHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Parameter \#2 \$offsetStart of method Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractMethod\:\:extractMethod\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Parameter \#3 \$offsetEnd of method Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractMethod\:\:extractMethod\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Parameter \#4 \$name of method Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractMethod\:\:extractMethod\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ExtractMethodHandler.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\GenerateMethodHandler\:\:determineOriginalSource\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\GenerateMethodHandler\:\:determineOriginalSource\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\GenerateMethodHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\GenerateMethodHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Domain\\Refactor\\GenerateMember\:\:generateMember\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/GenerateMethodHandler.php - - - - message: '#^Call to an undefined method Phpactor\\CodeTransform\\Domain\\Refactor\\ImportClass\\NameAlreadyUsedException\:\:name\(\)\.$#' - identifier: method.notFound - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ImportClassHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ImportClassHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ImportClassHandler\:\:suggestions\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$array of function reset expects array\|object, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(array\)\: mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$keys of function array_combine expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\CodeTransform\\Domain\\Refactor\\ImportClass\\NameImport\:\:forClass\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$text of callable Phpactor\\TextDocument\\Util\\WordAtOffset expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#2 \$alias of static method Phpactor\\CodeTransform\\Domain\\Refactor\\ImportClass\\NameImport\:\:forClass\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#2 \$byteOffset of callable Phpactor\\TextDocument\\Util\\WordAtOffset expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#2 \$values of function array_combine expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Parameter \#3 \$default of static method Phpactor\\Extension\\Rpc\\Response\\Input\\TextInput\:\:fromNameLabelAndDefault\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportClassHandler.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Diagnostics\\UnresolvableNameDiagnostic and Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Diagnostics\\UnresolvableNameDiagnostic will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ImportMissingClassesHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\ImportMissingClassesHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\Rpc\\RequestHandler\:\:handle\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php - - - - message: '#^Parameter \#1 \$uri of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:uri\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandler.php - - - - message: '#^Call to an undefined method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:withMultiple\(\)\.$#' - identifier: method.notFound - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 3 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:class\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:class\(\) has parameter \$className with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:class\(\) has parameter \$source with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:methodChoices\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:parentClass\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#1 \$class of method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:parentClass\(\) expects Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#1 \$parentClass of method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\OverrideMethodHandler\:\:methodChoices\(\) expects Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#1 \$sourceCode of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:fromUnknown\(\) expects Phpactor\\TextDocument\\TextDocument\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#3 \$choices of static method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:fromNameLabelChoices\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Parameter \#3 \$methodName of method Phpactor\\CodeTransform\\Domain\\Refactor\\OverrideMethod\:\:overrideMethod\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Strict comparison using \=\=\= between null and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass will always evaluate to false\.$#' - identifier: identical.alwaysFalse - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/OverrideMethodHandler.php - - - - message: '#^Call to an undefined method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:withMultiple\(\)\.$#' - identifier: method.notFound - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:getPropertyContext\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:handleClass\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:handleClass\(\) should return Phpactor\\Extension\\Rpc\\Response but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:handleSingle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:handleSingle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:propertiesChoices\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\PropertyAccessGeneratorHandler\:\:class\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#1 \$sourceCode of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:fromUnknown\(\) expects Phpactor\\TextDocument\\TextDocument\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#1 \$text of method Phpactor\\TextDocument\\TextEdits\:\:apply\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectOffset\(\) expects int\|Phpactor\\TextDocument\\ByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#2 \$propertyNames of method Phpactor\\CodeTransform\\Domain\\Refactor\\PropertyAccessGenerator\:\:generate\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Parameter \#3 \$offset of method Phpactor\\CodeTransform\\Domain\\Refactor\\PropertyAccessGenerator\:\:generate\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\RenameVariableHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Method Phpactor\\Extension\\CodeTransformExtra\\Rpc\\RenameVariableHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\CodeTransform\\Domain\\Refactor\\RenameVariable\:\:renameVariable\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Parameter \#2 \$oldSource of static method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Parameter \#2 \$path of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Parameter \#3 \$default of static method Phpactor\\Extension\\Rpc\\Response\\Input\\TextInput\:\:fromNameLabelAndDefault\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Parameter \#3 \$newName of method Phpactor\\CodeTransform\\Domain\\Refactor\\RenameVariable\:\:renameVariable\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Parameter \#4 \$scope of method Phpactor\\CodeTransform\\Domain\\Refactor\\RenameVariable\:\:renameVariable\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CodeTransformExtra/Rpc/RenameVariableHandler.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/Completion/CompletionExtension.php - - - - message: '#^PHPDoc tag @var with type array\ is not subtype of native type array\\>\.$#' - identifier: varTag.nativeType - count: 1 - path: lib/Extension/Completion/CompletionExtension.php - - - - message: '#^Parameter \#1 \$formatters of class Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter constructor expects array\, list\ given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/Completion/CompletionExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 3 - path: lib/Extension/Completion/CompletionExtension.php - - - - message: '#^Cannot call method canFormat\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Completion/Tests/Unit/CompletionExtensionTest.php - - - - message: '#^Cannot call method signatureHelp\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Completion/Tests/Unit/CompletionExtensionTest.php - - - - message: '#^Property Phpactor\\Extension\\Completion\\Tests\\Unit\\CompletionExtensionTest\:\:\$completor1 with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/Completion/Tests/Unit/CompletionExtensionTest.php - - - - message: '#^Property Phpactor\\Extension\\Completion\\Tests\\Unit\\CompletionExtensionTest\:\:\$formatter1 with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/Completion/Tests/Unit/CompletionExtensionTest.php - - - - message: '#^Property Phpactor\\Extension\\Completion\\Tests\\Unit\\CompletionExtensionTest\:\:\$signatureHelper1 with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/Completion/Tests/Unit/CompletionExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\CompletionExtra\\Application\\Complete\:\:complete\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/CompletionExtra/Application/Complete.php - - - - message: '#^Parameter \#1 \$filePath of method Phpactor\\Extension\\Core\\Application\\Helper\\FilesystemHelper\:\:contentsFromFileOrStdin\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionExtra/Command/CompleteCommand.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionExtra/Command/CompleteCommand.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\Extension\\CompletionExtra\\Application\\Complete\:\:complete\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionExtra/Command/CompleteCommand.php - - - - message: '#^Parameter \#3 \$type of method Phpactor\\Extension\\CompletionExtra\\Application\\Complete\:\:complete\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionExtra/Command/CompleteCommand.php - - - - message: '#^Parameter \#1 \$complete of class Phpactor\\Extension\\CompletionExtra\\Command\\CompleteCommand constructor expects Phpactor\\Extension\\CompletionExtra\\Application\\Complete, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionExtra/CompletionExtraExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\CompletionExtra\\Rpc\\HoverHandler constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionExtra/CompletionExtraExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\CompletionExtra\\Command\\CompleteCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionExtra/CompletionExtraExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\Extension\\CompletionExtra\\Rpc\\HoverHandler constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionExtra/CompletionExtraExtension.php - - - - message: '#^Method Phpactor\\Extension\\CompletionExtra\\Rpc\\HoverHandler\:\:renderClass\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CompletionExtra/Rpc/HoverHandler.php - - - - message: '#^Method Phpactor\\Extension\\CompletionExtra\\Rpc\\HoverHandler\:\:renderFunction\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CompletionExtra/Rpc/HoverHandler.php - - - - message: '#^Method Phpactor\\Extension\\CompletionExtra\\Rpc\\HoverHandler\:\:renderMember\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: lib/Extension/CompletionExtra/Rpc/HoverHandler.php - - - - message: '#^Method Phpactor\\Extension\\CompletionExtra\\Rpc\\HoverHandler\:\:renderSymbolContext\(\) should return string\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/CompletionExtra/Rpc/HoverHandler.php - - - - message: '#^Method Phpactor\\Extension\\CompletionExtra\\Rpc\\HoverHandler\:\:renderVariable\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/CompletionExtra/Rpc/HoverHandler.php - - - - message: '#^Parameter \#1 \$language of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:language\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionRpc/Handler/CompleteHandler.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionRpc/Handler/CompleteHandler.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionRpc/Handler/CompleteHandler.php - - - - message: '#^Parameter \#1 \$type of method Phpactor\\Completion\\Core\\TypedCompletorRegistry\:\:completorForType\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionRpc/Handler/CompleteHandler.php - - - - message: '#^Method Phpactor\\Extension\\CompletionRpc\\Tests\\Unit\\CompletionRpcExtensionTest\:\:createRequestHandler\(\) should return Phpactor\\Extension\\Rpc\\RequestHandler but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/CompletionRpc/Tests/Unit/CompletionRpcExtensionTest.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\Rpc\\RequestHandler\:\:handle\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionRpc/Tests/Unit/CompletionRpcExtensionTest.php - - - - message: '#^Cannot access offset ''suggestions'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/CompletionRpc/Tests/Unit/Handler/CompleteHandlerTest.php - - - - message: '#^Parameter \#2 \$haystack of method PHPUnit\\Framework\\Assert\:\:assertCount\(\) expects Countable\|iterable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionRpc/Tests/Unit/Handler/CompleteHandlerTest.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(string\)\: mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$filesystem of class Phpactor\\Completion\\Bridge\\TolerantParser\\SourceCodeFilesystem\\ScfClassCompletor constructor expects Phpactor\\Filesystem\\Domain\\Filesystem, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$formatters of class Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$innerCompletor of class Phpactor\\Completion\\Bridge\\TolerantParser\\DebugTolerantCompletor constructor expects Phpactor\\Completion\\Bridge\\TolerantParser\\TolerantCompletor, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$key of method Phpactor\\Extension\\CompletionWorse\\CompletionWorseExtension\:\:completorEnabledKey\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\Helper\\VariableCompletionHelper constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseClassMemberCompletor constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseConstructorCompletor constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseDeclaredClassCompletor constructor expects Phpactor\\WorseReflection\\Core\\Reflector\\ClassReflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseFunctionCompletor constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseNamedParameterCompletor constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseParameterCompletor constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseSignatureHelper constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\WorseReflection\\SnippetFormatter\\NameSearchResultClassSnippetFormatter constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\WorseReflection\\SnippetFormatter\\NameSearchResultFunctionSnippetFormatter constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Completion\\Bridge\\WorseReflection\\SuggestionDocumentor\\WorseSuggestionDocumentor constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#1 \$tolerantCompletors of class Phpactor\\Completion\\Bridge\\TolerantParser\\ChainTolerantCompletor constructor expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$fileToClass of class Phpactor\\Completion\\Bridge\\TolerantParser\\SourceCodeFilesystem\\ScfClassCompletor constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseClassMemberCompletor constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseConstructorCompletor constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseDeclaredClassCompletor constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseFunctionCompletor constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseNamedParameterCompletor constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseParameterCompletor constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseSignatureHelper constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \$renderer of class Phpactor\\Completion\\Bridge\\WorseReflection\\SuggestionDocumentor\\WorseSuggestionDocumentor constructor expects Phpactor\\ObjectRenderer\\Model\\ObjectRenderer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#3 \$snippetFormatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseClassMemberCompletor constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#3 \$snippetFormatter of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseFunctionCompletor constructor expects Phpactor\\Completion\\Core\\Formatter\\ObjectFormatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Parameter \#4 \$objectRenderer of class Phpactor\\Completion\\Bridge\\TolerantParser\\WorseReflection\\WorseClassMemberCompletor constructor expects Phpactor\\ObjectRenderer\\Model\\ObjectRenderer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 2 - path: lib/Extension/CompletionWorse/CompletionWorseExtension.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/CompletionWorse/Tests/Unit/CompletionWorseExtensionTest.php - - - - message: '#^Instanceof between Phpactor\\Completion\\Core\\Completor and Phpactor\\Completion\\Core\\Completor will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/CompletionWorse/Tests/Unit/CompletionWorseExtensionTest.php - - - - message: '#^Parameter \#2 \$haystack of function in_array expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/CompletionWorse/Tests/Unit/CompletionWorseExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\ComposerAutoloader\\ClassLoaderFactory\:\:resolveMap\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ComposerAutoloader/ClassLoaderFactory.php - - - - message: '#^Parameter \#1 \$classMap of method Composer\\Autoload\\ClassLoader\:\:addClassMap\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ComposerAutoloader/ClassLoaderFactory.php - - - - message: '#^Parameter \#2 \$paths of method Composer\\Autoload\\ClassLoader\:\:set\(\) expects list\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ComposerAutoloader/ClassLoaderFactory.php - - - - message: '#^Parameter \#2 \$paths of method Composer\\Autoload\\ClassLoader\:\:setPsr4\(\) expects list\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ComposerAutoloader/ClassLoaderFactory.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Method Phpactor\\Extension\\ComposerAutoloader\\ComposerAutoloaderExtension\:\:classMapsOnly\(\) has parameter \$autoloaderPaths with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Method Phpactor\\Extension\\ComposerAutoloader\\ComposerAutoloaderExtension\:\:classMapsOnly\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Method Phpactor\\Extension\\ComposerAutoloader\\ComposerAutoloaderExtension\:\:deregisterAutoloader\(\) has parameter \$currentAutoloaders with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Method Phpactor\\Extension\\ComposerAutoloader\\ComposerAutoloaderExtension\:\:logAutoloaderNotFound\(\) has parameter \$autoloaderPath with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(string\)\: Composer\\Autoload\\ClassLoader given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Parameter \#1 \$callback of function spl_autoload_register expects \(callable\(string\)\: void\)\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Parameter \#1 \$filename of function file_exists expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ComposerAutoloader/ComposerAutoloaderExtension.php - - - - message: '#^Method Phpactor\\Extension\\ComposerAutoloader\\Tests\\Unit\\ComposerAutoloaderExtensionTest\:\:create\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ComposerAutoloader/Tests/Unit/ComposerAutoloaderExtensionTest.php - - - - message: '#^Parameter \#1 \$array of function reset expects array\|object, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/Extension/ComposerAutoloader/Tests/Unit/ComposerAutoloaderExtensionTest.php - - - - message: '#^Parameter \#2 \$haystack of method PHPUnit\\Framework\\Assert\:\:assertCount\(\) expects Countable\|iterable, mixed given\.$#' - identifier: argument.type - count: 6 - path: lib/Extension/ComposerAutoloader/Tests/Unit/ComposerAutoloaderExtensionTest.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ComposerAutoloader/Tests/Unit/ComposerAutoloaderExtensionTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/Configuration/Model/JsonSchemaBuilder.php - - - - message: '#^Instanceof between Phpactor\\MapResolver\\Definition and Phpactor\\MapResolver\\Definition will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/Configuration/Model/JsonSchemaBuilder.php - - - - message: '#^Parameter \#1 \$verbosity of class Symfony\\Component\\Console\\Output\\ConsoleOutput constructor expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Console/ConsoleExtension.php - - - - message: '#^Parameter \#2 \$decorated of class Symfony\\Component\\Console\\Output\\ConsoleOutput constructor expects bool\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Console/ConsoleExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/Console/ConsoleExtension.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Console/Tests/Unit/ConsoleExtensionTest.php - - - - message: '#^Parameter \#1 \$array of static method Phpactor\\Extension\\ContextMenu\\Model\\ContextMenu\:\:fromArray\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/ContextMenuExtension.php - - - - message: '#^Parameter \#1 \$json of function json_decode expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/ContextMenuExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/ContextMenuExtension.php - - - - message: '#^Parameter \#3 \$classFileNormalizer of class Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/ContextMenuExtension.php - - - - message: '#^Cannot call method action\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Cannot call method handle\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Cannot call method nodeContext\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Cannot call method parameters\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Cannot call method symbol\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:actionSelectionAction\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:actionSelectionAction\(\) has parameter \$symbolMenu with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:actionSelectionAction\(\) should return Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:delegateAction\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:delegateAction\(\) has parameter \$symbolMenu with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:delegateAction\(\) should return Phpactor\\Extension\\Rpc\\Response but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:offsetFromSourceAndOffset\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:replaceTokens\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:replaceTokens\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:replaceTokens\(\) has parameter \$parameters with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:resolveAction\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:resolveAction\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\Extension\\ContextMenu\\Model\\Action\)\: \(string\|null\) given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#1 \$callbackAction of static method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:fromCallbackAndInputs\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\Extension\\Rpc\\Request\:\:fromNameAndParameters\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#1 \$offset of method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:resolveAction\(\) expects Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#1 \$parameters of method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:replaceTokens\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:offsetFromSourceAndOffset\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:offsetFromSourceAndOffset\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Extension\\Rpc\\Request\:\:fromNameAndParameters\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#2 \$symbol of method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:resolveAction\(\) expects Phpactor\\WorseReflection\\Core\\Inference\\Symbol, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Parameter \#3 \$currentPath of method Phpactor\\Extension\\ContextMenu\\Handler\\ContextMenuHandler\:\:offsetFromSourceAndOffset\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/ContextMenu/Handler/ContextMenuHandler.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Model\\Action\:\:__construct\(\) has parameter \$parameters with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Model/Action.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Model\\Action\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Model/Action.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Cannot call method key\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Model\\ContextMenu\:\:__construct\(\) has parameter \$actions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Model\\ContextMenu\:\:__construct\(\) has parameter \$contexts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Model\\ContextMenu\:\:forContext\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Model\\ContextMenu\:\:fromArray\(\) has parameter \$array with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Method Phpactor\\Extension\\ContextMenu\\Model\\ContextMenu\:\:getAction\(\) should return Phpactor\\Extension\\ContextMenu\\Model\\Action but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(string\)\: Phpactor\\Extension\\ContextMenu\\Model\\Action given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Parameter \#1 \$keys of function array_combine expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Parameter \#2 \$args of static method DTL\\Invoke\\Invoke\:\:new\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Parameter \#4 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Parameter \#5 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 5 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Property Phpactor\\Extension\\ContextMenu\\Model\\ContextMenu\:\:\$actions type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ContextMenu/Model/ContextMenu.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Application\\CacheClear\:\:cachePath\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Application/CacheClear.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 2 - path: lib/Extension/Core/Application/Helper/ClassFileNormalizer.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer\:\:classToFile\(\) should return string but returns null\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Core/Application/Helper/ClassFileNormalizer.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Application\\Helper\\FilesystemHelper\:\:contentsFromFileOrStdin\(\) should return string but returns string\|false\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Core/Application/Helper/FilesystemHelper.php - - - - message: '#^Cannot call method path\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/Core/Application/Status.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Application\\Status\:\:check\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Application/Status.php - - - - message: '#^Parameter \#1 \$filename of function file_exists expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Application/Status.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/Core/Application/Status.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Command/CacheClearCommand.php - - - - message: '#^Binary operation "\." between '' '' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Core/Command/ConfigDumpCommand.php - - - - message: '#^Cannot call method path\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/Core/Command/ConfigDumpCommand.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Command\\ConfigDumpCommand\:\:__construct\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Command/ConfigDumpCommand.php - - - - message: '#^Parameter \#1 \$filename of function file_exists expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Command/ConfigDumpCommand.php - - - - message: '#^Parameter \#1 \$messages of method Symfony\\Component\\Console\\Output\\OutputInterface\:\:writeln\(\) expects iterable\|string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Command/ConfigDumpCommand.php - - - - message: '#^Property Phpactor\\Extension\\Core\\Command\\ConfigDumpCommand\:\:\$registry is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: lib/Extension/Core/Command/ConfigDumpCommand.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/Extension/Core/Command/ConfigJsonSchemaCommand.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 3 - path: lib/Extension/Core/Command/StatusCommand.php - - - - message: '#^Binary operation "\." between '' \✘\ '' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Core/Command/StatusCommand.php - - - - message: '#^Binary operation "\." between '' \✔\ '' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Core/Command/StatusCommand.php - - - - message: '#^Binary operation "\." between ''\Version\:\<…'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Core/Command/StatusCommand.php - - - - message: '#^Binary operation "\." between ''\Working…'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Core/Command/StatusCommand.php - - - - message: '#^Parameter \#2 \$array of function implode expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Command/StatusCommand.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Command/StatusCommand.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper\:\:dump\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Console/Dumper/Dumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper\:\:dump\(\) has parameter \$data with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Console/Dumper/Dumper.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: lib/Extension/Core/Console/Dumper/DumperRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:__construct\(\) has parameter \$dumpers with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Console/Dumper/DumperRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) should return Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Core/Console/Dumper/DumperRegistry.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Console/Dumper/DumperRegistry.php - - - - message: '#^Parameter \#2 \$dumper of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:add\(\) expects Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Console/Dumper/DumperRegistry.php - - - - message: '#^Property Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:\$dumpers has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/Extension/Core/Console/Dumper/DumperRegistry.php - - - - message: '#^Cannot use \+\+ on mixed\.$#' - identifier: preInc.type - count: 1 - path: lib/Extension/Core/Console/Dumper/IndentedDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\IndentedDumper\:\:doDump\(\) has parameter \$data with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Console/Dumper/IndentedDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\IndentedDumper\:\:doDump\(\) has parameter \$padding with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Core/Console/Dumper/IndentedDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\IndentedDumper\:\:dump\(\) has parameter \$data with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Console/Dumper/IndentedDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\IndentedDumper\:\:formatValue\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Console/Dumper/IndentedDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\IndentedDumper\:\:formatValue\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Core/Console/Dumper/IndentedDumper.php - - - - message: '#^Parameter \#2 \$times of function str_repeat expects int, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/Core/Console/Dumper/IndentedDumper.php - - - - message: '#^Parameter \#5 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Console/Dumper/IndentedDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\JsonDumper\:\:dump\(\) has parameter \$data with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Console/Dumper/JsonDumper.php - - - - message: '#^Parameter \#1 \$messages of method Symfony\\Component\\Console\\Output\\OutputInterface\:\:writeln\(\) expects iterable\|string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Console/Dumper/JsonDumper.php - - - - message: '#^Cannot use \+\+ on mixed\.$#' - identifier: preInc.type - count: 1 - path: lib/Extension/Core/Console/Dumper/TableDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\TableDumper\:\:dump\(\) has parameter \$data with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Console/Dumper/TableDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\TableDumper\:\:formatArray\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Console/Dumper/TableDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\TableDumper\:\:formatArray\(\) has parameter \$data with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Console/Dumper/TableDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Dumper\\TableDumper\:\:formatArray\(\) has parameter \$padding with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Core/Console/Dumper/TableDumper.php - - - - message: '#^Parameter \#2 \$times of function str_repeat expects int, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/Core/Console/Dumper/TableDumper.php - - - - message: '#^Parameter \#4 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Console/Dumper/TableDumper.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Formatter\\Highlight\:\:highlightAtCol\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Console/Formatter/Highlight.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Prompt\\BashPrompt\:\:getBashPath\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Console/Prompt/BashPrompt.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Prompt\\BashPrompt\:\:isSupported\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Console/Prompt/BashPrompt.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Console/Prompt/BashPrompt.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Cannot call method isSupported\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Cannot call method prompt\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Prompt\\ChainPrompt\:\:__construct\(\) has parameter \$prompts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Prompt\\ChainPrompt\:\:isSupported\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Prompt\\ChainPrompt\:\:prompt\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Parameter \#1 \$prompt of method Phpactor\\Extension\\Core\\Console\\Prompt\\ChainPrompt\:\:addPrompt\(\) expects Phpactor\\Extension\\Core\\Console\\Prompt\\Prompt, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Property Phpactor\\Extension\\Core\\Console\\Prompt\\ChainPrompt\:\:\$prompts has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/Extension/Core/Console/Prompt/ChainPrompt.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Console\\Prompt\\Prompt\:\:isSupported\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Console/Prompt/Prompt.php - - - - message: '#^Binary operation "\." between mixed and ''/\.phpactor'' results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Parameter \#1 \$cache of class Phpactor\\Extension\\Core\\Command\\CacheClearCommand constructor expects Phpactor\\Extension\\Core\\Application\\CacheClear, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Parameter \#1 \$cacheClear of class Phpactor\\Extension\\Core\\Rpc\\CacheClearHandler constructor expects Phpactor\\Extension\\Core\\Application\\CacheClear, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Parameter \#1 \$cachePath of class Phpactor\\Extension\\Core\\Application\\CacheClear constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Parameter \#1 \$fileClassConverter of class Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer constructor expects Phpactor\\ClassFileConverter\\Domain\\ClassToFileFileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Parameter \#1 \$status of class Phpactor\\Extension\\Core\\Command\\StatusCommand constructor expects Phpactor\\Extension\\Core\\Application\\Status, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Parameter \#1 \$status of class Phpactor\\Extension\\Core\\Rpc\\StatusHandler constructor expects Phpactor\\Extension\\Core\\Application\\Status, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Parameter \#2 \$default of class Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Parameter \#2 \$paths of class Phpactor\\Extension\\Core\\Rpc\\StatusHandler constructor expects Phpactor\\ConfigLoader\\Core\\PathCandidates, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/Core/CoreExtension.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\CacheClearHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Rpc/CacheClearHandler.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Rpc/CacheClearHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\ConfigHandler\:\:__construct\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Rpc/ConfigHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\ConfigHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Rpc/ConfigHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\ConfigHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Rpc/ConfigHandler.php - - - - message: '#^Parameter \#1 \$information of static method Phpactor\\Extension\\Rpc\\Response\\InformationResponse\:\:fromString\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Rpc/ConfigHandler.php - - - - message: '#^Binary operation "\." between ''Version\: '' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Binary operation "\." between ''Work dir\: '' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\StatusHandler\:\:buildConfigFileMessage\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\StatusHandler\:\:buildSupportMessage\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\StatusHandler\:\:buildSupportMessage\(\) has parameter \$diagnostics with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\StatusHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\StatusHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\StatusHandler\:\:handleDetailedType\(\) has parameter \$status with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Method Phpactor\\Extension\\Core\\Rpc\\StatusHandler\:\:handleFormattedType\(\) has parameter \$diagnostics with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\ConfigLoader\\Core\\PathCandidate\)\: non\-falsy\-string given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(string\)\: non\-falsy\-string given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Parameter \#1 \$keys of function array_fill_keys expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Rpc/StatusHandler.php - - - - message: '#^Parameter \#1 \$trust of class Phpactor\\Extension\\Core\\Trust\\Trust constructor expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Core/Trust/Trust.php - - - - message: '#^Parameter \#1 \$commandName of method Phpactor\\Extension\\Debug\\Model\\Documentor\:\:document\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Debug/Command/GenerateDocumentationCommand.php - - - - message: '#^Parameter \#1 \$string of method Phpactor\\Extension\\Debug\\Model\\DocumentorRegistry\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Debug/Command/GenerateDocumentationCommand.php - - - - message: '#^Parameter \#1 \$extensionFqns of class Phpactor\\Extension\\Debug\\Model\\ExtensionDocumentor constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Debug/DebugExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/Debug/DebugExtension.php - - - - message: '#^Binary operation "\." between ''/''\|''\\\\'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/FilePathResolver/FilePathResolverExtension.php - - - - message: '#^Parameter \#1 \$filters of class Phpactor\\FilePathResolver\\FilteringPathResolver constructor expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/FilePathResolver/FilePathResolverExtension.php - - - - message: '#^Parameter \#1 \$projectRoot of static method Phpactor\\Extension\\FilePathResolver\\FilePathResolverExtension\:\:calculateProjectId\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/FilePathResolver/Tests/Unit/FilePathResolverExtensionTest.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/FilePathResolver/Tests/Unit/FilePathResolverExtensionTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServer/Command/StartCommand.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServer/Command/StartCommand.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServer\\DiagnosticProvider\\AggregateDiagnosticsProvider\:\:names\(\) should return list\ but returns array\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/AggregateDiagnosticsProvider.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\LanguageServerProtocol\\Diagnostic\)\: Phpactor\\LanguageServerProtocol\\Diagnostic given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/AggregateDiagnosticsProvider.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/AggregateDiagnosticsProvider.php - - - - message: '#^Parameter \#1 \$array of function array_filter expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/CodeFilteringDiagnosticProvider.php - - - - message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(mixed\)\: bool\)\|null, Closure\(Phpactor\\LanguageServerProtocol\\Diagnostic\)\: bool given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/CodeFilteringDiagnosticProvider.php - - - - message: '#^Parameter \#1 \$array of static method Phpactor\\LanguageServerProtocol\\Diagnostic\:\:fromArray\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(array\)\: Phpactor\\LanguageServerProtocol\\Diagnostic given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php - - - - message: '#^Parameter \#1 \$json of function json_decode expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php - - - - message: '#^PHPDoc tag @var with type Phpactor\\Container\\Extension is not subtype of native type class\-string\.$#' - identifier: varTag.nativeType - count: 1 - path: lib/Extension/LanguageServer/Dispatcher/PhpactorDispatcherFactory.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServer\\EventDispatcher\\LazyAggregateProvider\:\:getListenersForEvent\(\) should return iterable\ but returns iterable\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServer/EventDispatcher/LazyAggregateProvider.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServer/Handler/DebugHandler.php - - - - message: '#^Instanceof between Phpactor\\LanguageServerProtocol\\TextDocumentItem and Phpactor\\LanguageServerProtocol\\TextDocumentItem will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServer/Handler/DebugHandler.php - - - - message: '#^Call to static method Webmozart\\Assert\\Assert\:\:isArray\(\) with array\ and ''Attributes must be…'' will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Parameter \#1 \$array of function array_flip expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Parameter \#1 \$commandMap of class Phpactor\\LanguageServer\\Core\\Command\\CommandDispatcher constructor expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Parameter \#1 \.\.\.\$handlers of class Phpactor\\LanguageServer\\Core\\Handler\\Handlers constructor expects Phpactor\\LanguageServer\\Core\\Handler\\Handler, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Parameter \#1 \.\.\.\$middleware of class Phpactor\\LanguageServer\\Core\\Dispatcher\\Dispatcher\\MiddlewareDispatcher constructor expects Phpactor\\LanguageServer\\Core\\Middleware\\Middleware, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Parameter \#2 \$formatter of class Phpactor\\LanguageServer\\Handler\\TextDocument\\FormattingHandler constructor expects Phpactor\\LanguageServer\\Core\\Formatting\\Formatter, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Parameter \#2 \$paths of class Phpactor\\Extension\\LanguageServer\\DiagnosticProvider\\PathExcludingDiagnosticsProvider constructor expects list\, non\-empty\-array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Parameter \#2 \$serviceIds of class Phpactor\\Extension\\LanguageServer\\EventDispatcher\\LazyAggregateProvider constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Parameter \#7 \$statusProviders of class Phpactor\\Extension\\LanguageServer\\Handler\\DebugHandler constructor expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/LanguageServer/LanguageServerExtension.php - - - - message: '#^Using nullsafe property access "\?\-\>workDoneProgress" on left side of \?\? is unnecessary\. Use \-\> instead\.$#' - identifier: nullsafe.neverNull - count: 1 - path: lib/Extension/LanguageServer/LanguageServerSessionExtension.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\LanguageServer\\Middleware\\TraceMiddleware\:\:format\(\) expects Phpactor\\LanguageServer\\Core\\Rpc\\Message\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/Middleware/TraceMiddleware.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServer/Telemetry/LanguageServerTelemetry.php - - - - message: '#^Instanceof between OpenTelemetry\\API\\Trace\\SpanInterface and OpenTelemetry\\API\\Trace\\SpanInterface will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServer/Telemetry/LanguageServerTelemetry.php - - - - message: '#^Parameter \#1 \$array of static method Phpactor\\LanguageServerProtocol\\Diagnostic\:\:fromArray\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/Command/DiagnosticsCommandTest.php - - - - message: '#^Parameter \#1 \$command of class Symfony\\Component\\Console\\Tester\\CommandTester constructor expects Symfony\\Component\\Console\\Command\\Command, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/Command/StartCommandTest.php - - - - message: '#^Parameter \#2 \$paths of class Phpactor\\Extension\\LanguageServer\\DiagnosticProvider\\PathExcludingDiagnosticsProvider constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/DiagnosticsProvider/PathExcludingDiagnosticsProviderTest.php - - - - message: '#^Parameter \#1 \$actual of static method PHPUnit\\Framework\\Assert\:\:assertJson\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/Handler/DebugHandlerTest.php - - - - message: '#^@covers value PhpactorDispatcherFactory\:\:resolveRootUri references an invalid method\.$#' - identifier: phpunit.coversMethod - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/LanguageServerExtensionTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServer\\\\Listener\\\\WorkspaceListener'' and Phpactor\\LanguageServer\\Listener\\WorkspaceListener will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/LanguageServerExtensionTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServer\\\\LanguageServerBuilder'' and Phpactor\\LanguageServer\\LanguageServerBuilder will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/LanguageServerTestCase.php - - - - message: '#^Parameter \#4 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/LanguageServerTestCase.php - - - - message: '#^Offset ''message'' might not exist on array\\|null\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/Listener/InvalidConfigListenerTest.php - - - - message: '#^Parameter \#2 \$haystack of static method PHPUnit\\Framework\\Assert\:\:assertStringContainsString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServer/Tests/Unit/Listener/InvalidConfigListenerTest.php - - - - message: '#^Parameter \#1 \$probe of method Blackfire\\Client\:\:endProbe\(\) expects Blackfire\\Probe, Blackfire\\Probe\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerBlackfire/BlackfireProfiler.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerBridge/Converter/PositionConverter.php - - - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' - identifier: arrayValues.list - count: 1 - path: lib/Extension/LanguageServerBridge/Converter/TextEditConverter.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerBridge\\TextDocument\\WorkspaceTextDocumentLocator constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerBridge/LanguageServerBridgeExtension.php - - - - message: '#^Static method PHPUnit\\Framework\\Assert\:\:assertEquals\(\) invoked with named argument \$actual, but it''s not allowed because of @no\-named\-arguments\.$#' - identifier: argument.named - count: 1 - path: lib/Extension/LanguageServerBridge/Tests/Converter/LocationConverterTest.php - - - - message: '#^Static method PHPUnit\\Framework\\Assert\:\:assertEquals\(\) invoked with named argument \$expected, but it''s not allowed because of @no\-named\-arguments\.$#' - identifier: argument.named - count: 1 - path: lib/Extension/LanguageServerBridge/Tests/Converter/LocationConverterTest.php - - - - message: '#^Class Phpactor\\Extension\\LanguageServerBridge\\Converter\\TextEditConverter does not have a constructor and must be instantiated without any parameters\.$#' - identifier: new.noConstructor - count: 1 - path: lib/Extension/LanguageServerBridge/Tests/Converter/TextEditConverterTest.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/CorrectUndefinedVariableCodeAction.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/CorrectUndefinedVariableCodeAction.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\CreateClassProvider\:\:provideActionsFor\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/CreateClassProvider.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/CreateUnresolvableClassProvider.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/CreateUnresolvableClassProvider.php - - - - message: '#^Cannot call method containingRange\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/CreateUnresolvableClassProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ExtractConstantProvider\:\:provideActionsFor\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractConstantProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ExtractExpressionProvider\:\:provideActionsFor\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractExpressionProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ExtractMethodProvider\:\:provideActionsFor\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ExtractMethodProvider.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateDecoratorProvider.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateDecoratorProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\GenerateDecoratorProvider\:\:provideActionsFor\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateDecoratorProvider.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Cannot call method memberType\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Cannot call method range\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\LanguageServerProtocol\\Diagnostic\)\: Phpactor\\LanguageServerProtocol\\CodeAction given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Parameter \#1 \$range of static method Phpactor\\Extension\\LanguageServerBridge\\Converter\\RangeConverter\:\:toLspRange\(\) expects Phpactor\\TextDocument\\ByteOffsetRange, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Parameter \#1 \$string of function ucfirst expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/GenerateMemberProvider.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php - - - - message: '#^Cannot call method candidateFqn\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php - - - - message: '#^Cannot call method unresolvedName\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ImportNameProvider\:\:diagnosticsFromUnresolvedName\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php - - - - message: '#^Parameter \#1 \$unresolvedName of method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ImportNameProvider\:\:codeActionForFqn\(\) expects Phpactor\\CodeTransform\\Domain\\NameWithByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php - - - - message: '#^Parameter \#2 \$fqn of method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ImportNameProvider\:\:codeActionForFqn\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php - - - - message: '#^Parameter \#3 \$hasCandidates of method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ImportNameProvider\:\:diagnosticsFromUnresolvedName\(\) expects bool\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php - - - - message: '#^Strict comparison using \!\=\= between Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameCandidate and null will always evaluate to true\.$#' - identifier: notIdentical.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ImportNameProvider.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/PropertyAccessGeneratorProvider.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionProperty and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionProperty will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/PropertyAccessGeneratorProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\PropertyAccessGeneratorProvider\:\:provideActionsFor\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/PropertyAccessGeneratorProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ReplaceQualifierWithImportProvider\:\:provideActionsFor\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/ReplaceQualifierWithImportProvider.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\LanguageServerProtocol\\Diagnostic\)\: Phpactor\\LanguageServerProtocol\\Diagnostic given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/TransformerCodeActionPovider.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/TransformerCodeActionPovider.php - - - - message: '#^Parameter \#2 \$diagnostics of static method Phpactor\\Extension\\LanguageServerCodeTransform\\Converter\\DiagnosticsConverter\:\:toLspDiagnostics\(\) expects Phpactor\\CodeTransform\\Domain\\Diagnostics, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/CodeAction/TransformerCodeActionPovider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Converter\\DiagnosticsConverter\:\:toLspDiagnostics\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Converter/DiagnosticsConverter.php - - - - message: '#^Parameter \#1 \$generators of class Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\CreateClassProvider constructor expects Phpactor\\CodeTransform\\Domain\\Generators, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php - - - - message: '#^Parameter \#1 \$transformers of class Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\TransformerCodeActionPovider constructor expects Phpactor\\CodeTransform\\Domain\\Transformers, mixed given\.$#' - identifier: argument.type - count: 10 - path: lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php - - - - message: '#^Parameter \#2 \$generators of class Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\CreateUnresolvableClassProvider constructor expects Phpactor\\CodeTransform\\Domain\\Generators, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php - - - - message: '#^Parameter \#2 \$reportNonExistingClasses of class Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\ImportNameProvider constructor expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php - - - - message: '#^Parameter \#3 \$classToFile of class Phpactor\\Extension\\LanguageServerCodeTransform\\CodeAction\\CreateUnresolvableClassProvider constructor expects Phpactor\\ClassFileConverter\\Domain\\ClassToFile, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php - - - - message: '#^Parameter \#3 \$generateAccessor of class Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\PropertyAccessGeneratorCommand constructor expects Phpactor\\CodeTransform\\Domain\\Refactor\\PropertyAccessGenerator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LanguageServerCodeTransformExtension.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\GenerateDecoratorCommand\:\:__invoke\(\) has invalid return type Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ApplyWorkspaceEditResult\.$#' - identifier: class.notFound - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/GenerateDecoratorCommand.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\GenerateDecoratorCommand\:\:__invoke\(\) should return Amp\\Promise\ but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/GenerateDecoratorCommand.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Cannot access property \$title on mixed\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Cannot call method candidateFqn\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Cannot call method onlyUniqueNames\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Instanceof between Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameCandidate and Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameCandidate will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ImportAllUnresolvedNamesCommand\:\:candidates\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ImportAllUnresolvedNamesCommand\:\:resolveCandidate\(\) has parameter \$candidates with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameCandidate\)\: Phpactor\\LanguageServerProtocol\\MessageActionItem given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Parameter \#4 \$fqn of method Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ImportNameCommand\:\:__invoke\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportAllUnresolvedNamesCommand.php - - - - message: '#^Cannot call method getMessage\(\) on Throwable\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportNameCommand.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ImportNameCommand\:\:__invoke\(\) return type with generic interface Amp\\Promise does not specify its types\: TValue$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ImportNameCommand.php - - - - message: '#^Cannot access property \$title on mixed\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php - - - - message: '#^Cannot call method __toString\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php - - - - message: '#^Cannot call method class\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php - - - - message: '#^Parameter \#2 \$className of method Phpactor\\CodeTransform\\Domain\\Refactor\\OverrideMethod\:\:overrideMethod\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php - - - - message: '#^Parameter \#3 \$methodName of method Phpactor\\CodeTransform\\Domain\\Refactor\\OverrideMethod\:\:overrideMethod\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/OverrideMethodCommand.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ReplaceQualifierWithImportCommand\:\:__invoke\(\) has invalid return type Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ApplyWorkspaceEditResult\.$#' - identifier: class.notFound - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ReplaceQualifierWithImportCommand.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ReplaceQualifierWithImportCommand\:\:__invoke\(\) should return Amp\\Promise\ but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/ReplaceQualifierWithImportCommand.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/TransformCommand.php - - - - message: '#^Instanceof between Phpactor\\CodeTransform\\Domain\\Transformer and Phpactor\\CodeTransform\\Domain\\Transformer will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/TransformCommand.php - - - - message: '#^Parameter \#1 \$textEdits of static method Phpactor\\Extension\\LanguageServerBridge\\Converter\\TextEditConverter\:\:toLspTextEdits\(\) expects Phpactor\\TextDocument\\TextEdits, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/LspCommand/TransformCommand.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php - - - - message: '#^Cannot call method byClass\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php - - - - message: '#^Instanceof between Phpactor\\CodeTransform\\Domain\\NameWithByteOffset and Phpactor\\CodeTransform\\Domain\\NameWithByteOffset will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php - - - - message: '#^Instanceof between Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameCandidate and Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameCandidate will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Diagnostics\\UnresolvableNameDiagnostic\)\: Phpactor\\CodeTransform\\Domain\\NameWithByteOffset given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php - - - - message: '#^Parameter \#1 \$iterator of function iterator_to_array expects iterable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/CandidateFinder.php - - - - message: '#^Binary operation "\." between mixed and string results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporter.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameImporterResult\:\:__construct\(\) has parameter \$textEdits with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporterResult.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameImporterResult\:\:getTextEdits\(\) should return array\\|null but returns array\|null\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/NameImport/NameImporterResult.php - - - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' - identifier: arrayValues.list - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Model/OverrideMethod/OverridableMethodFinder.php - - - - message: '#^Property Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Benchmark\\CodeAction\\ImportNameProviderBench\:\:\$tester is unused\.$#' - identifier: property.unused - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Benchmark/CodeAction/ImportNameProviderBench.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\IntegrationTestCase\:\:container\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/IntegrationTestCase.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/IntegrationTestCase.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php - - - - message: '#^Instanceof between Phpactor\\LanguageServer\\Test\\LanguageServerTester and Phpactor\\LanguageServer\\Test\\LanguageServerTester will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php - - - - message: '#^Offset ''diagnostics'' might not exist on array\\|null\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php - - - - message: '#^Parameter \#1 \$response of method Phpactor\\LanguageServer\\Test\\LanguageServerTester\:\:assertSuccess\(\) expects Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage, Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php - - - - message: '#^Parameter \#2 \$haystack of static method PHPUnit\\Framework\\Assert\:\:assertCount\(\) expects Countable\|iterable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/CreateClassProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\ExtractConstantProviderTest\:\:testProvideActions\(\) has parameter \$expectedValue with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractConstantProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\ExtractExpressionProviderTest\:\:testProvideActions\(\) has parameter \$expectedValue with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractExpressionProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\ExtractMethodProviderTest\:\:testProvideActions\(\) has parameter \$expectedValue with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractMethodProviderTest.php - - - - message: '#^Property Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\ExtractMethodProviderTest\:\:\$extractMethod with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ExtractMethodProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\GenerateMethodProviderTest\:\:provideActionsTestData\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\GenerateMethodProviderTest\:\:provideDiagnosticsTestData\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\GenerateMethodProviderTest\:\:testDiagnostics\(\) has parameter \$expectedDiagnostics with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\GenerateMethodProviderTest\:\:testDiagnostics\(\) has parameter \$missingMethods with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\GenerateMethodProviderTest\:\:testProvideActions\(\) has parameter \$expectedActions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\GenerateMethodProviderTest\:\:testProvideActions\(\) has parameter \$missingMethods with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/GenerateMethodProviderTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ImportNameProviderTest.php - - - - message: '#^Cannot access property \$title on mixed\.$#' - identifier: property.nonObject - count: 3 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ImportNameProviderTest.php - - - - message: '#^Instanceof between Phpactor\\LanguageServer\\Test\\LanguageServerTester and Phpactor\\LanguageServer\\Test\\LanguageServerTester will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ImportNameProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\PropertyAccessGeneratorProviderTest\:\:testProvideActions\(\) has parameter \$expectedActions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/PropertyAccessGeneratorProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\CodeAction\\ReplaceQualifierWithImportProviderTest\:\:testProvideActions\(\) has parameter \$expectedValue with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/CodeAction/ReplaceQualifierWithImportProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\LspCommand\\TestFileToClass\:\:fileToClassCandidates\(\) should return Phpactor\\ClassFileConverter\\Domain\\ClassNameCandidates but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/CreateClassCommandTest.php - - - - message: '#^Cannot access property \$params on mixed\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot call method executeCommand\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot call method filterByMethod\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot call method resolveLastResponse\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot call method responseWatcher\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot call method shiftNotification\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot call method shiftRequest\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot call method transmitter\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot call method workspace\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\LspCommand\\ExtractConstantCommandTest\:\:createTester\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\LspCommand\\ExtractConstantCommandTest\:\:provideExceptions\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Parameter \#1 \$changes of class Phpactor\\LanguageServerProtocol\\WorkspaceEdit constructor expects array\{\}\|null, array\{''file\:///file\.php''\: array\\} given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractConstantCommandTest.php - - - - message: '#^Cannot access property \$params on mixed\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot call method executeCommand\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot call method filterByMethod\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot call method resolveLastResponse\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot call method responseWatcher\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot call method shiftNotification\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot call method shiftRequest\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot call method transmitter\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot call method workspace\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\LspCommand\\ExtractExpressionCommandTest\:\:createTester\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Parameter \#1 \$changes of class Phpactor\\LanguageServerProtocol\\WorkspaceEdit constructor expects array\{\}\|null, array\{''file\:///file\.php''\: array\\} given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractExpressionCommandTest.php - - - - message: '#^Cannot access property \$params on mixed\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Cannot call method executeCommand\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Cannot call method filterByMethod\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Cannot call method resolveLastResponse\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Cannot call method responseWatcher\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Cannot call method shiftNotification\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Cannot call method shiftRequest\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Cannot call method transmitter\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Cannot call method workspace\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\LspCommand\\ExtractMethodCommandTest\:\:createTester\(\) has parameter \$extractMethod with generic class Prophecy\\Prophecy\\ObjectProphecy but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\LspCommand\\ExtractMethodCommandTest\:\:createTester\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Parameter \#3 \$extractMethod of class Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\ExtractMethodCommand constructor expects Phpactor\\CodeTransform\\Domain\\Refactor\\ExtractMethod, object given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ExtractMethodCommandTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\LspCommand\\GenerateDecoratorCommandTest\:\:createTester\(\) has parameter \$generateAccessors with generic class Prophecy\\Prophecy\\ObjectProphecy but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateDecoratorCommandTest.php - - - - message: '#^Parameter \#1 \$changes of class Phpactor\\LanguageServerProtocol\\WorkspaceEdit constructor expects array\{\}\|null, array\{''file\:///file\.php''\: array\\} given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateDecoratorCommandTest.php - - - - message: '#^Parameter \#3 \$generateDecorator of class Phpactor\\Extension\\LanguageServerCodeTransform\\LspCommand\\GenerateDecoratorCommand constructor expects Phpactor\\CodeTransform\\Domain\\Refactor\\GenerateDecorator, object given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/GenerateDecoratorCommandTest.php - - - - message: '#^Cannot access property \$params on Phpactor\\LanguageServer\\Core\\Rpc\\NotificationMessage\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportAllUnresolvedNamesCommandTest.php - - - - message: '#^Offset ''message'' might not exist on array\\|null\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportAllUnresolvedNamesCommandTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\Unit\\LspCommand\\ImportNameCommandTest\:\:assertWorkspaceResponse\(\) has parameter \$promise with generic interface Amp\\Promise but does not specify its types\: TValue$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportNameCommandTest.php - - - - message: '#^Offset ''message'' might not exist on array\\|null\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportNameCommandTest.php - - - - message: '#^Unable to resolve the template type T in call to function Amp\\Promise\\wait$#' - identifier: argument.templateType - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/ImportNameCommandTest.php - - - - message: '#^Parameter \#1 \$changes of class Phpactor\\LanguageServerProtocol\\WorkspaceEdit constructor expects array\{\}\|null, array\{''file\:///file\.php''\: array\\} given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/PropertyAccessGeneratorCommandTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with Phpactor\\CodeTransform\\Domain\\SourceCode will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCodeTransform/Tests/Unit/LspCommand/TransformCommandTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php - - - - message: '#^Cannot call method alias\(\) on Phpactor\\CodeTransform\\Domain\\Refactor\\ImportClass\\NameImport\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php - - - - message: '#^Instanceof between Phpactor\\Completion\\Core\\Suggestion and Phpactor\\Completion\\Core\\Suggestion will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php - - - - message: '#^Parameter \#2 \$replace of function str_replace expects array\\|string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php - - - - message: '#^Parameter \#3 \$type of callable Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameImporter expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCompletion\\Handler\\SignatureHelpHandler\:\:signatureHelp\(\) return type with generic interface Amp\\Promise does not specify its types\: TValue$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/LanguageServerCompletion/Handler/SignatureHelpHandler.php - - - - message: '#^Binary operation "\." between mixed and ''/\.phpactor'' results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Extension/TestExtension.php - - - - message: '#^Call to an undefined method Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClassLike\:\:cases\(\)\.$#' - identifier: method.notFound - count: 2 - path: lib/Extension/LanguageServerCompletion/Tests/Integration/MarkdownObjectRendererTest.php - - - - message: '#^Call to an undefined method Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClassLike\:\:properties\(\)\.$#' - identifier: method.notFound - count: 5 - path: lib/Extension/LanguageServerCompletion/Tests/Integration/MarkdownObjectRendererTest.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 7 - path: lib/Extension/LanguageServerCompletion/Tests/Integration/MarkdownObjectRendererTest.php - - - - message: '#^Parameter \#1 \$object of method Phpactor\\ObjectRenderer\\Model\\ObjectRenderer\:\:render\(\) expects object, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Integration/MarkdownObjectRendererTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Integration/MarkdownObjectRendererTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServer\\\\LanguageServerBuilder'' and Phpactor\\LanguageServer\\LanguageServerBuilder will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/IntegrationTestCase.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Cannot access property \$detail on mixed\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Cannot access property \$documentation on mixed\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Cannot access property \$isIncomplete on mixed\.$#' - identifier: property.nonObject - count: 7 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Cannot access property \$items on mixed\.$#' - identifier: property.nonObject - count: 7 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 29 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Cannot access property \$value on mixed\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Instanceof between Phpactor\\LanguageServerProtocol\\CompletionList and Phpactor\\LanguageServerProtocol\\CompletionList will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:completionItem\(\) has parameter \$data with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:create\(\) has parameter \$aliases with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:create\(\) has parameter \$importNameTextEdits with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:create\(\) has parameter \$suggestions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:createCompletor\(\) has parameter \$suggestions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:createNameImporter\(\) has parameter \$importNameTextEdits with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Parameter \#1 \$promises of function Amp\\Promise\\all expects array\\|React\\Promise\\PromiseInterface\>, array\\> given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Parameter \#1 \$suggestions of class Phpactor\\Completion\\Core\\Completor@anonymous/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest\.php\:561 constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Parameter \#1 \$suggestions of method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:createNameImporter\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Parameter \#2 \$aliases of method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:createNameImporter\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Parameter \#2 \$items of method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\CompletionHandlerTest\:\:assertCompletion\(\) expects array\, mixed given\.$#' - identifier: argument.type - count: 6 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Parameter \#2 \$textEdits of static method Phpactor\\Extension\\LanguageServerCodeTransform\\Model\\NameImport\\NameImporterResult\:\:createResult\(\) expects array\\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/CompletionHandlerTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/HoverHandlerTest.php - - - - message: '#^Parameter \#1 \$response of method Phpactor\\LanguageServer\\Test\\LanguageServerTester\:\:assertSuccess\(\) expects Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage, Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/HoverHandlerTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/SignatureHelpHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerCompletion\\Tests\\Unit\\Handler\\SignatureHelpHandlerTest\:\:create\(\) has parameter \$suggestions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Handler/SignatureHelpHandlerTest.php - - - - message: '#^Parameter \#1 \$suggestionType of static method Phpactor\\Extension\\LanguageServerCompletion\\Util\\PhpactorToLspCompletionType\:\:fromPhpactorType\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspCompletionTypeTest.php - - - - message: '#^Parameter \#2 \$message of method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspCompletionTypeTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServerProtocol\\\\ParameterInformation'' and Phpactor\\LanguageServerProtocol\\ParameterInformation will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspSignatureTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServerProtocol\\\\SignatureHelp'' and Phpactor\\LanguageServerProtocol\\SignatureHelp will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspSignatureTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServerProtocol\\\\SignatureInformation'' and Phpactor\\LanguageServerProtocol\\SignatureInformation will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspSignatureTest.php - - - - message: '#^Offset 0 might not exist on array\\|null\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspSignatureTest.php - - - - message: '#^Parameter \#2 \$haystack of method PHPUnit\\Framework\\Assert\:\:assertCount\(\) expects Countable\|iterable, array\\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Tests/Unit/Util/PhpactorToLspSignatureTest.php - - - - message: '#^Binary operation "\." between ''\$'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/LanguageServerCompletion/Util/PhpactorToLspSignature.php - - - - message: '#^Cannot call method documentation\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCompletion/Util/PhpactorToLspSignature.php - - - - message: '#^Cannot call method label\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerCompletion/Util/PhpactorToLspSignature.php - - - - message: '#^Parameter \#2 \$value of class Phpactor\\LanguageServerProtocol\\MarkupContent constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerCompletion/Util/PhpactorToLspSignature.php - - - - message: '#^Cannot access property \$title on mixed\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerConfiguration/Listener/AutoConfigListener.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerDiagnostics/Model/PhpLinter.php - - - - message: '#^Parameter \$message of class Phpactor\\LanguageServerProtocol\\Diagnostic constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerDiagnostics/Model/PhpLinter.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerDiagnostics\\Provider\\PhpLintDiagnosticProvider\:\:provideDiagnostics\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerDiagnostics/Provider/PhpLintDiagnosticProvider.php - - - - message: '#^Parameter \#1 \$array of static method Phpactor\\LanguageServerProtocol\\Range\:\:fromArray\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerEvaluatableExpression/Protocol/EvaluatableExpression.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerHover\\Handler\\HoverHandler\:\:hover\(\) should return Amp\\Promise\ but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerHover/Handler/HoverHandler.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerHover\\Handler\\HoverHandler\:\:renderDeclaredConstant\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: lib/Extension/LanguageServerHover/Handler/HoverHandler.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerHover\\Handler\\HoverHandler constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerHover/LanguageServerHoverExtension.php - - - - message: '#^Parameter \#2 \$reflector of class Phpactor\\Extension\\LanguageServerHover\\Handler\\HoverHandler constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerHover/LanguageServerHoverExtension.php - - - - message: '#^Parameter \#3 \$renderer of class Phpactor\\Extension\\LanguageServerHover\\Handler\\HoverHandler constructor expects Phpactor\\ObjectRenderer\\Model\\ObjectRenderer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerHover/LanguageServerHoverExtension.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerHover\\Renderer\\MemberDocblock\:\:ancestorsAndSelf\(\) should return array\ but returns array\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerHover/Renderer/MemberDocblock.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerHover\\Renderer\\MemberDocblock\:\:buildAncestors\(\) has parameter \$ancestors with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerHover/Renderer/MemberDocblock.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerHover\\Renderer\\MemberDocblock\:\:buildAncestors\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerHover/Renderer/MemberDocblock.php - - - - message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(mixed\)\: bool\)\|null, Closure\(Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMember\)\: bool given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerHover/Renderer/MemberDocblock.php - - - - message: '#^Parameter \#1 \$object of function get_class expects object, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerHover/Twig/TwigFunctions.php - - - - message: '#^Cannot call method path\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerIndexer\\Handler\\IndexerHandler\:\:reindex\(\) should return Amp\\Promise\ but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php - - - - message: '#^Parameter \#1 \$process of method Phpactor\\Extension\\LanguageServerIndexer\\Handler\\IndexerHandler\:\:watch\(\) expects Phpactor\\AmpFsWatch\\WatcherProcess, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php - - - - message: '#^Parameter \#1 \$uri of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:fromUri\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerIndexer\\Handler\\WorkspaceSymbolHandler\:\:symbol\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerIndexer/Handler/WorkspaceSymbolHandler.php - - - - message: '#^Parameter \#3 \$limit of class Phpactor\\Extension\\LanguageServerIndexer\\Model\\WorkspaceSymbolProvider constructor expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerIndexer/LanguageServerIndexerExtension.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerIndexer/Model/WorkspaceSymbolProvider.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record and Phpactor\\Indexer\\Model\\Record will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerIndexer/Model/WorkspaceSymbolProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerIndexer\\Model\\WorkspaceSymbolProvider\:\:provideFor\(\) should return Amp\\Promise\\> but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerIndexer/Model/WorkspaceSymbolProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerIndexer\\Tests\\IntegrationTestCase\:\:container\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerIndexer/Tests/IntegrationTestCase.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerIndexer/Tests/IntegrationTestCase.php - - - - message: '#^Cannot access offset ''message'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/IndexerHandlerTest.php - - - - message: '#^Cannot access offset ''value'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/IndexerHandlerTest.php - - - - message: '#^Cannot access property \$id on Phpactor\\LanguageServer\\Core\\Rpc\\RequestMessage\|null\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/IndexerHandlerTest.php - - - - message: '#^Cannot access property \$params on Phpactor\\LanguageServer\\Core\\Rpc\\Message\|null\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/IndexerHandlerTest.php - - - - message: '#^Parameter \#2 \$haystack of static method PHPUnit\\Framework\\Assert\:\:assertStringContainsString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/IndexerHandlerTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/Model/WorkspaceSymbolProviderTest.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Indexer and Phpactor\\Indexer\\Model\\Indexer will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/Model/WorkspaceSymbolProviderTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerIndexer\\Tests\\Unit\\Model\\WorkspaceSymbolProviderTest\:\:testProvide\(\) has parameter \$workspace with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/Model/WorkspaceSymbolProviderTest.php - - - - message: '#^Parameter \#2 \$contents of method Phpactor\\TestUtils\\Workspace\:\:put\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerIndexer/Tests/Unit/Model/WorkspaceSymbolProviderTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerIndexer/Watcher/LanguageServerWatcher.php - - - - message: '#^Instanceof between Phpactor\\LanguageServerProtocol\\FileEvent and Phpactor\\LanguageServerProtocol\\FileEvent will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerIndexer/Watcher/LanguageServerWatcher.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerIndexer\\Watcher\\LanguageServerWatcher\:\:getListenersForEvent\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerIndexer/Watcher/LanguageServerWatcher.php - - - - message: '#^Property Phpactor\\Extension\\LanguageServerIndexer\\Watcher\\LanguageServerWatcher\:\:\$queue \(array\\) does not accept array\.$#' - identifier: assign.propertyType - count: 1 - path: lib/Extension/LanguageServerIndexer/Watcher/LanguageServerWatcher.php - - - - message: '#^Using nullsafe property access "\?\-\>didChangeWatchedFiles" on left side of \?\? is unnecessary\. Use \-\> instead\.$#' - identifier: nullsafe.neverNull - count: 1 - path: lib/Extension/LanguageServerIndexer/Watcher/LanguageServerWatcher.php - - - - message: '#^Cannot call method getEndPosition\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerInlineValue/Handler/InlineValueHandler.php - - - - message: '#^Cannot call method getStartPosition\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerInlineValue/Handler/InlineValueHandler.php - - - - message: '#^Parameter \#1 \$diffText of method Phpactor\\Diff\\DiffToTextEditsConverter\:\:toTextEdits\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Formatter/PhpCsFixerFormatter.php - - - - message: '#^Parameter \#3 \$workspace of class Phpactor\\Extension\\LanguageServerPhpCsFixer\\LspCommand\\FormatCommand constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/LanguageServerPhpCsFixerExtension.php - - - - message: '#^Parameter \#1 \$diffText of method Phpactor\\Diff\\DiffToTextEditsConverter\:\:toTextEdits\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/LspCommand/FormatCommand.php - - - - message: '#^Parameter \#1 \$exitCode of class Phpactor\\Extension\\LanguageServerPhpCsFixer\\Exception\\PhpCsFixerError constructor expects int, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php - - - - message: '#^Parameter \#1 \$version of method Phpactor\\Extension\\LanguageServerPhpCsFixer\\Model\\PhpCsFixerProcess\:\:resolveEnv\(\) expects Phpactor\\VersionResolver\\SemVersion\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php - - - - message: '#^Parameter \#1 \$version of method Phpactor\\Extension\\LanguageServerPhpCsFixer\\Model\\PhpCsFixerProcess\:\:resolveExtraArgs\(\) expects Phpactor\\VersionResolver\\SemVersion\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php - - - - message: '#^Parameter \#3 \$stderr of class Phpactor\\Extension\\LanguageServerPhpCsFixer\\Exception\\PhpCsFixerError constructor expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php - - - - message: '#^Parameter \#4 \$stdout of class Phpactor\\Extension\\LanguageServerPhpCsFixer\\Exception\\PhpCsFixerError constructor expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerPhpCsFixer/Model/PhpCsFixerProcess.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Cannot access offset 0 on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Cannot access property \$appliedFixers on mixed\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Cannot access property \$files on mixed\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Parameter \#1 \$json of function json_decode expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Parameter \#1 \$rule of method Phpactor\\Extension\\LanguageServerPhpCsFixer\\Provider\\PhpCsFixerDiagnosticsProvider\:\:createRuleDiagnostics\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Parameter \#1 \$string of method SebastianBergmann\\Diff\\Parser\:\:parse\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Parameter \#2 \$options of method Phpactor\\Extension\\LanguageServerPhpCsFixer\\Model\\PhpCsFixerProcess\:\:fix\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Parameter \#3 \$subject of function preg_replace expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Provider/PhpCsFixerDiagnosticsProvider.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertIsString\(\) with string will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php - - - - message: '#^Cannot access offset ''files'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php - - - - message: '#^Cannot call method getStdout\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php - - - - message: '#^Parameter \#1 \$source of function Amp\\ByteStream\\buffer expects Amp\\ByteStream\\InputStream, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php - - - - message: '#^Parameter \#2 \$haystack of static method PHPUnit\\Framework\\Assert\:\:assertStringContainsString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Model/PhpCsFixerProcessTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServerProtocol\\\\CodeAction'' and Phpactor\\LanguageServerProtocol\\CodeAction will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 2 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Provider/PhpCsFixerDiagnosticsProviderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServerProtocol\\\\Diagnostic'' and Phpactor\\LanguageServerProtocol\\Diagnostic will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Provider/PhpCsFixerDiagnosticsProviderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\ will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 2 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Provider/PhpCsFixerDiagnosticsProviderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\ will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 2 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/Provider/PhpCsFixerDiagnosticsProviderTest.php - - - - message: '#^Cannot call method __toString\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/VersionResolver/PhpCsFixerVersionResolverTest.php - - - - message: '#^Parameter \#2 \$string of static method PHPUnit\\Framework\\Assert\:\:assertMatchesRegularExpression\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/Tests/VersionResolver/PhpCsFixerVersionResolverTest.php - - - - message: '#^Cannot call method getStdout\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/VersionResolver/PhpCsFixerVersionResolver.php - - - - message: '#^Cannot call method join\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/VersionResolver/PhpCsFixerVersionResolver.php - - - - message: '#^Offset 1 might not exist on array\{0\: string, 1\?\: non\-falsy\-string\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/VersionResolver/PhpCsFixerVersionResolver.php - - - - message: '#^Parameter \#1 \$source of function Amp\\ByteStream\\buffer expects Amp\\ByteStream\\InputStream, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/VersionResolver/PhpCsFixerVersionResolver.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpCsFixer/VersionResolver/PhpCsFixerVersionResolver.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Cannot access offset ''identifier'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Cannot access offset ''line'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Cannot access offset ''message'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Cannot access offset ''messages'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Cannot access offset ''tip'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerPhpstan\\Model\\DiagnosticsParser\:\:decodeJson\(\) should return array\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Parameter \#1 \$message of method Phpactor\\Extension\\LanguageServerPhpstan\\Model\\DiagnosticsParser\:\:resolveCodeDescription\(\) expects array\{tip\?\: string\}, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Parameter \$code of class Phpactor\\LanguageServerProtocol\\Diagnostic constructor expects int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Parameter \$message of class Phpactor\\LanguageServerProtocol\\Diagnostic constructor expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerPhpstan/Model/DiagnosticsParser.php - - - - message: '#^Parameter \#1 \$jsonString of method Phpactor\\Extension\\LanguageServerPhpstan\\Model\\DiagnosticsParser\:\:parse\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/PhpstanProcess.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerPhpstan/Model/PhpstanProcess.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPhpstan/Model/PhpstanProcess.php - - - - message: '#^PHPDoc tag @return with type Generator\ is incompatible with native type void\.$#' - identifier: return.phpDocType - count: 1 - path: lib/Extension/LanguageServerPhpstan/Tests/Provider/PhpstanDiagnosticProviderTest.php - - - - message: '#^Parameter \#1 \$jsonString of method Phpactor\\Extension\\LanguageServerPsalm\\Model\\DiagnosticsParser\:\:parse\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPsalm/Model/PsalmProcess.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPsalm/Model/PsalmProcess.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPsalm/Model/PsalmProcess.php - - - - message: '#^Parameter \#1 of closure expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerPsalm/Tests/Model/PsalmProcessTest.php - - - - message: '#^Parameter \#2 \$diagnostics of static method Phpactor\\Extension\\LanguageServerPsalm\\Tests\\Model\\PsalmProcessTest\:\:assertDiagnostics\(\) expects list\, array given\.$#' - identifier: argument.type - count: 3 - path: lib/Extension/LanguageServerPsalm/Tests/Model/PsalmProcessTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\GotoImplementationHandler\:\:gotoImplementation\(\) return type with generic interface Amp\\Promise does not specify its types\: TValue$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Handler/GotoImplementationHandler.php - - - - message: '#^Property Phpactor\\LanguageServerProtocol\\TextDocumentItem\:\:\$languageId \(string\) on left side of \?\? is not nullable\.$#' - identifier: nullCoalesce.property - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Handler/GotoImplementationHandler.php - - - - message: '#^Cannot call method toArray\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Handler/HighlightHandler.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Adapter\\Indexer\\WorkspaceUpdateReferenceFinder constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\GotoDefinitionHandler constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\GotoImplementationHandler constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\ReferencesHandler constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\TypeDefinitionHandler constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Parameter \#2 \$definitionLocator of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\GotoDefinitionHandler constructor expects Phpactor\\ReferenceFinder\\DefinitionLocator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Parameter \#2 \$finder of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\GotoImplementationHandler constructor expects Phpactor\\ReferenceFinder\\ClassImplementationFinder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Parameter \#2 \$typeLocator of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\TypeDefinitionHandler constructor expects Phpactor\\ReferenceFinder\\TypeLocator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Parameter \#3 \$definitionLocator of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Handler\\ReferencesHandler constructor expects Phpactor\\ReferenceFinder\\DefinitionLocator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/LanguageServerReferenceFinderExtension.php - - - - message: '#^Access to an undefined property Microsoft\\PhpParser\\MissingToken\|Microsoft\\PhpParser\\Node\\QualifiedName\:\:\$nameParts\.$#' - identifier: property.notFound - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Cannot call method getEndPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Cannot call method getEndPosition\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Cannot call method getStartPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Cannot call method getStartPosition\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Cannot cast Microsoft\\PhpParser\\MissingToken\|Microsoft\\PhpParser\\Node\\QualifiedName to string\.$#' - identifier: cast.string - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 6 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Parameter \#1 \$start of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Model\\Highlight constructor expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Parameter \#2 \$end of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Model\\Highlight constructor expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlighter.php - - - - message: '#^Parameter \#1 \.\.\.\$highlights of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Model\\Highlights constructor expects Phpactor\\LanguageServerProtocol\\DocumentHighlight, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Model/Highlights.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/GotoDefinitionHandlerTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/GotoImplementationHandlerTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 3 - path: lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/ReferencesHandlerTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/LanguageServerReferenceFinder/Tests/Unit/Handler/TypeDefinitionHandlerTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServer\\\\LanguageServerBuilder'' and Phpactor\\LanguageServer\\LanguageServerBuilder will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Tests/Unit/LanguageServerReferenceFinderExtensionTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 3 - path: lib/Extension/LanguageServerReferenceFinder/Tests/Unit/LanguageServerReferenceFinderExtensionTest.php - - - - message: '#^Parameter \#1 \$response of method Phpactor\\LanguageServer\\Test\\LanguageServerTester\:\:assertSuccess\(\) expects Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage, Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerReferenceFinder/Tests/Unit/LanguageServerReferenceFinderExtensionTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerRename/Handler/FileRenameHandler.php - - - - message: '#^Instanceof between Phpactor\\LanguageServerProtocol\\FileRename and Phpactor\\LanguageServerProtocol\\FileRename will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerRename/Handler/FileRenameHandler.php - - - - message: '#^Parameter \#1 \$map of method Phpactor\\Rename\\Model\\LocatedTextEditsMap\:\:merge\(\) expects Phpactor\\Rename\\Model\\LocatedTextEditsMap, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Handler/FileRenameHandler.php - - - - message: '#^Property Phpactor\\LanguageServerProtocol\\ServerCapabilities\:\:\$workspace \(array\{workspaceFolders\: Phpactor\\LanguageServerProtocol\\WorkspaceFoldersServerCapabilities, fileOperations\: Phpactor\\LanguageServerProtocol\\FileOperationOptions\}\|null\) does not accept array\{workspaceFolders\?\: Phpactor\\LanguageServerProtocol\\WorkspaceFoldersServerCapabilities, fileOperations\: Phpactor\\LanguageServerProtocol\\FileOperationOptions\}\.$#' - identifier: assign.propertyType - count: 1 - path: lib/Extension/LanguageServerRename/Handler/FileRenameHandler.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerRename\\Handler\\RenameHandler\:\:prepareRename\(\) should return Amp\\Promise\ but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerRename/Handler/RenameHandler.php - - - - message: '#^Parameter \#2 \$renameResult of method Phpactor\\Extension\\LanguageServerRename\\Handler\\RenameHandler\:\:resultToWorkspaceEdit\(\) expects Phpactor\\Rename\\Model\\RenameResult\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Handler/RenameHandler.php - - - - message: '#^Parameter \#1 \$renamers of class Phpactor\\Rename\\Model\\Renamer\\ChainRenamer constructor expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/LanguageServerRenameExtension.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerRename\\Util\\LocatedTextEditConverter constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/LanguageServerRenameExtension.php - - - - message: '#^Parameter \#1 \$classToFile of class Phpactor\\Rename\\Adapter\\ClassToFile\\ClassToFileNameToUriConverter constructor expects Phpactor\\ClassFileConverter\\Domain\\ClassToFile, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/LanguageServerRenameWorseExtension.php - - - - message: '#^Parameter \#1 \$fileToClass of class Phpactor\\Rename\\Adapter\\ClassToFile\\ClassToFileUriToNameConverter constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/LanguageServerRenameWorseExtension.php - - - - message: '#^Parameter \#1 \$locator of class Phpactor\\ReferenceFinder\\DefinitionAndReferenceFinder constructor expects Phpactor\\ReferenceFinder\\DefinitionLocator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/LanguageServerRenameWorseExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Rename\\Adapter\\WorseReflection\\WorseNameToUriConverter constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/LanguageServerRenameWorseExtension.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerReferenceFinder\\Adapter\\Indexer\\WorkspaceUpdateReferenceFinder constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/LanguageServerRenameWorseExtension.php - - - - message: '#^Parameter \#1 \$range of class Phpactor\\Rename\\Model\\Renamer\\InMemoryRenamer constructor expects Phpactor\\TextDocument\\ByteOffsetRange\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Extension/TestExtension.php - - - - message: '#^Parameter \#2 \$results of class Phpactor\\Rename\\Model\\Renamer\\InMemoryRenamer constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Extension/TestExtension.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerRename\\Tests\\IntegrationTestCase\:\:container\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerRename/Tests/IntegrationTestCase.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/IntegrationTestCase.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php - - - - message: '#^Instanceof between Phpactor\\LanguageServerProtocol\\WorkspaceEdit and Phpactor\\LanguageServerProtocol\\WorkspaceEdit will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerRename\\Tests\\Unit\\Handler\\FileRenameHandlerTest\:\:createHandler\(\) has parameter \$workspaceEdits with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerRename\\Tests\\Unit\\Handler\\FileRenameHandlerTest\:\:createServer\(\) has parameter \$workspaceEdits with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php - - - - message: '#^Offset ''fileOperations'' might not exist on array\{workspaceFolders\: Phpactor\\LanguageServerProtocol\\WorkspaceFoldersServerCapabilities, fileOperations\: Phpactor\\LanguageServerProtocol\\FileOperationOptions\}\|null\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php - - - - message: '#^Parameter \#1 \$map of class Phpactor\\Rename\\Model\\LocatedTextEditsMap constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php - - - - message: '#^Parameter \#2 \$haystack of static method PHPUnit\\Framework\\Assert\:\:assertCount\(\) expects Countable\|iterable, array\\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/FileRenameHandlerTest.php - - - - message: '#^Cannot access property \$prepareProvider on bool\|Phpactor\\LanguageServerProtocol\\RenameOptions\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php - - - - message: '#^Cannot access property \$result on Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null\.$#' - identifier: property.nonObject - count: 5 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerRename\\Tests\\Unit\\Handler\\RenameHandlerTest\:\:bootContainerWithRangeAndResults\(\) has parameter \$results with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php - - - - message: '#^Offset 0 might not exist on array\\|null\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php - - - - message: '#^Parameter \#1 \$response of method Phpactor\\LanguageServer\\Test\\LanguageServerTester\:\:assertSuccess\(\) expects Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage, Phpactor\\LanguageServer\\Core\\Rpc\\ResponseMessage\|null given\.$#' - identifier: argument.type - count: 3 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php - - - - message: '#^Property Phpactor\\Extension\\LanguageServerRename\\Tests\\Unit\\Handler\\RenameHandlerTest\:\:\$renamer is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Unit/Handler/RenameHandlerTest.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractor\:\:parse\(\) should return Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractorResult but returns \$this\(Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractor\)\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php - - - - message: '#^Parameter \#2 \$offsets of class Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractorResult constructor expects array\\>, array\\> given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php - - - - message: '#^Parameter \#3 \$ranges of class Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractorResult constructor expects array\\>, array\\> given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 4 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php - - - - message: '#^Property Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractor\:\:\$points type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php - - - - message: '#^Property Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractor\:\:\$rangeCloseMarkers type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php - - - - message: '#^Property Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractor\:\:\$rangeOpenMarkers type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractor.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractorResult\:\:offsets\(\) should return array\ but returns array\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractorResult.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerRename\\Tests\\Util\\OffsetExtractorResult\:\:ranges\(\) should return array\ but returns array\.$#' - identifier: return.type - count: 1 - path: lib/Extension/LanguageServerRename/Tests/Util/OffsetExtractorResult.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerSelectionRange\\Handler\\SelectionRangeHandler constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerSelectionRange/LanguageServerSelectionRangeExtension.php - - - - message: '#^Parameter \#1 \$node of method Phpactor\\Extension\\LanguageServerSelectionRange\\Model\\RangeProvider\:\:buildRange\(\) expects Microsoft\\PhpParser\\Node, Microsoft\\PhpParser\\Node\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerSelectionRange/Model/RangeProvider.php - - - - message: '#^Cannot call method getEndPosition\(\) on Microsoft\\PhpParser\\Node\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php - - - - message: '#^Cannot call method getEndPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php - - - - message: '#^Cannot call method getStartPosition\(\) on Microsoft\\PhpParser\\Node\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php - - - - message: '#^Cannot call method getStartPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php - - - - message: '#^Cannot call method getText\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 2 - path: lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php - - - - message: '#^Parameter \#1 \$node of method Phpactor\\Extension\\LanguageServerSymbolProvider\\Adapter\\TolerantDocumentSymbolProvider\:\:buildNode\(\) expects Microsoft\\PhpParser\\Node, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerSymbolProvider\\Handler\\DocumentSymbolProviderHandler\:\:documentSymbols\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/Handler/DocumentSymbolProviderHandler.php - - - - message: '#^Parameter \#1 \$workspace of class Phpactor\\Extension\\LanguageServerSymbolProvider\\Handler\\DocumentSymbolProviderHandler constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/LanguageServerSymbolProviderExtension.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with array\ and ''Missing document…'' will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php - - - - message: '#^Instanceof between Phpactor\\LanguageServerProtocol\\DocumentSymbol and Phpactor\\LanguageServerProtocol\\DocumentSymbol will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php - - - - message: '#^PHPDoc tag @throws with type Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php - - - - message: '#^PHPDoc tag @throws with type PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php - - - - message: '#^Strict comparison using \!\=\= between null and array\ will always evaluate to true\.$#' - identifier: notIdentical.alwaysTrue - count: 1 - path: lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/LanguageServerWorseReflection/DiagnosticProvider/WorseDiagnosticProvider.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/Extension/LanguageServerWorseReflection/InlayHint/InlayHintWalker.php - - - - message: '#^Parameter \#1 \$index of method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ReflectionParameterCollection\:\:at\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/LanguageServerWorseReflection/InlayHint/InlayHintWalker.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerWorseReflection\\Tests\\Benchmark\\WorkspaceIndexBench\:\:benchUpdate\(\) has parameter \$params with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Tests/Benchmark/WorkspaceIndexBench.php - - - - message: '#^Parameter \#2 \$newText of method Phpactor\\LanguageServer\\Test\\LanguageServerTester\\TextDocumentTester\:\:update\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Tests/Benchmark/WorkspaceIndexBench.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServerProtocol\\\\Diagnostic'' and Phpactor\\LanguageServerProtocol\\Diagnostic will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Tests/Unit/DiagnosticProvider/WorseDiagnosticProviderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\Extension\\\\LanguageServerWorseReflection\\\\SourceLocator\\\\WorkspaceSourceLocator'' and Phpactor\\Extension\\LanguageServerWorseReflection\\SourceLocator\\WorkspaceSourceLocator will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Tests/Unit/LanguageServerWorseReflectionExtensionTest.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerWorseReflection\\Workspace\\WorkspaceIndex\:\:updateNames\(\) has parameter \$currentNames with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerWorseReflection\\Workspace\\WorkspaceIndex\:\:updateNames\(\) has parameter \$newNames with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php - - - - message: '#^Property Phpactor\\Extension\\LanguageServerWorseReflection\\Workspace\\WorkspaceIndex\:\:\$documentToNameMap \(array\\>\) does not accept array\\.$#' - identifier: assign.propertyType - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php - - - - message: '#^Method Phpactor\\Extension\\LanguageServerWorseReflection\\Workspace\\WorkspaceIndexListener\:\:getListenersForEvent\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndexListener.php - - - - message: '#^Property Phpactor\\LanguageServerProtocol\\TextDocumentItem\:\:\$text \(string\) on left side of \?\? is not nullable\.$#' - identifier: nullCoalesce.property - count: 1 - path: lib/Extension/LanguageServerWorseReflection/Workspace/WorkspaceIndexListener.php - - - - message: '#^Method Phpactor\\Extension\\Logger\\Formatter\\FormatterRegistry\:\:__construct\(\) has parameter \$serviceMap with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Logger/Formatter/FormatterRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Logger\\Formatter\\FormatterRegistry\:\:get\(\) should return Monolog\\Formatter\\FormatterInterface but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Logger/Formatter/FormatterRegistry.php - - - - message: '#^Parameter \#1 \$id of method Psr\\Container\\ContainerInterface\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Logger/Formatter/FormatterRegistry.php - - - - message: '#^Parameter \#1 \$string of function strtoupper expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Logger/Formatter/PrettyFormatter.php - - - - message: '#^Parameter \#1 \$mainLogger of class Phpactor\\Extension\\Logger\\LoggerFactory constructor expects Psr\\Log\\LoggerInterface, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Logger/LoggingExtension.php - - - - message: '#^Parameter \#2 \$level of class Monolog\\Handler\\StreamHandler constructor expects 100\|200\|250\|300\|400\|500\|550\|600\|''ALERT''\|''alert''\|''CRITICAL''\|''critical''\|''DEBUG''\|''debug''\|''EMERGENCY''\|''emergency''\|''ERROR''\|''error''\|''INFO''\|''info''\|''NOTICE''\|''notice''\|''WARNING''\|''warning'', string given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Logger/LoggingExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/Logger/LoggingExtension.php - - - - message: '#^Method Phpactor\\Extension\\Logger\\Tests\\Unit\\Formatter\\PrettyFormatterTest\:\:provideFormat\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Logger/Tests/Unit/Formatter/PrettyFormatterTest.php - - - - message: '#^Method Phpactor\\Extension\\Logger\\Tests\\Unit\\Formatter\\PrettyFormatterTest\:\:testFormat\(\) has parameter \$record with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Logger/Tests/Unit/Formatter/PrettyFormatterTest.php - - - - message: '#^Parameter \#1 \$record of method Phpactor\\Extension\\Logger\\Formatter\\PrettyFormatter\:\:format\(\) expects array\{message\: string, context\: array\, level\: 100\|200\|250\|300\|400\|500\|550\|600, level_name\: ''ALERT''\|''CRITICAL''\|''DEBUG''\|''EMERGENCY''\|''ERROR''\|''INFO''\|''NOTICE''\|''WARNING'', channel\: string, datetime\: DateTimeImmutable, extra\: array\\}, array\{level_name\: ''info'', context\: array\{\}, message\: ''hello'', datetime\: DateTime\} given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Logger/Tests/Unit/Formatter/PrettyFormatterTest.php - - - - message: '#^Method Phpactor\\Extension\\Logger\\Tests\\Unit\\LoggingExtensionTest\:\:create\(\) has parameter \$options with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Logger/Tests/Unit/LoggingExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\Logger\\Tests\\Unit\\LoggingExtensionTest\:\:provideLoggingFormatters\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Logger/Tests/Unit/LoggingExtensionTest.php - - - - message: '#^Parameter \#1 \$json of function json_decode expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Logger/Tests/Unit/LoggingExtensionTest.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Logger/Tests/Unit/LoggingExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Application\\Navigator\:\:destinationsFor\(\) should return array\ but returns array\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Navigation/Application/Navigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Handler\\NavigateHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Handler\\NavigateHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:requireInput\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Extension\\Navigation\\Application\\Navigator\:\:canCreateNew\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Extension\\Navigation\\Application\\Navigator\:\:createNew\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Extension\\Navigation\\Application\\Navigator\:\:destinationsFor\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Parameter \#2 \$destinationName of method Phpactor\\Extension\\Navigation\\Application\\Navigator\:\:canCreateNew\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Parameter \#2 \$destinationName of method Phpactor\\Extension\\Navigation\\Application\\Navigator\:\:createNew\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/Navigation/Handler/NavigateHandler.php - - - - message: '#^Parameter \#1 \$navigator of class Phpactor\\Extension\\Navigation\\Application\\Navigator constructor expects Phpactor\\Extension\\Navigation\\Navigator\\Navigator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/NavigationExtension.php - - - - message: '#^Parameter \#1 \$navigator of class Phpactor\\Extension\\Navigation\\Handler\\NavigateHandler constructor expects Phpactor\\Extension\\Navigation\\Application\\Navigator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/NavigationExtension.php - - - - message: '#^Parameter \#1 \$navigators of class Phpactor\\Extension\\Navigation\\Navigator\\ChainNavigator constructor expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/NavigationExtension.php - - - - message: '#^Parameter \#1 \$pathFinder of class Phpactor\\Extension\\Navigation\\Navigator\\PathFinderNavigator constructor expects Phpactor\\PathFinder\\PathFinder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/NavigationExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\Navigation\\Navigator\\WorseReflectionNavigator constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/NavigationExtension.php - - - - message: '#^Parameter \#2 \$classNew of class Phpactor\\Extension\\Navigation\\Application\\Navigator constructor expects Phpactor\\Extension\\CodeTransformExtra\\Application\\ClassNew, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/NavigationExtension.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\ChainNavigator\:\:destinationsFor\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Navigation/Navigator/ChainNavigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\Navigator\:\:destinationsFor\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Navigation/Navigator/Navigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\PathFinderNavigator\:\:destinationsFor\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Navigation/Navigator/PathFinderNavigator.php - - - - message: '#^Cannot access offset non\-falsy\-string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\WorseReflectionNavigator\:\:destinationsFor\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\WorseReflectionNavigator\:\:destinationsFor\(\) should return array but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\WorseReflectionNavigator\:\:forReflectionClass\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\WorseReflectionNavigator\:\:forReflectionClass\(\) has parameter \$destinations with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\WorseReflectionNavigator\:\:forReflectionInterface\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Navigator\\WorseReflectionNavigator\:\:forReflectionInterface\(\) has parameter \$destinations with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php - - - - message: '#^Parameter \#1 \$destinations of method Phpactor\\Extension\\Navigation\\Navigator\\WorseReflectionNavigator\:\:forReflectionClass\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Navigation/Navigator/WorseReflectionNavigator.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: lib/Extension/Navigation/Tests/IntegrationTestCase.php - - - - message: '#^Method Phpactor\\Extension\\Navigation\\Tests\\IntegrationTestCase\:\:container\(\) should return Phpactor\\Container\\Container but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Navigation/Tests/IntegrationTestCase.php - - - - message: '#^Method Phpactor\\Extension\\ObjectRenderer\\ObjectRendererBuilder\:\:buildTwig\(\) should return Twig\\Environment but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/ObjectRenderer/ObjectRendererBuilder.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/ObjectRenderer/ObjectRendererExtension.php - - - - message: '#^Parameter \#1 \$paths of method Phpactor\\CodeBuilder\\Domain\\TemplatePathResolver\\PhpVersionPathResolver\:\:resolve\(\) expects list\, non\-empty\-array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ObjectRenderer/ObjectRendererExtension.php - - - - message: '#^Parameter \#1 \$phpVersion of class Phpactor\\CodeBuilder\\Domain\\TemplatePathResolver\\PhpVersionPathResolver constructor expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ObjectRenderer/ObjectRendererExtension.php - - - - message: '#^Parameter \#2 \$params of class Phpactor\\Extension\\OpenTelemetry\\Model\\PostContext constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/OpenTelemetry/Model/HookBootstrap.php - - - - message: '#^Parameter \#2 \$params of class Phpactor\\Extension\\OpenTelemetry\\Model\\PreContext constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/OpenTelemetry/Model/HookBootstrap.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/PHPUnit/CodeTransform/GenerateTestMethods.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Token and Microsoft\\PhpParser\\Token will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/PHPUnit/FrameWalker/AssertInstanceOfWalker.php - - - - message: '#^Parameter \#2 \$className of static method Phpactor\\WorseReflection\\Core\\TypeFactory\:\:reflectedClass\(\) expects Phpactor\\WorseReflection\\Core\\ClassName\|string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PHPUnit/FrameWalker/AssertInstanceOfWalker.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/Extension/PHPUnit/Tests/Unit/CodeTransform/GenerateTestMethodsTest.php - - - - message: '#^Parameter \#1 \$code of static method Phpactor\\CodeTransform\\Domain\\SourceCode\:\:fromStringAndPath\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PHPUnit/Tests/Unit/CodeTransform/GenerateTestMethodsTest.php - - - - message: '#^Parameter \#1 \$source of method Phpactor\\Extension\\PHPUnit\\Tests\\Unit\\CodeTransform\\GenerateTestMethodsTest\:\:createTestMethodGenerator\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PHPUnit/Tests/Unit/CodeTransform/GenerateTestMethodsTest.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PHPUnit/Tests/Unit/CodeTransform/GenerateTestMethodsTest.php - - - - message: '#^Parameter \#2 \$updater of class Phpactor\\Extension\\PHPUnit\\CodeTransform\\GenerateTestMethods constructor expects Phpactor\\CodeBuilder\\Domain\\Updater, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PHPUnit/Tests/Unit/CodeTransform/GenerateTestMethodsTest.php - - - - message: '#^Cannot access offset ''php'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: lib/Extension/Php/Model/ComposerPhpVersionResolver.php - - - - message: '#^Cannot access offset ''platform'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Php/Model/ComposerPhpVersionResolver.php - - - - message: '#^Method Phpactor\\Extension\\Php\\Model\\ComposerPhpVersionResolver\:\:resolve\(\) should return string\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Php/Model/ComposerPhpVersionResolver.php - - - - message: '#^Parameter \#1 \$versionString of method Phpactor\\Extension\\Php\\Model\\ComposerPhpVersionResolver\:\:resolveLowestVersion\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Php/Model/ComposerPhpVersionResolver.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Php/PhpExtension.php - - - - message: '#^Parameter \#1 \$composerJsonPath of class Phpactor\\Extension\\Php\\Model\\ComposerPhpVersionResolver constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Php/PhpExtension.php - - - - message: '#^Parameter \#1 \$version of class Phpactor\\Extension\\Php\\Model\\ConstantPhpVersionResolver constructor expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Php/PhpExtension.php - - - - message: '#^Parameter \#1 \$diffText of method Phpactor\\Diff\\DiffToTextEditsConverter\:\:toTextEdits\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Formatter/PhpCodeSnifferFormatter.php - - - - message: '#^Parameter \#1 \$diffText of method Phpactor\\Diff\\DiffToTextEditsConverter\:\:toTextEdits\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/LspCommand/FormatCommand.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Model/PhpCodeSnifferProcess.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Model/PhpCodeSnifferProcess.php - - - - message: '#^Parameter \#4 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Model/PhpCodeSnifferProcess.php - - - - message: '#^Parameter \#5 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Model/PhpCodeSnifferProcess.php - - - - message: '#^Parameter \#3 \$workspace of class Phpactor\\Extension\\PhpCodeSniffer\\LspCommand\\FormatCommand constructor expects Phpactor\\LanguageServer\\Core\\Workspace\\Workspace, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/PhpCodeSnifferExtension.php - - - - message: '#^Parameter \#1 \$string of method SebastianBergmann\\Diff\\Parser\:\:parse\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Provider/PhpCodeSnifferDiagnosticsProvider.php - - - - message: '#^Cannot call method getStdout\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/PhpCodeSniffer/Tests/Model/PhpCodeSnifferProcessTest.php - - - - message: '#^Parameter \#1 \$source of function Amp\\ByteStream\\buffer expects Amp\\ByteStream\\InputStream, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Tests/Model/PhpCodeSnifferProcessTest.php - - - - message: '#^Parameter \#2 \$haystack of static method PHPUnit\\Framework\\Assert\:\:assertStringContainsString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Tests/Model/PhpCodeSnifferProcessTest.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/PhpCodeSniffer/Tests/Model/PhpCodeSnifferProcessTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServerProtocol\\\\CodeAction'' and Phpactor\\LanguageServerProtocol\\CodeAction will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 2 - path: lib/Extension/PhpCodeSniffer/Tests/Provider/PhpCodeSnifferDiagnosticsProviderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\LanguageServerProtocol\\\\Diagnostic'' and Phpactor\\LanguageServerProtocol\\Diagnostic will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Extension/PhpCodeSniffer/Tests/Provider/PhpCodeSnifferDiagnosticsProviderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\ will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 2 - path: lib/Extension/PhpCodeSniffer/Tests/Provider/PhpCodeSnifferDiagnosticsProviderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\ will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 2 - path: lib/Extension/PhpCodeSniffer/Tests/Provider/PhpCodeSnifferDiagnosticsProviderTest.php - - - - message: '#^Parameter \#1 \$finders of class Phpactor\\ReferenceFinder\\ChainImplementationFinder constructor expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinder/ReferenceFinderExtension.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\ReferenceFinder\\\\ReferenceFinder'' and Phpactor\\ReferenceFinder\\ReferenceFinder will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Extension/ReferenceFinder/Tests/Unit/ReferenceFinderExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoDefinitionHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php - - - - message: '#^Method Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoDefinitionHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php - - - - message: '#^Parameter \#1 \$language of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:language\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php - - - - message: '#^Parameter \#1 \$target of method Phpactor\\Extension\\Rpc\\Response\\OpenFileResponse\:\:withTarget\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php - - - - message: '#^Parameter \#1 \$uri of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:uri\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoDefinitionHandler.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Instanceof between Phpactor\\TextDocument\\Location and Phpactor\\TextDocument\\Location will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Method Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoImplementationHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Method Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoImplementationHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Method Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoImplementationHandler\:\:locationsToReferences\(\) should return array\ but returns list\.$#' - identifier: return.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Parameter \#1 \$language of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:language\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Parameter \#1 \$target of method Phpactor\\Extension\\Rpc\\Response\\OpenFileResponse\:\:withTarget\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Parameter \#1 \$uri of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:uri\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoImplementationHandler.php - - - - message: '#^Method Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoTypeHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php - - - - message: '#^Method Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoTypeHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php - - - - message: '#^Parameter \#1 \$language of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:language\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php - - - - message: '#^Parameter \#1 \$target of method Phpactor\\Extension\\Rpc\\Response\\OpenFileResponse\:\:withTarget\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php - - - - message: '#^Parameter \#1 \$uri of method Phpactor\\TextDocument\\TextDocumentBuilder\:\:uri\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/Handler/GotoTypeHandler.php - - - - message: '#^Parameter \#1 \$finder of class Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoImplementationHandler constructor expects Phpactor\\ReferenceFinder\\ClassImplementationFinder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/ReferenceFinderRpcExtension.php - - - - message: '#^Parameter \#1 \$locator of class Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoDefinitionHandler constructor expects Phpactor\\ReferenceFinder\\DefinitionLocator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/ReferenceFinderRpcExtension.php - - - - message: '#^Parameter \#1 \$locator of class Phpactor\\Extension\\ReferenceFinderRpc\\Handler\\GotoTypeHandler constructor expects Phpactor\\ReferenceFinder\\TypeLocator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/ReferenceFinderRpc/ReferenceFinderRpcExtension.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\Rpc\\RequestHandler\:\:handle\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/ReferenceFinderRpc/Tests/Unit/ReferenceFinderRpcExtensionTest.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Cannot call method parameters\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Command\\RpcCommand\:\:__construct\(\) has parameter \$inputStream with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Command\\RpcCommand\:\:lastRequest\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Command\\RpcCommand\:\:processRequest\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Command\\RpcCommand\:\:processRequest\(\) has parameter \$request with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Command\\RpcCommand\:\:resolveInput\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\Rpc\\Command\\RpcCommand\:\:processRequest\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Parameter \#1 \$stream of function fgets expects resource, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Command/RpcCommand.php - - - - message: '#^Cannot access offset 0 on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Rpc/Diff/TextEditBuilder.php - - - - message: '#^Cannot access offset 1 on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Rpc/Diff/TextEditBuilder.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Diff\\TextEditBuilder\:\:calculateTextEdits\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Diff/TextEditBuilder.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Handler\:\:configure\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Handler.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Handler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Handler.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Handler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Handler.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Handler\\AbstractHandler\:\:createInputCallback\(\) should return Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/Handler/AbstractHandler.php - - - - message: '#^Parameter \#1 \$callbackAction of static method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:fromCallbackAndInputs\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Handler/AbstractHandler.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Handler\\EchoHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Handler/EchoHandler.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Handler\\EchoHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Handler/EchoHandler.php - - - - message: '#^Parameter \#1 \$message of static method Phpactor\\Extension\\Rpc\\Response\\EchoResponse\:\:fromMessage\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Handler/EchoHandler.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\HandlerRegistry\:\:get\(\) has parameter \$handlerName with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/HandlerRegistry.php - - - - message: '#^Cannot access offset mixed on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Registry\\ActiveHandlerRegistry\:\:__construct\(\) has parameter \$handlers with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Registry\\ActiveHandlerRegistry\:\:all\(\) should return array\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Registry\\ActiveHandlerRegistry\:\:get\(\) has parameter \$handlerName with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Registry\\ActiveHandlerRegistry\:\:get\(\) should return Phpactor\\Extension\\Rpc\\Handler but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Parameter \#1 \$handler of method Phpactor\\Extension\\Rpc\\Registry\\ActiveHandlerRegistry\:\:register\(\) expects Phpactor\\Extension\\Rpc\\Handler, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Registry\\ActiveHandlerRegistry\:\:\$handlers has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/Extension/Rpc/Registry/ActiveHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Registry\\LazyContainerHandlerRegistry\:\:__construct\(\) has parameter \$serviceMap with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Registry\\LazyContainerHandlerRegistry\:\:all\(\) should return array\ but returns array\\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Registry\\LazyContainerHandlerRegistry\:\:get\(\) has parameter \$handlerName with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Registry\\LazyContainerHandlerRegistry\:\:get\(\) should return Phpactor\\Extension\\Rpc\\Handler but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(string\)\: mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php - - - - message: '#^Parameter \#1 \$id of method Psr\\Container\\ContainerInterface\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 3 - path: lib/Extension/Rpc/Registry/LazyContainerHandlerRegistry.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Request\:\:__construct\(\) has parameter \$parameters with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Request.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Request\:\:fromArray\(\) has parameter \$actionConfig with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Request.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Request\:\:fromNameAndParameters\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Request.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Request\:\:fromNameAndParameters\(\) has parameter \$parameters with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Request.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Request\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Request.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Request\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Request.php - - - - message: '#^Parameter \#1 \$name of class Phpactor\\Extension\\Rpc\\Request constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Request.php - - - - message: '#^Parameter \#2 \$parameters of class Phpactor\\Extension\\Rpc\\Request constructor expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Request.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\RequestHandler\\RequestHandler\:\:handle\(\) should return Phpactor\\Extension\\Rpc\\Response but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/RequestHandler/RequestHandler.php - - - - message: '#^Parameter \#1 \$config of method Phpactor\\MapResolver\\Resolver\:\:resolve\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/RequestHandler/RequestHandler.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\CloseFileResponse\:\:fromPath\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/CloseFileResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\CloseFileResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/CloseFileResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\CollectionResponse\:\:__construct\(\) has parameter \$actions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/CollectionResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\CollectionResponse\:\:actions\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/CollectionResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\CollectionResponse\:\:fromActions\(\) has parameter \$actions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/CollectionResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\CollectionResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/CollectionResponse.php - - - - message: '#^Parameter \#1 \$action of method Phpactor\\Extension\\Rpc\\Response\\CollectionResponse\:\:add\(\) expects Phpactor\\Extension\\Rpc\\Response, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/CollectionResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\EchoResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/EchoResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ErrorResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/ErrorResponse.php - - - - message: '#^Cannot access offset ''file'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Cannot access offset ''references'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\FileReferencesResponse\:\:fromArray\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\FileReferencesResponse\:\:fromArray\(\) has parameter \$array with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\FileReferencesResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\FileReferencesResponse\:\:references\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(array\)\: mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Parameter \#1 \$references of class Phpactor\\Extension\\Rpc\\Response\\FileReferencesResponse constructor expects array\, list given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Parameter \#1 \$start of static method Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference\:\:fromStartEndLineNumberLineAndCol\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Parameter \#2 \$end of static method Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference\:\:fromStartEndLineNumberLineAndCol\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Parameter \#3 \$lineNumber of static method Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference\:\:fromStartEndLineNumberLineAndCol\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Parameter \#4 \$line of static method Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference\:\:fromStartEndLineNumberLineAndCol\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Parameter \#5 \$col of static method Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference\:\:fromStartEndLineNumberLineAndCol\(\) expects int\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/FileReferencesResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\InformationResponse\:\:__construct\(\) has parameter \$information with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Response/InformationResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\InformationResponse\:\:information\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/Response/InformationResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\InformationResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/InformationResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:__construct\(\) has parameter \$choices with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ChoiceInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:__construct\(\) has parameter \$keyMap with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ChoiceInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:choices\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ChoiceInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:fromNameLabelChoices\(\) has parameter \$choices with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ChoiceInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:fromNameLabelChoicesAndDefault\(\) has parameter \$choices with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ChoiceInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ChoiceInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ChoiceInput\:\:withKeys\(\) has parameter \$keyMap with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ChoiceInput.php - - - - message: '#^Unsafe usage of new static\(\)\.$#' - identifier: new.static - count: 2 - path: lib/Extension/Rpc/Response/Input/ChoiceInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ConfirmInput\:\:fromNameAndLabel\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/Input/ConfirmInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ConfirmInput\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ConfirmInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\Input\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/Input.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\ListInput\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/ListInput.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Response\\Input\\ListInput\:\:\$allowMultipleResults has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/Extension/Rpc/Response/Input/ListInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\TextInput\:\:fromNameLabelAndDefault\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/Input/TextInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Input\\TextInput\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Input/TextInput.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:__construct\(\) has parameter \$inputs with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/InputCallbackResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:fromCallbackAndInputs\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/InputCallbackResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:fromCallbackAndInputs\(\) has parameter \$inputs with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/InputCallbackResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:inputs\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/InputCallbackResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/InputCallbackResponse.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\Extension\\Rpc\\Response\\Input\\Input\)\: array\{name\: string, type\: string, parameters\: array\} given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/InputCallbackResponse.php - - - - message: '#^Parameter \#1 \$input of method Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:add\(\) expects Phpactor\\Extension\\Rpc\\Response\\Input\\Input, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/InputCallbackResponse.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Response\\InputCallbackResponse\:\:\$inputs type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/InputCallbackResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\OpenFileResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/OpenFileResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Reference\\FileReferences\:\:__construct\(\) has parameter \$references with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Reference\\FileReferences\:\:fromPathAndReferences\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Reference\\FileReferences\:\:fromPathAndReferences\(\) has parameter \$filePath with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Reference\\FileReferences\:\:fromPathAndReferences\(\) has parameter \$references with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Reference\\FileReferences\:\:toArray\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference\)\: mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Parameter \#1 \$filePath of class Phpactor\\Extension\\Rpc\\Response\\Reference\\FileReferences constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Parameter \#1 \$reference of method Phpactor\\Extension\\Rpc\\Response\\Reference\\FileReferences\:\:addReference\(\) expects Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Response\\Reference\\FileReferences\:\:\$references type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/Reference/FileReferences.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference\:\:fromStartEndLineNumberLineAndCol\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/Reference/Reference.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\Reference\\Reference\:\:toArray\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/Reference/Reference.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReplaceFileSourceResponse\:\:fromPathAndSource\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/ReplaceFileSourceResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReplaceFileSourceResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/ReplaceFileSourceResponse.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Cannot call method value\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnChoiceResponse\:\:__construct\(\) has parameter \$options with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnChoiceResponse\:\:fromOptions\(\) has parameter \$options with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnChoiceResponse\:\:options\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnChoiceResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Parameter \#1 \$option of method Phpactor\\Extension\\Rpc\\Response\\ReturnChoiceResponse\:\:add\(\) expects Phpactor\\Extension\\Rpc\\Response\\ReturnOption, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Response\\ReturnChoiceResponse\:\:\$options type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/ReturnChoiceResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnOption\:\:__construct\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Response/ReturnOption.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnOption\:\:fromNameAndValue\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/ReturnOption.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnOption\:\:fromNameAndValue\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Response/ReturnOption.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnOption\:\:value\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/ReturnOption.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnResponse\:\:__construct\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Response/ReturnResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnResponse\:\:fromValue\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/Rpc/Response/ReturnResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/ReturnResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\ReturnResponse\:\:value\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/ReturnResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:fromPathOldAndNewSource\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Response/UpdateFileSourceResponse.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Response\\UpdateFileSourceResponse\:\:parameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Response/UpdateFileSourceResponse.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Rpc/RpcExtension.php - - - - message: '#^Parameter \#1 \$handler of class Phpactor\\Extension\\Rpc\\Command\\RpcCommand constructor expects Phpactor\\Extension\\Rpc\\RequestHandler, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/RpcExtension.php - - - - message: '#^Parameter \#1 \$handlerRegistry of class Phpactor\\Extension\\Rpc\\RpcCommandDocumentor constructor expects Phpactor\\Extension\\Rpc\\HandlerRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/RpcExtension.php - - - - message: '#^Parameter \#1 \$registry of class Phpactor\\Extension\\Rpc\\RequestHandler\\RequestHandler constructor expects Phpactor\\Extension\\Rpc\\HandlerRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/RpcExtension.php - - - - message: '#^Parameter \#2 \$replayPath of class Phpactor\\Extension\\Rpc\\Command\\RpcCommand constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/RpcExtension.php - - - - message: '#^Parameter \#3 \$storeReplay of class Phpactor\\Extension\\Rpc\\Command\\RpcCommand constructor expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/RpcExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/Rpc/RpcExtension.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\RpcVersion\:\:asString\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/RpcVersion.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Test\\HandlerTester\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Test/HandlerTester.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Test\\HandlerTester\:\:handle\(\) has parameter \$parameters with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Test/HandlerTester.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\Rpc\\RequestHandler\\RequestHandler\:\:handle\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Test/HandlerTester.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Tests\\Integration\\Command\\RpcCommandTest\:\:execute\(\) has parameter \$input with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Tests/Integration/Command/RpcCommandTest.php - - - - message: '#^Parameter \#1 \$handler of class Phpactor\\Extension\\Rpc\\Command\\RpcCommand constructor expects Phpactor\\Extension\\Rpc\\RequestHandler, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Tests/Integration/Command/RpcCommandTest.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Tests\\Unit\\Diff\\TextEditBuilderTest\:\:testDiff\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Tests/Unit/Diff/TextEditBuilderTest.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/Rpc/Tests/Unit/Registry/ActiveHandlerRegistryTest.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Tests\\Unit\\Registry\\ActiveHandlerRegistryTest\:\:create\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/Rpc/Tests/Unit/Registry/ActiveHandlerRegistryTest.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Tests\\Unit\\Registry\\ActiveHandlerRegistryTest\:\:create\(\) has parameter \$actions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/Rpc/Tests/Unit/Registry/ActiveHandlerRegistryTest.php - - - - message: '#^Cannot access offset 0 on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RequestHandlerTest.php - - - - message: '#^Cannot call method setDefaults\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RequestHandlerTest.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\Rpc\\RequestHandler\\RequestHandler\:\:handle\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RequestHandlerTest.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Tests\\Unit\\RequestHandlerTest\:\:\$handler with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RequestHandlerTest.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Tests\\Unit\\RequestHandlerTest\:\:\$handlerRegistry with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RequestHandlerTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\Extension\\\\Rpc\\\\Response'' and Phpactor\\Extension\\Rpc\\Response will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RpcExtensionTest.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RpcExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\Rpc\\Tests\\Unit\\RpcExtensionTest\:\:getHandler\(\) should return Phpactor\\Extension\\Rpc\\RequestHandler but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RpcExtensionTest.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\Rpc\\RequestHandler\:\:handle\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Rpc/Tests/Unit/RpcExtensionTest.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Tests\\Unit\\Test\\HandlerTesterTest\:\:\$response is unused\.$#' - identifier: property.unused - count: 1 - path: lib/Extension/Rpc/Tests/Unit/Test/HandlerTesterTest.php - - - - message: '#^Property Phpactor\\Extension\\Rpc\\Tests\\Unit\\Test\\HandlerTesterTest\:\:\$response with generic class Prophecy\\Prophecy\\ObjectProphecy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: lib/Extension/Rpc/Tests/Unit/Test/HandlerTesterTest.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/SourceCodeFilesystem/SourceCodeFilesystemExtension.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/SourceCodeFilesystem/SourceCodeFilesystemExtension.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystem\\SourceCodeFilesystemExtension\:\:projectRoot\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/SourceCodeFilesystem/SourceCodeFilesystemExtension.php - - - - message: '#^Parameter \#2 \$classLoader of class Phpactor\\Filesystem\\Adapter\\Composer\\ComposerFileListProvider constructor expects Composer\\Autoload\\ClassLoader, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystem/SourceCodeFilesystemExtension.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystem\\Tests\\Unit\\SourceCodeFilesystemExtensionTest\:\:createRegistry\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/SourceCodeFilesystem/Tests/Unit/SourceCodeFilesystemExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystem\\Tests\\Unit\\SourceCodeFilesystemExtensionTest\:\:createRegistry\(\) should return Phpactor\\Filesystem\\Domain\\FilesystemRegistry but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/SourceCodeFilesystem/Tests/Unit/SourceCodeFilesystemExtensionTest.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystem\\Tests\\Unit\\SourceCodeFilesystemExtensionTest\:\:provideFilesystems\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/SourceCodeFilesystem/Tests/Unit/SourceCodeFilesystemExtensionTest.php - - - - message: '#^Parameter \#1 \$expected of method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) expects class\-string\, string given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystem/Tests/Unit/SourceCodeFilesystemExtensionTest.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystem/Tests/Unit/SourceCodeFilesystemExtensionTest.php - - - - message: '#^Parameter \#1 \$filesystemName of method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:classSearch\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Command/ClassSearchCommand.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Command/ClassSearchCommand.php - - - - message: '#^Parameter \#2 \$data of method Phpactor\\Extension\\Core\\Console\\Dumper\\Dumper\:\:dump\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Command/ClassSearchCommand.php - - - - message: '#^Parameter \#2 \$name of method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:classSearch\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Command/ClassSearchCommand.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Cannot access offset ''class'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\Rpc\\ClassSearchHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\Rpc\\ClassSearchHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Parameter \#1 \$array of function reset expects array\|object, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\Extension\\Rpc\\Response\\ReturnOption\:\:fromNameAndValue\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Parameter \#2 \$name of method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:classSearch\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/Rpc/ClassSearchHandler.php - - - - message: '#^Binary operation "\." between ''\{'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Binary operation "\." between mixed and ''\.php'' results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Cannot call method path\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 4 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Iterating over an object of an unknown class Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\FileList\.$#' - identifier: class.notFound - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:builtInResults\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:builtInResults\(\) has parameter \$results with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:classSearch\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:convertFqnToRelativePath\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:resolveShortName\(\) has parameter \$declaredClass with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:resolveShortName\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:tryAndReflect\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Method Phpactor\\Filesystem\\Domain\\Filesystem\:\:fileList\(\) invoked with 1 parameter, 0 required\.$#' - identifier: arguments.count - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^PHPDoc tag @var for variable \$files contains unknown class Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\FileList\.$#' - identifier: class.notFound - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^PHPDoc tag @var with type Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\FileList\ is not subtype of native type Phpactor\\Filesystem\\Domain\\FileList\.$#' - identifier: varTag.nativeType - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Parameter \#1 \$array of function array_values expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Parameter \#1 \$haystack of function strrpos expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Parameter \#1 \$string of function substr expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Parameter \#2 \$name of method Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch\:\:builtInResults\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilestem/Application/ClassSearch.php - - - - message: '#^Parameter \#1 \$classSearch of class Phpactor\\Extension\\SourceCodeFilesystemExtra\\Rpc\\ClassSearchHandler constructor expects Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php - - - - message: '#^Parameter \#1 \$filesystemRegistry of class Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch constructor expects Phpactor\\Filesystem\\Domain\\FilesystemRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php - - - - message: '#^Parameter \#1 \$search of class Phpactor\\Extension\\SourceCodeFilesystemExtra\\Command\\ClassSearchCommand constructor expects Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\SourceCodeFilesystemExtra\\Command\\ClassSearchCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php - - - - message: '#^Parameter \#2 \$fileToClass of class Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch constructor expects Phpactor\\ClassFileConverter\\Domain\\FileToClass, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php - - - - message: '#^Parameter \#3 \$reflector of class Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilestem\\Application\\ClassSearch constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/SourceCodeFilesystemExtra/SourceCodeFilesystemExtraExtension.php - - - - message: '#^Parameter \#1 \$serviceEl of method Phpactor\\Extension\\Symfony\\Adapter\\Symfony\\XmlSymfonyContainerInspector\:\:serviceFromEl\(\) expects DOMNode, DOMNameSpaceNode\|DOMNode given\.$#' - identifier: argument.type - count: 2 - path: lib/Extension/Symfony/Adapter/Symfony/XmlSymfonyContainerInspector.php - - - - message: '#^Cannot access property \$parent on Microsoft\\PhpParser\\Node\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/Extension/Symfony/Completor/SymfonyContainerCompletor.php - - - - message: '#^Cannot call method label\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/Extension/Symfony/Tests/Integration/Completor/SymfonyContainerCompletorTest.php - - - - message: '#^Cannot call method name\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/Extension/Symfony/Tests/Integration/Completor/SymfonyContainerCompletorTest.php - - - - message: '#^Cannot access property \$type on Phpactor\\Extension\\Symfony\\Model\\SymfonyContainerService\|null\.$#' - identifier: property.nonObject - count: 2 - path: lib/Extension/Symfony/Tests/Unit/Adapter/XmlSymfonyContainerInspectorTest.php - - - - message: '#^Cannot call method at\(\) on Phpactor\\WorseReflection\\Core\\Inference\\FunctionArguments\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/Symfony/WorseReflection/SymfonyContainerContextResolver.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, Phpactor\\WorseReflection\\Core\\Inference\\FunctionArguments\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/Symfony/WorseReflection/SymfonyContainerContextResolver.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/WorseReferenceFinder/Tests/Unit/WorseReferenceFinderExtensionTest.php - - - - message: '#^Instanceof between Phpactor\\ReferenceFinder\\ReferenceFinder and Phpactor\\ReferenceFinder\\ReferenceFinder will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/WorseReferenceFinder/Tests/Unit/WorseReferenceFinderExtensionTest.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\WorseReferenceFinder\\WorsePlainTextClassDefinitionLocator constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReferenceFinder/WorseReferenceFinderExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\WorseReferenceFinder\\WorseReflectionDefinitionLocator constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReferenceFinder/WorseReferenceFinderExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\WorseReferenceFinder\\WorseReflectionTypeLocator constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReferenceFinder/WorseReferenceFinderExtension.php - - - - message: '#^Method Phpactor\\Extension\\WorseReflection\\Tests\\Unit\\WorseReflectionExtensionTest\:\:createReflector\(\) should return Phpactor\\WorseReflection\\Reflector but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Extension/WorseReflection/Tests/Unit/WorseReflectionExtensionTest.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#1 \$converter of class Phpactor\\WorseReflection\\Bridge\\Phpactor\\ClassToFileSourceLocator constructor expects Phpactor\\ClassFileConverter\\Domain\\ClassToFile, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#1 \$frameWalker of method Phpactor\\WorseReflection\\ReflectorBuilder\:\:addFrameWalker\(\) expects Phpactor\\WorseReflection\\Core\\Inference\\Walker, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#1 \$locator of method Phpactor\\WorseReflection\\ReflectorBuilder\:\:addLocator\(\) expects Phpactor\\WorseReflection\\Core\\SourceCodeLocator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#1 \$memberContextResolver of method Phpactor\\WorseReflection\\ReflectorBuilder\:\:addMemberContextResolver\(\) expects Phpactor\\WorseReflection\\Core\\Inference\\Resolver\\MemberAccess\\MemberContextResolver, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#1 \$provider of method Phpactor\\WorseReflection\\ReflectorBuilder\:\:addDiagnosticProvider\(\) expects Phpactor\\WorseReflection\\Core\\DiagnosticProvider, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#1 \$provider of method Phpactor\\WorseReflection\\ReflectorBuilder\:\:addMemberProvider\(\) expects Phpactor\\WorseReflection\\Core\\Virtual\\ReflectionMemberProvider, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#2 \$priority of method Phpactor\\WorseReflection\\ReflectorBuilder\:\:addLocator\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#2 \$stubPath of class Phpactor\\WorseReflection\\Core\\SourceCodeLocator\\StubSourceLocator constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#3 \$cacheDir of class Phpactor\\WorseReflection\\Core\\SourceCodeLocator\\StubSourceLocator constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflection/WorseReflectionExtension.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Extension\\WorseReflectionAnalyse\\Model\\Analyser\:\:analyse\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionAnalyse/Command/AnalyseCommand.php - - - - message: '#^Parameter \#1 \$path of method Phpactor\\Extension\\WorseReflectionAnalyse\\Model\\Analyser\:\:fileList\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionAnalyse/Command/AnalyseCommand.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/WorseReflectionAnalyse/Tests/Command/AnalyseCommandTest.php - - - - message: '#^Instanceof between Phpactor\\Extension\\WorseReflectionAnalyse\\Command\\AnalyseCommand and Phpactor\\Extension\\WorseReflectionAnalyse\\Command\\AnalyseCommand will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/WorseReflectionAnalyse/Tests/Command/AnalyseCommandTest.php - - - - message: '#^Parameter \#1 \$filesystem of class Phpactor\\Extension\\WorseReflectionAnalyse\\Model\\Analyser constructor expects Phpactor\\Filesystem\\Domain\\FilesystemRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionAnalyse/WorseReflectionAnalyseExtension.php - - - - message: '#^Parameter \#2 \$reflector of class Phpactor\\Extension\\WorseReflectionAnalyse\\Model\\Analyser constructor expects Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionAnalyse/WorseReflectionAnalyseExtension.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Extension/WorseReflectionExtra/Application/ClassReflector.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethod will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Extension/WorseReflectionExtra/Application/ClassReflector.php - - - - message: '#^Method Phpactor\\Extension\\WorseReflectionExtra\\Application\\ClassReflector\:\:reflect\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/WorseReflectionExtra/Application/ClassReflector.php - - - - message: '#^Call to an undefined method Phpactor\\WorseReflection\\Core\\Inference\\Variable\:\:nodeContext\(\)\.$#' - identifier: method.notFound - count: 2 - path: lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php - - - - message: '#^Cannot call method toInt\(\) on int\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php - - - - message: '#^Cannot call method type\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php - - - - message: '#^Cannot call method value\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php - - - - message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Extension/WorseReflectionExtra/Application/OffsetInfo.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Command/ClassReflectorCommand.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry\:\:get\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Command/OffsetInfoCommand.php - - - - message: '#^Parameter \#1 \$sourcePath of method Phpactor\\Extension\\WorseReflectionExtra\\Application\\OffsetInfo\:\:infoForOffset\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Command/OffsetInfoCommand.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\Extension\\WorseReflectionExtra\\Application\\OffsetInfo\:\:infoForOffset\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Command/OffsetInfoCommand.php - - - - message: '#^Parameter \#3 \$showFrame of method Phpactor\\Extension\\WorseReflectionExtra\\Application\\OffsetInfo\:\:infoForOffset\(\) expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Command/OffsetInfoCommand.php - - - - message: '#^Method Phpactor\\Extension\\WorseReflectionExtra\\Rpc\\OffsetInfoHandler\:\:handle\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php - - - - message: '#^Method Phpactor\\Extension\\WorseReflectionExtra\\Rpc\\OffsetInfoHandler\:\:handle\(\) has parameter \$arguments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php - - - - message: '#^Method Phpactor\\Extension\\WorseReflectionExtra\\Rpc\\OffsetInfoHandler\:\:serialize\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php - - - - message: '#^Parameter \#1 \$information of static method Phpactor\\Extension\\Rpc\\Response\\InformationResponse\:\:fromString\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php - - - - message: '#^Parameter \#1 \$offset of method Phpactor\\Extension\\WorseReflectionExtra\\Rpc\\OffsetInfoHandler\:\:serialize\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php - - - - message: '#^Parameter \#1 \$offset of static method Phpactor\\TextDocument\\ByteOffset\:\:fromInt\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php - - - - message: '#^Parameter \#1 \$text of static method Phpactor\\TextDocument\\TextDocumentBuilder\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/Rpc/OffsetInfoHandler.php - - - - message: '#^Parameter \#1 \$classFileNormalizer of class Phpactor\\Extension\\WorseReflectionExtra\\Application\\ClassReflector constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Parameter \#1 \$infoForOffset of class Phpactor\\Extension\\WorseReflectionExtra\\Command\\OffsetInfoCommand constructor expects Phpactor\\Extension\\WorseReflectionExtra\\Application\\OffsetInfo, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\WorseReflectionExtra\\Application\\OffsetInfo constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\WorseReflectionExtra\\Command\\ClassReflectorCommand constructor expects Phpactor\\Extension\\WorseReflectionExtra\\Application\\ClassReflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Extension\\WorseReflectionExtra\\Rpc\\OffsetInfoHandler constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Parameter \#2 \$classFileNormalizer of class Phpactor\\Extension\\WorseReflectionExtra\\Application\\OffsetInfo constructor expects Phpactor\\Extension\\Core\\Application\\Helper\\ClassFileNormalizer, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\WorseReflectionExtra\\Command\\ClassReflectorCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Parameter \#2 \$dumperRegistry of class Phpactor\\Extension\\WorseReflectionExtra\\Command\\OffsetInfoCommand constructor expects Phpactor\\Extension\\Core\\Console\\Dumper\\DumperRegistry, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Parameter \#2 \$reflector of class Phpactor\\Extension\\WorseReflectionExtra\\Application\\ClassReflector constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Extension/WorseReflectionExtra/WorseReflectionExtraExtension.php - - - - message: '#^Method Phpactor\\FilePathResolver\\CachingPathResolver\:\:resolve\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/FilePathResolver/CachingPathResolver.php - - - - message: '#^Property Phpactor\\FilePathResolver\\CachingPathResolver\:\:\$cache type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/FilePathResolver/CachingPathResolver.php - - - - message: '#^Method Phpactor\\FilePathResolver\\Exception\\UnknownToken\:\:__construct\(\) has parameter \$knownTokens with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/FilePathResolver/Exception/UnknownToken.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\FilePathResolver\\\\PathResolver'' and Phpactor\\FilePathResolver\\FilteringPathResolver will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/FilePathResolver/Tests/Unit/FilteringPathResolverTest.php - - - - message: '#^Cannot call method isDir\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Filesystem/Adapter/Simple/SimpleFilesystem.php - - - - message: '#^Parameter \#1 \$originFile of method Symfony\\Component\\Filesystem\\Filesystem\:\:copy\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Filesystem/Adapter/Simple/SimpleFilesystem.php - - - - message: '#^Parameter \#1 \$string of static method Phpactor\\Filesystem\\Domain\\FilePath\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Filesystem/Adapter/Simple/SimpleFilesystem.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Filesystem/Domain/FilePath.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\Filesystem\\\\Domain\\\\FilePath'' and Phpactor\\Filesystem\\Domain\\FilePath will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Filesystem/Tests/Adapter/Composer/ComposerFilesystemTest.php - - - - message: '#^Parameter \#2 \$classLoader of class Phpactor\\Filesystem\\Adapter\\Composer\\ComposerFilesystem constructor expects Composer\\Autoload\\ClassLoader, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Filesystem/Tests/Adapter/Composer/ComposerFilesystemTest.php - - - - message: '#^Method Phpactor\\Filesystem\\Tests\\Adapter\\IntegrationTestCase\:\:getProjectAutoloader\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Filesystem/Tests/Adapter/IntegrationTestCase.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\Filesystem\\\\Domain\\\\FileList'' and Phpactor\\Filesystem\\Domain\\FileList will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Filesystem/Tests/Unit/Domain/ChainFileListProviderTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\Filesystem\\\\Domain\\\\FilePath'' and Phpactor\\Filesystem\\Domain\\FilePath will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: lib/Filesystem/Tests/Unit/Domain/FilePathTest.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 1 - path: lib/Indexer/Adapter/Php/FileSearchIndex.php - - - - message: '#^Parameter \#1 \$array \(non\-empty\-list\\) to function array_filter does not contain falsy values, the array will always stay the same\.$#' - identifier: arrayFilter.same - count: 1 - path: lib/Indexer/Adapter/Php/FileSearchIndex.php - - - - message: '#^Method Phpactor\\Indexer\\Adapter\\Php\\InMemory\\InMemoryIndex\:\:get\(\) should return TRecord of Phpactor\\Indexer\\Model\\Record but returns Phpactor\\Indexer\\Model\\Record\.$#' - identifier: return.type - count: 1 - path: lib/Indexer/Adapter/Php/InMemory/InMemoryIndex.php - - - - message: '#^Method Phpactor\\Indexer\\Adapter\\Php\\InMemory\\InMemoryIndex\:\:lastUpdate\(\) should return int but returns int\|null\.$#' - identifier: return.type - count: 1 - path: lib/Indexer/Adapter/Php/InMemory/InMemoryIndex.php - - - - message: '#^Property Phpactor\\Indexer\\Adapter\\Php\\InMemory\\InMemoryIndex\:\:\$lastUpdate \(int\|null\) is never assigned null so it can be removed from the property type\.$#' - identifier: property.unusedType - count: 1 - path: lib/Indexer/Adapter/Php/InMemory/InMemoryIndex.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\ClassRecord and Phpactor\\Indexer\\Model\\Record\\HasPath will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Adapter/ReferenceFinder/IndexedImplementationFinder.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/AbstractClassLikeIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\ClassRecord and Phpactor\\Indexer\\Model\\Record\\ClassRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/AbstractClassLikeIndexer.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/ClassDeclarationIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\ClassRecord and Phpactor\\Indexer\\Model\\Record\\ClassRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/ClassDeclarationIndexer.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\Indexer\\Model\\Record\\ClassRecord\:\:fromName\(\) expects string, Microsoft\\PhpParser\\ResolvedName\|string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/ClassDeclarationIndexer.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 4 - path: lib/Indexer/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexer.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\ClassRecord and Phpactor\\Indexer\\Model\\Record\\ClassRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\FileRecord and Phpactor\\Indexer\\Model\\Record\\FileRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexer.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\Indexer\\Model\\Record\\ClassRecord\:\:fromName\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/ClassLikeReferenceIndexer.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 3 - path: lib/Indexer/Adapter/Tolerant/Indexer/ConstantDeclarationIndexer.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\DelimitedList\\ConstElementList and Microsoft\\PhpParser\\Node\\DelimitedList\\ConstElementList will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/ConstantDeclarationIndexer.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\Expression\\CallExpression and Microsoft\\PhpParser\\Node\\Expression\\CallExpression will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/ConstantDeclarationIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\ConstantRecord and Phpactor\\Indexer\\Model\\Record\\ConstantRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/ConstantDeclarationIndexer.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/FunctionDeclarationIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\FunctionRecord and Phpactor\\Indexer\\Model\\Record\\FunctionRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/FunctionDeclarationIndexer.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 4 - path: lib/Indexer/Adapter/Tolerant/Indexer/FunctionReferenceIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\FileRecord and Phpactor\\Indexer\\Model\\Record\\FileRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/FunctionReferenceIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\FunctionRecord and Phpactor\\Indexer\\Model\\Record\\FunctionRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/FunctionReferenceIndexer.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 4 - path: lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\Expression\\Variable and Microsoft\\PhpParser\\Node\\Expression\\Variable will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Token and Microsoft\\PhpParser\\Token will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\FileRecord and Phpactor\\Indexer\\Model\\Record\\FileRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\MemberRecord and Phpactor\\Indexer\\Model\\Record\\MemberRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Adapter/Tolerant/Indexer/MemberIndexer.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/TraitUseClauseIndexer.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\ClassRecord and Phpactor\\Indexer\\Model\\Record\\ClassRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/TraitUseClauseIndexer.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\Indexer\\Model\\Record\\ClassRecord\:\:fromName\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Adapter/Tolerant/Indexer/TraitUseClauseIndexer.php - - - - message: '#^Cannot call method path\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Indexer/Extension/Command/IndexBuildCommand.php - - - - message: '#^Cannot call method stop\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Indexer/Extension/Command/IndexBuildCommand.php - - - - message: '#^Cannot call method wait\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Indexer/Extension/Command/IndexBuildCommand.php - - - - message: '#^Parameter \#1 \$subPath of method Phpactor\\Indexer\\Model\\Indexer\:\:getJob\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/Command/IndexBuildCommand.php - - - - message: '#^Parameter \#1 \.\.\.\$paths of static method Symfony\\Component\\Filesystem\\Path\:\:join\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/Command/IndexBuildCommand.php - - - - message: '#^Property Phpactor\\Indexer\\Extension\\Command\\IndexBuildCommand\:\:\$usage is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: lib/Indexer/Extension/Command/IndexBuildCommand.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/Indexer/Extension/Command/IndexCleanCommand.php - - - - message: '#^Cannot access offset 0 on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Indexer/Extension/Command/IndexCleanCommand.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/Indexer/Extension/Command/IndexCleanCommand.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\Indexer\\Model\\IndexInfos\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/Command/IndexCleanCommand.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Indexer/Extension/Command/IndexCleanCommand.php - - - - message: '#^Binary operation "\+" between mixed and 1 results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/Indexer/Extension/Command/IndexQueryCommand.php - - - - message: '#^Cannot call method references\(\) on Phpactor\\Indexer\\Model\\Record\\FileRecord\|null\.$#' - identifier: method.nonObject - count: 2 - path: lib/Indexer/Extension/Command/IndexQueryCommand.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\Indexer\\Model\\Query\\Criteria\:\:exactShortName\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/Command/IndexSearchCommand.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\Indexer\\Model\\Query\\Criteria\:\:fqnBeginsWith\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/Command/IndexSearchCommand.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\Indexer\\Model\\Query\\Criteria\:\:shortNameBeginsWith\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/Command/IndexSearchCommand.php - - - - message: '#^Cannot call method resolve\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Method Phpactor\\Indexer\\Extension\\IndexerExtension\:\:projectRoot\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, Closure\(string\)\: string given\.$#' - identifier: argument.type - count: 2 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#1 \$path of function dirname expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#1 \$reflector of class Phpactor\\Indexer\\Adapter\\ReferenceFinder\\Util\\ContainerTypeResolver constructor expects Phpactor\\WorseReflection\\Core\\Reflector\\ClassReflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#1 \$watchers of class Phpactor\\AmpFsWatch\\Watcher\\Fallback\\FallbackWatcher constructor expects array\, list given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#1 \.\.\.\$paths of static method Symfony\\Component\\Filesystem\\Path\:\:join\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#2 \$includePatterns of class Phpactor\\AmpFsWatch\\Watcher\\PatternMatching\\PatternMatchingWatcher constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#2 \$reflector of class Phpactor\\Indexer\\Adapter\\ReferenceFinder\\IndexedImplementationFinder constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#2 \$reflector of class Phpactor\\Indexer\\Adapter\\ReferenceFinder\\IndexedReferenceFinder constructor expects Phpactor\\WorseReflection\\Reflector, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Parameter \#3 \$excludePatterns of class Phpactor\\AmpFsWatch\\Watcher\\PatternMatching\\PatternMatchingWatcher constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Indexer/Extension/IndexerExtension.php - - - - message: '#^Cannot call method wait\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Indexer/Extension/Rpc/IndexHandler.php - - - - message: '#^Parameter \#1 \$time of class Amp\\Delayed constructor expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Extension/Rpc/IndexHandler.php - - - - message: '#^Parameter \#2 \$includePatterns of class Phpactor\\Indexer\\Adapter\\Filesystem\\FilesystemFileListProvider constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/IndexAgentBuilder.php - - - - message: '#^Parameter \#3 \$excludePatterns of class Phpactor\\Indexer\\Adapter\\Filesystem\\FilesystemFileListProvider constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/IndexAgentBuilder.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Indexer/Model/IndexJob.php - - - - message: '#^Instanceof between SplFileInfo and SplFileInfo will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Model/IndexJob.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 3 - path: lib/Indexer/Model/Query/ClassQuery.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\ClassRecord and Phpactor\\Indexer\\Model\\Record\\ClassRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Model/Query/ClassQuery.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\FileRecord and Phpactor\\Indexer\\Model\\Record\\FileRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Model/Query/ClassQuery.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Indexer/Model/Query/FunctionQuery.php - - - - message: '#^Cannot call method references\(\) on Phpactor\\Indexer\\Model\\Record\\FunctionRecord\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Indexer/Model/Query/FunctionQuery.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\FileRecord and Phpactor\\Indexer\\Model\\Record\\FileRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Model/Query/FunctionQuery.php - - - - message: '#^Parameter \#1 \$record of method Phpactor\\Indexer\\Model\\RecordReferences\:\:to\(\) expects Phpactor\\Indexer\\Model\\Record, Phpactor\\Indexer\\Model\\Record\\FunctionRecord\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Model/Query/FunctionQuery.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/Indexer/Model/Query/MemberQuery.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\FileRecord and Phpactor\\Indexer\\Model\\Record\\FileRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Model/Query/MemberQuery.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\MemberRecord and Phpactor\\Indexer\\Model\\Record\\MemberRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Model/Query/MemberQuery.php - - - - message: '#^Property Phpactor\\Indexer\\Model\\QueryClient\:\:\$enhancer \(Phpactor\\Indexer\\Model\\RecordReferenceEnhancer\|null\) is never assigned null so it can be removed from the property type\.$#' - identifier: property.unusedType - count: 1 - path: lib/Indexer/Model/QueryClient.php - - - - message: '#^Property Phpactor\\Indexer\\Model\\QueryClient\:\:\$enhancer is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: lib/Indexer/Model/QueryClient.php - - - - message: '#^Property Phpactor\\Indexer\\Model\\QueryClient\:\:\$index is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: lib/Indexer/Model/QueryClient.php - - - - message: '#^Method Phpactor\\Indexer\\Model\\Record\\FileRecord\:\:identifier\(\) should return string but returns string\|null\.$#' - identifier: return.type - count: 1 - path: lib/Indexer/Model/Record/FileRecord.php - - - - message: '#^Parameter \#2 \$memberName of class Phpactor\\Indexer\\Model\\Record\\MemberRecord constructor expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Model/Record/MemberRecord.php - - - - message: '#^Method Phpactor\\Indexer\\Model\\RecordSerializer\\PhpSerializer\:\:deserialize\(\) should return Phpactor\\Indexer\\Model\\Record\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Indexer/Model/RecordSerializer/PhpSerializer.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\Indexer\\\\Model\\\\Record\\\\ClassRecord'' and Phpactor\\Indexer\\Model\\Record\\ClassRecord will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 5 - path: lib/Indexer/Tests/Adapter/IndexBuilderTestCase.php - - - - message: '#^Method Phpactor\\Indexer\\Tests\\Adapter\\IndexBuilderTestCase\:\:provideIndexesReferences\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Indexer/Tests/Adapter/IndexBuilderTestCase.php - - - - message: '#^Method Phpactor\\Indexer\\Tests\\Adapter\\Php\\FileSearchIndexTest\:\:search\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Indexer/Tests/Adapter/Php/FileSearchIndexTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 5 - path: lib/Indexer/Tests/Adapter/ReferenceFinder/IndexedNameSearcherTest.php - - - - message: '#^Instanceof between Phpactor\\ReferenceFinder\\Search\\NameSearchResult and Phpactor\\ReferenceFinder\\Search\\NameSearchResult will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 5 - path: lib/Indexer/Tests/Adapter/ReferenceFinder/IndexedNameSearcherTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Indexer/Tests/Adapter/Tolerant/Indexer/MemberIndexerTest.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Record\\MemberRecord and Phpactor\\Indexer\\Model\\Record\\MemberRecord will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Indexer/Tests/Adapter/Tolerant/Indexer/MemberIndexerTest.php - - - - message: '#^Parameter \#2 \$memberName of method Phpactor\\Indexer\\Model\\Query\\MemberQuery\:\:referencesTo\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Tests/Adapter/Tolerant/Indexer/MemberIndexerTest.php - - - - message: '#^Method Phpactor\\Indexer\\Tests\\Benchmark\\SearchBench\:\:benchBareFileSearch\(\) has parameter \$params with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Indexer/Tests/Benchmark/SearchBench.php - - - - message: '#^Method Phpactor\\Indexer\\Tests\\Benchmark\\SearchBench\:\:benchFullFileSearch\(\) has parameter \$params with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Indexer/Tests/Benchmark/SearchBench.php - - - - message: '#^Method Phpactor\\Indexer\\Tests\\Benchmark\\SearchBench\:\:provideSearches\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Indexer/Tests/Benchmark/SearchBench.php - - - - message: '#^Parameter \#1 \$name of class Phpactor\\Indexer\\Model\\Query\\Criteria\\ShortNameBeginsWith constructor expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/Indexer/Tests/Benchmark/SearchBench.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/Indexer/Tests/Extension/IndexerExtensionTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\Indexer\\\\Model\\\\Indexer'' and Phpactor\\Indexer\\Model\\Indexer will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 2 - path: lib/Indexer/Tests/Extension/IndexerExtensionTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\WorseReflection\\\\Core\\\\Reflection\\\\ReflectionClass'' and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Indexer/Tests/Extension/IndexerExtensionTest.php - - - - message: '#^Instanceof between Phpactor\\Indexer\\Model\\Indexer and Phpactor\\Indexer\\Model\\Indexer will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/Indexer/Tests/Extension/IndexerExtensionTest.php - - - - message: '#^Parameter \#1 \$request of method Phpactor\\Extension\\Rpc\\RequestHandler\:\:handle\(\) expects Phpactor\\Extension\\Rpc\\Request, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Tests/Extension/IndexerExtensionTest.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: lib/Indexer/Tests/IntegrationTestCase.php - - - - message: '#^Method Phpactor\\Indexer\\Tests\\IntegrationTestCase\:\:container\(\) has parameter \$config with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Indexer/Tests/IntegrationTestCase.php - - - - message: '#^Method Phpactor\\Indexer\\Tests\\IntegrationTestCase\:\:container\(\) should return Phpactor\\Container\\Container but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/Indexer/Tests/IntegrationTestCase.php - - - - message: '#^Parameter \#2 \$parameters of static method Phpactor\\Container\\PhpactorContainer\:\:fromExtensions\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Tests/IntegrationTestCase.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Tests/IntegrationTestCase.php - - - - message: '#^Method Phpactor\\Indexer\\Tests\\Unit\\Adapter\\ReferenceFinder\\Util\\ContainerTypeResolverTest\:\:testResolve\(\) has parameter \$manifest with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Indexer/Tests/Unit/Adapter/ReferenceFinder/Util/ContainerTypeResolverTest.php - - - - message: '#^Call to method Phpactor\\Indexer\\Model\\MemoryUsage\:\:memoryLimit\(\) on a separate line has no effect\.$#' - identifier: method.resultUnused - count: 1 - path: lib/Indexer/Tests/Unit/Model/MemoryUsageTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertIsInt\(\) with int will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Indexer/Tests/Unit/Model/MemoryUsageTest.php - - - - message: '#^Binary operation "\+\=" between \(float\|int\) and mixed results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: lib/Indexer/Util/Filesystem.php - - - - message: '#^Cannot call method getPathName\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Indexer/Util/Filesystem.php - - - - message: '#^Cannot call method getSize\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Indexer/Util/Filesystem.php - - - - message: '#^Invalid array key type float\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/Indexer/Util/Filesystem.php - - - - message: '#^Parameter \#1 \$path of static method Phpactor\\Indexer\\Util\\Filesystem\:\:removeDir\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Indexer/Util/Filesystem.php - - - - message: '#^Method Phpactor\\Name\\FullyQualifiedName\:\:fromArray\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/FullyQualifiedName.php - - - - message: '#^Method Phpactor\\Name\\FullyQualifiedName\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/FullyQualifiedName.php - - - - message: '#^Method Phpactor\\Name\\Name\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/Name.php - - - - message: '#^Class Phpactor\\Name\\Names implements generic interface IteratorAggregate but does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: lib/Name/Names.php - - - - message: '#^Method Phpactor\\Name\\Names\:\:fromNames\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Name/Names.php - - - - message: '#^Method Phpactor\\Name\\Names\:\:fromNames\(\) has parameter \$array with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/Names.php - - - - message: '#^Method Phpactor\\Name\\Names\:\:getIterator\(\) return type with generic class ArrayIterator does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: lib/Name/Names.php - - - - message: '#^Parameter \#1 \.\.\.\$names of class Phpactor\\Name\\Names constructor expects Phpactor\\Name\\Name, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Name/Names.php - - - - message: '#^Property Phpactor\\Name\\Names\:\:\$names type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/Names.php - - - - message: '#^Method Phpactor\\Name\\QualifiedName\:\:__construct\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/QualifiedName.php - - - - message: '#^Method Phpactor\\Name\\QualifiedName\:\:fromArray\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/QualifiedName.php - - - - message: '#^Method Phpactor\\Name\\QualifiedName\:\:toArray\(\) should return array\ but returns array\.$#' - identifier: return.type - count: 1 - path: lib/Name/QualifiedName.php - - - - message: '#^Property Phpactor\\Name\\QualifiedName\:\:\$parts type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/QualifiedName.php - - - - message: '#^Variable \$parts on left side of \?\? always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: lib/Name/QualifiedName.php - - - - message: '#^Method Phpactor\\Name\\Tests\\Unit\\AbstractQualifiedNameTestCase\:\:createFromArray\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/Tests/Unit/AbstractQualifiedNameTestCase.php - - - - message: '#^Method Phpactor\\Name\\Tests\\Unit\\AbstractQualifiedNameTestCase\:\:provideCreateFromArray\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Name/Tests/Unit/AbstractQualifiedNameTestCase.php - - - - message: '#^Method Phpactor\\Name\\Tests\\Unit\\AbstractQualifiedNameTestCase\:\:provideCreateFromString\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/Name/Tests/Unit/AbstractQualifiedNameTestCase.php - - - - message: '#^Method Phpactor\\Name\\Tests\\Unit\\AbstractQualifiedNameTestCase\:\:testCreateFromArray\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/Tests/Unit/AbstractQualifiedNameTestCase.php - - - - message: '#^Method Phpactor\\Name\\Tests\\Unit\\FullyQualifiedNameTest\:\:createFromArray\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/Tests/Unit/FullyQualifiedNameTest.php - - - - message: '#^Parameter \#2 \$haystack of static method PHPUnit\\Framework\\Assert\:\:assertCount\(\) expects Countable\|iterable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Name/Tests/Unit/NamesTest.php - - - - message: '#^Method Phpactor\\Name\\Tests\\Unit\\QualifiedNameTest\:\:createFromArray\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/Name/Tests/Unit/QualifiedNameTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/PathFinder/PathFinder.php - - - - message: '#^Instanceof between Phpactor\\PathFinder\\Pattern and Phpactor\\PathFinder\\Pattern will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/PathFinder/PathFinder.php - - - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' - identifier: arrayValues.list - count: 1 - path: lib/PathFinder/Pattern.php - - - - message: '#^Method Phpactor\\PathFinder\\Tests\\Unit\\PathFinderTest\:\:testTeleport\(\) has parameter \$expectedTargets with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/PathFinder/Tests/Unit/PathFinderTest.php - - - - message: '#^Method Phpactor\\PathFinder\\Tests\\Unit\\PathFinderTest\:\:testTeleport\(\) has parameter \$targets with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/PathFinder/Tests/Unit/PathFinderTest.php - - - - message: '#^Parameter \#2 \$destinations of static method Phpactor\\PathFinder\\PathFinder\:\:fromAbsoluteDestinations\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/PathFinder/Tests/Unit/PathFinderTest.php - - - - message: '#^Cannot access an offset on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/Phpactor.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 1 - path: lib/Phpactor.php - - - - message: '#^Instanceof between Phpactor\\ClassMover\\Extension\\ClassMoverExtension\|Phpactor\\Extension\\Behat\\BehatExtension\|Phpactor\\Extension\\Behat\\BehatSuggestExtension\|Phpactor\\Extension\\ClassMover\\ClassMoverExtension\|Phpactor\\Extension\\ClassToFile\\ClassToFileExtension\|Phpactor\\Extension\\ClassToFileExtra\\ClassToFileExtraExtension\|Phpactor\\Extension\\CodeTransform\\CodeTransformExtension\|Phpactor\\Extension\\CodeTransformExtra\\CodeTransformExtraExtension\|Phpactor\\Extension\\Completion\\CompletionExtension\|Phpactor\\Extension\\CompletionExtra\\CompletionExtraExtension\|Phpactor\\Extension\\CompletionRpc\\CompletionRpcExtension\|Phpactor\\Extension\\CompletionWorse\\CompletionWorseExtension\|Phpactor\\Extension\\ComposerAutoloader\\ComposerAutoloaderExtension\|Phpactor\\Extension\\ComposerInspector\\ComposerInspectorExtension\|Phpactor\\Extension\\Configuration\\ConfigurationExtension\|Phpactor\\Extension\\Console\\ConsoleExtension\|Phpactor\\Extension\\ContextMenu\\ContextMenuExtension\|Phpactor\\Extension\\Core\\CoreExtension\|Phpactor\\Extension\\Debug\\DebugExtension\|Phpactor\\Extension\\FilePathResolver\\FilePathResolverExtension\|Phpactor\\Extension\\LanguageServer\\LanguageServerExtension\|Phpactor\\Extension\\LanguageServerBlackfire\\LanguageServerBlackfireExtension\|Phpactor\\Extension\\LanguageServerBridge\\LanguageServerBridgeExtension\|Phpactor\\Extension\\LanguageServerCodeTransform\\LanguageServerCodeTransformExtension\|Phpactor\\Extension\\LanguageServerCompletion\\LanguageServerCompletionExtension\|Phpactor\\Extension\\LanguageServerConfiguration\\LanguageServerConfigurationExtension\|Phpactor\\Extension\\LanguageServerDiagnostics\\LanguageServerDiagnosticsExtension\|Phpactor\\Extension\\LanguageServerEvaluatableExpression\\LanguageServerEvaluatableExpressionExtension\|Phpactor\\Extension\\LanguageServerHighlight\\LanguageServerHighlightExtension\|Phpactor\\Extension\\LanguageServerHover\\LanguageServerHoverExtension\|Phpactor\\Extension\\LanguageServerIndexer\\LanguageServerIndexerExtension\|Phpactor\\Extension\\LanguageServerInlineValue\\LanguageServerInlineValueExtension\|Phpactor\\Extension\\LanguageServerPhpCsFixer\\LanguageServerPhpCsFixerExtension\|Phpactor\\Extension\\LanguageServerPhpCsFixer\\LanguageServerPhpCsFixerSuggestExtension\|Phpactor\\Extension\\LanguageServerPhpstan\\LanguageServerPhpstanExtension\|Phpactor\\Extension\\LanguageServerPhpstan\\LanguageServerPhpstanSuggestExtension\|Phpactor\\Extension\\LanguageServerPsalm\\LanguageServerPsalmExtension\|Phpactor\\Extension\\LanguageServerPsalm\\LanguageServerPsalmSuggestExtension\|Phpactor\\Extension\\LanguageServerReferenceFinder\\LanguageServerReferenceFinderExtension\|Phpactor\\Extension\\LanguageServerRename\\LanguageServerRenameExtension\|Phpactor\\Extension\\LanguageServerRename\\LanguageServerRenameWorseExtension\|Phpactor\\Extension\\LanguageServerSelectionRange\\LanguageServerSelectionRangeExtension\|Phpactor\\Extension\\LanguageServerSymbolProvider\\LanguageServerSymbolProviderExtension\|Phpactor\\Extension\\LanguageServerWorseReflection\\LanguageServerWorseReflectionExtension\|Phpactor\\Extension\\Logger\\LoggingExtension\|Phpactor\\Extension\\Navigation\\NavigationExtension\|Phpactor\\Extension\\ObjectRenderer\\ObjectRendererExtension\|Phpactor\\Extension\\OpenTelemetry\\OpenTelemetryExtension\|Phpactor\\Extension\\Php\\PhpExtension\|Phpactor\\Extension\\PhpCodeSniffer\\PhpCodeSnifferExtension\|Phpactor\\Extension\\PhpCodeSniffer\\PhpCodeSnifferSuggestExtension\|Phpactor\\Extension\\PHPUnit\\PHPUnitExtension\|Phpactor\\Extension\\Prophecy\\ProphecyExtension\|Phpactor\\Extension\\Prophecy\\ProphecySuggestExtension\|Phpactor\\Extension\\ReferenceFinder\\ReferenceFinderExtension\|Phpactor\\Extension\\ReferenceFinderRpc\\ReferenceFinderRpcExtension\|Phpactor\\Extension\\Rpc\\RpcExtension\|Phpactor\\Extension\\SourceCodeFilesystem\\SourceCodeFilesystemExtension\|Phpactor\\Extension\\SourceCodeFilesystemExtra\\SourceCodeFilesystemExtraExtension\|Phpactor\\Extension\\Symfony\\SymfonyExtension\|Phpactor\\Extension\\Symfony\\SymfonySuggestExtension\|Phpactor\\Extension\\WorseReferenceFinder\\WorseReferenceFinderExtension\|Phpactor\\Extension\\WorseReflection\\WorseReflectionExtension\|Phpactor\\Extension\\WorseReflectionAnalyse\\WorseReflectionAnalyseExtension\|Phpactor\\Extension\\WorseReflectionExtra\\WorseReflectionExtraExtension\|Phpactor\\Indexer\\Extension\\IndexerExtension and Phpactor\\Container\\Extension will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Phpactor.php - - - - message: '#^Parameter \#1 \$config of method Phpactor\\MapResolver\\Resolver\:\:resolve\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Phpactor.php - - - - message: '#^Parameter \#1 \$config of static method Phpactor\\Phpactor\:\:configureLanguageServer\(\) expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/Phpactor.php - - - - message: '#^Parameter \#1 \$minimumMemoryLimit of static method Phpactor\\Phpactor\:\:updateMinMemory\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Phpactor.php - - - - message: '#^Parameter \#1 \$string of function substr expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Phpactor.php - - - - message: '#^Dead catch \- Phpactor\\ReferenceFinder\\Exception\\UnsupportedDocument is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 1 - path: lib/ReferenceFinder/ChainDefinitionLocationProvider.php - - - - message: '#^Method Phpactor\\ReferenceFinder\\ChainDefinitionLocationProvider\:\:__construct\(\) has parameter \$providers with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ReferenceFinder/ChainDefinitionLocationProvider.php - - - - message: '#^Parameter \#1 \$provider of method Phpactor\\ReferenceFinder\\ChainDefinitionLocationProvider\:\:add\(\) expects Phpactor\\ReferenceFinder\\DefinitionLocator, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ReferenceFinder/ChainDefinitionLocationProvider.php - - - - message: '#^Method Phpactor\\ReferenceFinder\\ChainReferenceFinder\:\:__construct\(\) has parameter \$finders with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/ReferenceFinder/ChainReferenceFinder.php - - - - message: '#^Parameter \#1 \$finder of method Phpactor\\ReferenceFinder\\ChainReferenceFinder\:\:add\(\) expects Phpactor\\ReferenceFinder\\ReferenceFinder, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/ReferenceFinder/ChainReferenceFinder.php - - - - message: '#^Property Phpactor\\ReferenceFinder\\Tests\\Unit\\ChainReferenceFinderTest\:\:\$locator1 \(Prophecy\\Prophecy\\ObjectProphecy\\) does not accept Prophecy\\Prophecy\\ObjectProphecy\\.$#' - identifier: assign.propertyType - count: 1 - path: lib/ReferenceFinder/Tests/Unit/ChainReferenceFinderTest.php - - - - message: '#^Property Phpactor\\ReferenceFinder\\Tests\\Unit\\ChainReferenceFinderTest\:\:\$locator1 has unknown class Phpactor\\ReferenceFinder\\ClassReferenceFinder as its type\.$#' - identifier: class.notFound - count: 1 - path: lib/ReferenceFinder/Tests/Unit/ChainReferenceFinderTest.php - - - - message: '#^Property Phpactor\\ReferenceFinder\\Tests\\Unit\\ChainReferenceFinderTest\:\:\$locator2 \(Prophecy\\Prophecy\\ObjectProphecy\\) does not accept Prophecy\\Prophecy\\ObjectProphecy\\.$#' - identifier: assign.propertyType - count: 1 - path: lib/ReferenceFinder/Tests/Unit/ChainReferenceFinderTest.php - - - - message: '#^Property Phpactor\\ReferenceFinder\\Tests\\Unit\\ChainReferenceFinderTest\:\:\$locator2 has unknown class Phpactor\\ReferenceFinder\\ClassReferenceFinder as its type\.$#' - identifier: class.notFound - count: 1 - path: lib/ReferenceFinder/Tests/Unit/ChainReferenceFinderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\ReferenceFinder\\\\Search\\\\NameSearchResult'' and Phpactor\\ReferenceFinder\\Search\\NameSearchResult will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 2 - path: lib/ReferenceFinder/Tests/Unit/Search/NameSearchResultTest.php - - - - message: '#^Method Phpactor\\Rename\\Adapter\\ClassMover\\FileRenamer\:\:renameFile\(\) should return Amp\\Promise\ but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Rename/Adapter/ClassMover/FileRenamer.php - - - - message: '#^Cannot call method __toString\(\) on Phpactor\\TextDocument\\TextDocumentUri\|null\.$#' - identifier: method.nonObject - count: 2 - path: lib/Rename/Adapter/ReferenceFinder/AbstractReferenceRenamer.php - - - - message: '#^Parameter \#1 \$array of function array_pop expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Rename/Adapter/ReferenceFinder/ClassMover/ClassRenamer.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Rename/Adapter/ReferenceFinder/ClassMover/ClassRenamer.php - - - - message: '#^Cannot access property \$start on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/Rename/Adapter/ReferenceFinder/MemberRenamer.php - - - - message: '#^Cannot call method getEndPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Rename/Adapter/ReferenceFinder/MemberRenamer.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Token and Microsoft\\PhpParser\\Token will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Rename/Adapter/Tolerant/TokenUtil.php - - - - message: '#^Method Phpactor\\Rename\\Model\\FileRenamer\\LoggingFileRenamer\:\:renameFile\(\) should return Amp\\Promise\ but returns Amp\\Promise\\.$#' - identifier: return.type - count: 1 - path: lib/Rename/Model/FileRenamer/LoggingFileRenamer.php - - - - message: '#^Method Phpactor\\Rename\\Model\\FileRenamer\\TestFileRenamer\:\:renameFile\(\) should return Amp\\Promise\ but returns Amp\\Failure\\.$#' - identifier: return.type - count: 1 - path: lib/Rename/Model/FileRenamer/TestFileRenamer.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/Rename/Tests/Adapter/ClassMover/FileRenamerTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\Rename\\\\Model\\\\LocatedTextEditsMap'' and Phpactor\\Rename\\Model\\LocatedTextEditsMap will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Rename/Tests/Adapter/ClassMover/FileRenamerTest.php - - - - message: '#^Cannot call method path\(\) on Phpactor\\TextDocument\\TextDocumentUri\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/Rename/Tests/Adapter/ClassMover/FileRenamerTest.php - - - - message: '#^Instanceof between Phpactor\\Rename\\Model\\LocatedTextEditsMap and Phpactor\\Rename\\Model\\LocatedTextEditsMap will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Rename/Tests/Adapter/ClassMover/FileRenamerTest.php - - - - message: '#^Instanceof between Phpactor\\TextDocument\\TextDocument and Phpactor\\TextDocument\\TextDocument will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Rename/Tests/Adapter/ClassMover/FileRenamerTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\TextDocument\\\\TextDocumentUri'' and Phpactor\\TextDocument\\TextDocumentUri will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/Rename/Tests/Adapter/ClassToFile/ClassToFileNameToUriConverterTest.php - - - - message: '#^Cannot call method __toString\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Rename/Tests/Adapter/ReferenceFinder/ClassMover/ClassRenamerTest.php - - - - message: '#^Cannot call method newUri\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/Rename/Tests/Adapter/ReferenceFinder/ClassMover/ClassRenamerTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Rename/Tests/Adapter/ReferenceFinder/MemberRenamerTest.php - - - - message: '#^Instanceof between Phpactor\\TextDocument\\ByteOffsetRange and Phpactor\\TextDocument\\ByteOffsetRange will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Rename/Tests/Adapter/ReferenceFinder/MemberRenamerTest.php - - - - message: '#^Parameter \#1 \$uri of class Phpactor\\TextDocument\\Location constructor expects Phpactor\\TextDocument\\TextDocumentUri, Phpactor\\TextDocument\\TextDocumentUri\|null given\.$#' - identifier: argument.type - count: 2 - path: lib/Rename/Tests/Adapter/ReferenceFinder/MemberRenamerTest.php - - - - message: '#^Parameter \#2 \$documentUri of class Phpactor\\Rename\\Model\\LocatedTextEdits constructor expects Phpactor\\TextDocument\\TextDocumentUri, Phpactor\\TextDocument\\TextDocumentUri\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Rename/Tests/Adapter/ReferenceFinder/MemberRenamerTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Rename/Tests/Adapter/ReferenceFinder/VariableRenamerTest.php - - - - message: '#^Instanceof between Phpactor\\TextDocument\\ByteOffsetRange and Phpactor\\TextDocument\\ByteOffsetRange will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Rename/Tests/Adapter/ReferenceFinder/VariableRenamerTest.php - - - - message: '#^Parameter \#2 \$documentUri of class Phpactor\\Rename\\Model\\LocatedTextEdits constructor expects Phpactor\\TextDocument\\TextDocumentUri, Phpactor\\TextDocument\\TextDocumentUri\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/Rename/Tests/Adapter/ReferenceFinder/VariableRenamerTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/Rename/Tests/Integration/Adapter/ReferenceFinder/MemberRenamerTest.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\AbstractReflectionMethodCall and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionMethodCall will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/Rename/Tests/Integration/Adapter/ReferenceFinder/MemberRenamerTest.php - - - - message: '#^@dataProvider provideRename related method not found\.$#' - identifier: phpunit.dataProviderMethod - count: 1 - path: lib/Rename/Tests/RenamerTestCase.php - - - - message: '#^Parameter \#1 \$edits of static method Phpactor\\Rename\\Model\\LocatedTextEdits\:\:fromLocatedEditsToCollection\(\) expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: lib/Rename/Tests/RenamerTestCase.php - - - - message: '#^Parameter \#1 \$iterator of function iterator_to_array expects iterable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/Rename/Tests/RenamerTestCase.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\TextDocument\\\\EfficientLineCols'' and Phpactor\\TextDocument\\EfficientLineCols will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 1 - path: lib/TextDocument/Tests/Unit/EfficientLineColsTest.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/TextDocument/Tests/Unit/LineColTest.php - - - - message: '#^Instanceof between Phpactor\\TextDocument\\LineCol and Phpactor\\TextDocument\\LineCol will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/TextDocument/Tests/Unit/LineColTest.php - - - - message: '#^Cannot call method __toString\(\) on Phpactor\\TextDocument\\TextDocumentUri\|null\.$#' - identifier: method.nonObject - count: 2 - path: lib/TextDocument/Tests/Unit/TextDocumentBuilderTest.php - - - - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Phpactor\\\\TextDocument\\\\TextEdits'' and Phpactor\\TextDocument\\TextEdits will always evaluate to true\.$#' - identifier: staticMethod.alreadyNarrowedType - count: 2 - path: lib/TextDocument/Tests/Unit/TextEditsTest.php - - - - message: '#^Cannot call method __toString\(\) on Phpactor\\TextDocument\\TextDocumentUri\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/TextDocument/TextDocumentLocator/InMemoryDocumentLocator.php - - - - message: '#^Offset ''path'' might not exist on array\{0\?\: string, scheme\?\: non\-falsy\-string\|null, 1\?\: non\-falsy\-string\|null, path\?\: non\-empty\-string\|null, 2\?\: non\-empty\-string\|null\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/TextDocument/TextDocumentUri.php - - - - message: '#^Offset ''scheme'' might not exist on array\{0\?\: string, scheme\?\: non\-falsy\-string\|null, 1\?\: non\-falsy\-string\|null, path\?\: non\-empty\-string\|null, 2\?\: non\-empty\-string\|null\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: lib/TextDocument/TextDocumentUri.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/TextDocument/TextEdits.php - - - - message: '#^Instanceof between Phpactor\\TextDocument\\TextEdit and Phpactor\\TextDocument\\TextEdit will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/TextDocument/TextEdits.php - - - - message: '#^Property Phpactor\\VersionResolver\\CachedSemVerResolver\:\:\$version \(Phpactor\\VersionResolver\\SemVersion\|null\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: lib/VersionResolver/CachedSemVerResolver.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/WorseReferenceFinder/TolerantVariableDefintionLocator.php - - - - message: '#^Instanceof between Phpactor\\ReferenceFinder\\PotentialLocation and Phpactor\\ReferenceFinder\\PotentialLocation will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReferenceFinder/TolerantVariableDefintionLocator.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReferenceFinder/TolerantVariableReferenceFinder.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Token and Microsoft\\PhpParser\\Token will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReferenceFinder/TolerantVariableReferenceFinder.php - - - - message: '#^Method Phpactor\\WorseReferenceFinder\\TolerantVariableReferenceFinder\:\:variableName\(\) should return string\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReferenceFinder/TolerantVariableReferenceFinder.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/WorseReferenceFinder/WorsePlainTextClassDefinitionLocator.php - - - - message: '#^Cannot call method __toString\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReferenceFinder/WorsePlainTextClassDefinitionLocator.php - - - - message: '#^Method Phpactor\\WorseReferenceFinder\\WorsePlainTextClassDefinitionLocator\:\:resolveClassName\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReferenceFinder/WorsePlainTextClassDefinitionLocator.php - - - - message: '#^Method Phpactor\\WorseReferenceFinder\\WorsePlainTextClassDefinitionLocator\:\:resolveImportTable\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/WorseReferenceFinder/WorsePlainTextClassDefinitionLocator.php - - - - message: '#^Cannot call method name\(\) on Phpactor\\WorseReflection\\Core\\Type\\ClassType\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReferenceFinder/WorseReflectionDefinitionLocator.php - - - - message: '#^Parameter \#1 \$type of class Phpactor\\ReferenceFinder\\TypeLocation constructor expects Phpactor\\WorseReflection\\Core\\Type, Phpactor\\WorseReflection\\Core\\Type\\ClassType\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReferenceFinder/WorseReflectionDefinitionLocator.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 2 - path: lib/WorseReflection/Bridge/Phpactor/ClassToFileSourceLocator.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 13 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\DeprecatedTag and Phpactor\\DocblockParser\\Ast\\Tag\\DeprecatedTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\ExtendsTag and Phpactor\\DocblockParser\\Ast\\Tag\\ExtendsTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\ImplementsTag and Phpactor\\DocblockParser\\Ast\\Tag\\ImplementsTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\MethodTag and Phpactor\\DocblockParser\\Ast\\Tag\\MethodTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\MixinTag and Phpactor\\DocblockParser\\Ast\\Tag\\MixinTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\ParamTag and Phpactor\\DocblockParser\\Ast\\Tag\\ParamTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\ParameterTag and Phpactor\\DocblockParser\\Ast\\Tag\\ParameterTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\PropertyTag and Phpactor\\DocblockParser\\Ast\\Tag\\PropertyTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\ReturnTag and Phpactor\\DocblockParser\\Ast\\Tag\\ReturnTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\TemplateTag and Phpactor\\DocblockParser\\Ast\\Tag\\TemplateTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Tag\\VarTag and Phpactor\\DocblockParser\\Ast\\Tag\\VarTag will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\TypeNode and Phpactor\\DocblockParser\\Ast\\TypeNode will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Parameter \#1 \$string of function ltrim expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Parameter \#9 \$index of class Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionParameter constructor expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/ParsedDocblock.php - - - - message: '#^Cannot call method resolveFullyQualifiedName\(\) on Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionScope\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/Phpactor/DocblockParser/TypeConverter.php - - - - message: '#^Cannot access an offset on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/WorseReflection/Bridge/PsrLog/ArrayLogger.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\PsrLog\\ArrayLogger\:\:messages\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/WorseReflection/Bridge/PsrLog/ArrayLogger.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\PsrLog\\ArrayLogger\:\:messages\(\) should return array but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Bridge/PsrLog/ArrayLogger.php - - - - message: '#^Property Phpactor\\WorseReflection\\Bridge\\PsrLog\\ArrayLogger\:\:\$messages has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/WorseReflection/Bridge/PsrLog/ArrayLogger.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\Expression and Microsoft\\PhpParser\\Node\\Expression will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/AssignmentToMissingPropertyProvider.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Token and Microsoft\\PhpParser\\Token will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/AssignmentToMissingPropertyProvider.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\WorseReflection\\Core\\Inference\\Assignments\:\:byName\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php - - - - message: '#^Parameter \#1 \$str1 of function levenshtein expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php - - - - message: '#^Parameter \#2 \$varName of class Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Diagnostics\\UndefinedVariableDiagnostic constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php - - - - message: '#^Parameter \#3 \$suggestions of class Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Diagnostics\\UndefinedVariableDiagnostic constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnresolvableNameProvider.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportProvider.php - - - - message: '#^Cannot access property \$parent on Microsoft\\PhpParser\\Node\|null\.$#' - identifier: property.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportProvider.php - - - - message: '#^Cannot call method toString\(\) on Phpactor\\DocblockParser\\Ast\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportProvider.php - - - - message: '#^Instanceof between Phpactor\\DocblockParser\\Ast\\Type\\CallableNode and Phpactor\\DocblockParser\\Ast\\Type\\CallableNode will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UnusedImportProvider.php - - - - message: '#^Cannot access offset 0 on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot access property \$name on mixed\.$#' - identifier: property.nonObject - count: 2 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot access property \$nameParts on mixed\.$#' - identifier: property.nonObject - count: 5 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot access property \$parent on mixed\.$#' - identifier: property.nonObject - count: 19 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method getFileContents\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method getImportTablesForCurrentScope\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method getNamespaceDefinition\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method getNamespacedName\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method getStartPosition\(\) on Microsoft\\PhpParser\\MissingToken\|Microsoft\\PhpParser\\Node\\QualifiedName\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method getStartPosition\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method getText\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method isFullyQualifiedName\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method isQualifiedName\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot call method isRelativeName\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Cannot use array destructuring on mixed\.$#' - identifier: offsetAccess.nonArray - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Expression on left side of \?\? is always null\.$#' - identifier: nullCoalesce.expr - count: 2 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Instanceof between mixed and Microsoft\\PhpParser\\Node\\TraitSelectOrAliasClause will always evaluate to false\.$#' - identifier: instanceof.alwaysFalse - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Patch\\TolerantQualifiedNameResolver\:\:getResolvedName\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Patch\\TolerantQualifiedNameResolver\:\:getResolvedName\(\) has parameter \$namespaceDefinition with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Patch\\TolerantQualifiedNameResolver\:\:getResolvedName\(\) has parameter \$node with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Patch\\TolerantQualifiedNameResolver\:\:isConstantName\(\) has parameter \$node with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Patch\\TolerantQualifiedNameResolver\:\:tryResolveFromImportTable\(\) has parameter \$node with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Patch\\TolerantQualifiedNameResolver\:\:tryResolveFromImportTable\(\) should return null but returns Microsoft\\PhpParser\\ResolvedName\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Parameter \#1 \$array of function array_slice expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Parameter \#1 \$string of function strtolower expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Parameter \#1 \$tokens of static method Microsoft\\PhpParser\\ResolvedName\:\:buildName\(\) expects array\, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Parameter \#2 \$importTable of static method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Patch\\TolerantQualifiedNameResolver\:\:tryResolveFromImportTable\(\) expects array\, mixed given\.$#' - identifier: argument.type - count: 4 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 2 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Result of && is always false\.$#' - identifier: booleanAnd.alwaysFalse - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Variable \$resolvedName on left side of \?\? always exists and is always null\.$#' - identifier: nullCoalesce.variable - count: 2 - path: lib/WorseReflection/Bridge/TolerantParser/Patch/TolerantQualifiedNameResolver.php - - - - message: '#^Strict comparison using \=\=\= between null and Microsoft\\PhpParser\\ResolvedName will always evaluate to false\.$#' - identifier: identical.alwaysFalse - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/AbstractReflectionClassMember.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\ClassBaseClause and Microsoft\\PhpParser\\Node\\ClassBaseClause will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionClass.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\ClassInterfaceClause and Microsoft\\PhpParser\\Node\\ClassInterfaceClause will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionClass.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\QualifiedName and Microsoft\\PhpParser\\Node\\QualifiedName will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionClass.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionConstant.php - - - - message: '#^Call to function is_array\(\) with array\ will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionDeclaredConstant.php - - - - message: '#^Parameter \#1 \$parts of static method Phpactor\\WorseReflection\\Core\\Name\:\:fromParts\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionFunction.php - - - - message: '#^Cannot access an offset on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethod.php - - - - message: '#^Cannot call method getEndPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethod.php - - - - message: '#^Cannot call method getStartPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethod.php - - - - message: '#^Parameter \#2 \$array of function implode expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethod.php - - - - message: '#^Property Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\ReflectionMethodCall\:\:\$node is unused\.$#' - identifier: property.unused - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionMethodCall.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\ReflectionParameter\:\:name\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionParameter.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionPromotedProperty.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionProperty.php - - - - message: '#^Parameter \#1 \$parts of static method Phpactor\\WorseReflection\\Core\\Name\:\:fromParts\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionScope.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\TraitImport\\TraitImport\:\:__construct\(\) has parameter \$traitAliases with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\TraitImport\\TraitImport\:\:getAlias\(\) has parameter \$name with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\TraitImport\\TraitImport\:\:getAlias\(\) should return Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\TraitImport\\TraitAlias but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\TraitImport\\TraitImport\:\:hasAliasFor\(\) has parameter \$name with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\TraitImport\\TraitImport\:\:traitAliases\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php - - - - message: '#^Parameter \#1 \$key of function array_key_exists expects int\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImport.php - - - - message: '#^Argument of an invalid type Microsoft\\PhpParser\\Node\\DelimitedList\\TraitSelectOrAliasClauseList supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImports.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImports.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImports.php - - - - message: '#^Method Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\TraitImport\\TraitImports\:\:visiblity\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImports.php - - - - message: '#^Parameter \#2 \$visiblity of class Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\TraitImport\\TraitAlias constructor expects Phpactor\\WorseReflection\\Core\\Visibility\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TraitImport/TraitImports.php - - - - message: '#^Parameter \#2 \$node of static method Phpactor\\WorseReflection\\Core\\Util\\NodeUtil\:\:typeFromQualfiedNameLike\(\) expects Microsoft\\PhpParser\\Node, Microsoft\\PhpParser\\Node\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolver.php - - - - message: '#^PHPDoc tag @var above a method has no effect\.$#' - identifier: varTag.misplaced - count: 1 - path: lib/WorseReflection/Core/Cache/TtlCache.php - - - - message: '#^Parameter \#2 \$resolved of method Phpactor\\WorseReflection\\Core\\ClassHierarchyResolver\:\:doResolve\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 5 - path: lib/WorseReflection/Core/ClassHierarchyResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\DefaultValue\:\:__construct\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/DefaultValue.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\DefaultValue\:\:fromValue\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/DefaultValue.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\DefaultValue\:\:value\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/WorseReflection/Core/DefaultValue.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\DefaultValue\:\:\$undefined has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/WorseReflection/Core/DefaultValue.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\Context\\FunctionCallContext\:\:arguments\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: lib/WorseReflection/Core/Inference/Context/FunctionCallContext.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Inference\\Frame\\ConcreteFrame\:\:\$properties \(Phpactor\\WorseReflection\\Core\\Inference\\PropertyAssignments\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: lib/WorseReflection/Core/Inference/Frame/ConcreteFrame.php - - - - message: '#^Parameter \#1 \$node of method Phpactor\\WorseReflection\\Core\\Inference\\FrameResolver\:\:resolveScopeNode\(\) expects Microsoft\\PhpParser\\Node, Microsoft\\PhpParser\\Node\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/FrameResolver.php - - - - message: '#^Cannot call method short\(\) on Phpactor\\WorseReflection\\Core\\ClassName\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/GenericMapResolver.php - - - - message: '#^Parameter \#1 \$index of method Phpactor\\WorseReflection\\Core\\Types\\:\:at\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/GenericMapResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\LocalAssignments\:\:fromArray\(\) has parameter \$assignments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/WorseReflection/Core/Inference/LocalAssignments.php - - - - message: '#^Parameter \#1 \$variables of class Phpactor\\WorseReflection\\Core\\Inference\\LocalAssignments constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/LocalAssignments.php - - - - message: '#^Cannot call method get\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/MemberTypeResolver.php - - - - message: '#^Cannot call method has\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/MemberTypeResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\MemberTypeResolver\:\:constantType\(\) should return Phpactor\\WorseReflection\\Core\\Inference\\NodeContext but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Inference/MemberTypeResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\MemberTypeResolver\:\:memberType\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/WorseReflection/Core/Inference/MemberTypeResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\MemberTypeResolver\:\:methodType\(\) should return Phpactor\\WorseReflection\\Core\\Inference\\NodeContext but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Inference/MemberTypeResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\MemberTypeResolver\:\:propertyType\(\) should return Phpactor\\WorseReflection\\Core\\Inference\\NodeContext but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Inference/MemberTypeResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\NodeContext\:\:scope\(\) should return Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionScope but returns Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionScope\|null\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Inference/NodeContext.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Inference\\NodeContext\:\:\$type on left side of \?\? is not nullable nor uninitialized\.$#' - identifier: nullCoalesce.initializedProperty - count: 1 - path: lib/WorseReflection/Core/Inference/NodeContext.php - - - - message: '#^Parameter \#1 \$type of static method Phpactor\\WorseReflection\\Core\\TypeFactory\:\:fromStringWithReflector\(\) expects string, Microsoft\\PhpParser\\ResolvedName\|string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/NodeToTypeConverter.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\PropertyAssignments\:\:create\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/WorseReflection/Core/Inference/PropertyAssignments.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\PropertyAssignments\:\:fromArray\(\) has parameter \$assignments with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/WorseReflection/Core/Inference/PropertyAssignments.php - - - - message: '#^Parameter \#1 \$variables of class Phpactor\\WorseReflection\\Core\\Inference\\PropertyAssignments constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/PropertyAssignments.php - - - - message: '#^Cannot access property \$elementKey on mixed\.$#' - identifier: property.nonObject - count: 2 - path: lib/WorseReflection/Core/Inference/Resolver/ArrayCreationExpressionResolver.php - - - - message: '#^Cannot access property \$elementValue on mixed\.$#' - identifier: property.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/ArrayCreationExpressionResolver.php - - - - message: '#^Parameter \#2 \$node of method Phpactor\\WorseReflection\\Core\\Inference\\NodeContextResolver\:\:resolveNode\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/WorseReflection/Core/Inference/Resolver/ArrayCreationExpressionResolver.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/ArrayCreationExpressionResolver.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\DelimitedList\\ArrayElementList and Microsoft\\PhpParser\\Node\\DelimitedList\\ArrayElementList will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 2 - path: lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\DelimitedList\\ListExpressionList and Microsoft\\PhpParser\\Node\\DelimitedList\\ListExpressionList will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Token and Microsoft\\PhpParser\\Token will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php - - - - message: '#^Parameter \#1 \$key of method Phpactor\\WorseReflection\\Core\\Type\\ArrayLiteral\:\:set\(\) expects \(int\|string\), mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/ConstElementResolver.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/ForeachStatementResolver.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\DelimitedList\\ArrayElementList and Microsoft\\PhpParser\\Node\\DelimitedList\\ArrayElementList will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/ForeachStatementResolver.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\ForeachValue and Microsoft\\PhpParser\\Node\\ForeachValue will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/ForeachStatementResolver.php - - - - message: '#^Cannot call method getEndPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/FunctionDeclarationResolver.php - - - - message: '#^Cannot call method getStartPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/FunctionDeclarationResolver.php - - - - message: '#^Cannot call method getText\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/FunctionDeclarationResolver.php - - - - message: '#^Parameter \#1 \$symbolName of static method Phpactor\\WorseReflection\\Core\\Inference\\NodeContextFactory\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/GlobalDeclarationResolver.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\Expression and Microsoft\\PhpParser\\Node\\Expression will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/IfStatementResolver.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/MemberAccess/NodeContextFromMemberAccess.php - - - - message: '#^Cannot call method getEndPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/MethodDeclarationResolver.php - - - - message: '#^Cannot call method getStartPosition\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/MethodDeclarationResolver.php - - - - message: '#^Cannot call method getText\(\) on Microsoft\\PhpParser\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/MethodDeclarationResolver.php - - - - message: '#^Parameter \#2 \$node of method Phpactor\\WorseReflection\\Core\\Inference\\NodeContextResolver\:\:resolveNode\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token, Microsoft\\PhpParser\\Node\\Statement\\ClassDeclaration\|Microsoft\\PhpParser\\Node\\Statement\\InterfaceDeclaration\|Microsoft\\PhpParser\\Node\\Statement\\TraitDeclaration\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/MethodDeclarationResolver.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 2 - path: lib/WorseReflection/Core/Inference/Resolver/ParameterResolver.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\:\:get\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/ParameterResolver.php - - - - message: '#^Parameter \#1 \$name of method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\:\:has\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/ParameterResolver.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/WorseReflection/Core/Inference/Resolver/ParameterResolver.php - - - - message: '#^Cannot use \+\+ on mixed\.$#' - identifier: preInc.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/PostfixUpdateExpressionResolver.php - - - - message: '#^Cannot use \-\- on mixed\.$#' - identifier: preDec.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/PostfixUpdateExpressionResolver.php - - - - message: '#^Parameter \#2 \$importTable of static method Phpactor\\WorseReflection\\Core\\Util\\NodeUtil\:\:resolveNameFromImportTable\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/QualifiedNameResolver.php - - - - message: '#^Parameter \#1 \$offset of method Phpactor\\WorseReflection\\Core\\Type\\ArrayAccessType\:\:typeAtOffset\(\) expects \(int\|string\), mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/SubscriptExpressionResolver.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/UseVariableNameResolver.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/VariableResolver.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/VariableResolver.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Inference\\Variable and Phpactor\\WorseReflection\\Core\\Inference\\Variable will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/VariableResolver.php - - - - message: '#^Parameter \#1 \$symbolName of static method Phpactor\\WorseReflection\\Core\\Inference\\NodeContextFactory\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Resolver/VariableResolver.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Inference\\TypeAssertion\:\:apply\(\) should return Phpactor\\WorseReflection\\Core\\Type but returns mixed\.$#' - identifier: return.type - count: 2 - path: lib/WorseReflection/Core/Inference/TypeAssertion.php - - - - message: '#^Parameter \#1 \$typeAssertions of class Phpactor\\WorseReflection\\Core\\Inference\\TypeAssertions constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/TypeAssertions.php - - - - message: '#^Cannot access property \$variableName on mixed\.$#' - identifier: property.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php - - - - message: '#^Cannot call method getEndPosition\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php - - - - message: '#^Cannot call method getStartPosition\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php - - - - message: '#^Cannot call method getText\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php - - - - message: '#^Parameter \#1 \$symbolName of static method Phpactor\\WorseReflection\\Core\\Inference\\NodeContextFactory\:\:create\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php - - - - message: '#^Parameter \#2 \$start of static method Phpactor\\WorseReflection\\Core\\Inference\\NodeContextFactory\:\:create\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php - - - - message: '#^Parameter \#3 \$end of static method Phpactor\\WorseReflection\\Core\\Inference\\NodeContextFactory\:\:create\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/FunctionLikeWalker.php - - - - message: '#^Parameter \#2 \$node of method Phpactor\\WorseReflection\\Core\\Inference\\FrameResolver\:\:resolveNode\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token, Microsoft\\PhpParser\\Node\\Expression\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/IncludeWalker.php - - - - message: '#^Cannot call method getElements\(\) on Microsoft\\PhpParser\\Node\\DelimitedList\\ArgumentExpressionList\|null\.$#' - identifier: method.nonObject - count: 3 - path: lib/WorseReflection/Core/Inference/Walker/TestAssertWalker.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Inference/Walker/TestAssertWalker.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Name\:\:fromParts\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/WorseReflection/Core/Name.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Name\:\:prepend\(\) has parameter \$name with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Name.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Name\:\:prepend\(\) should return static\(Phpactor\\WorseReflection\\Core\\Name\) but returns Phpactor\\WorseReflection\\Core\\Name\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Name.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Name\:\:substitute\(\) has parameter \$alias with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Name.php - - - - message: '#^Parameter \#1 \$parts of class Phpactor\\WorseReflection\\Core\\Name constructor expects array\, array given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Name.php - - - - message: '#^Parameter \#1 \$parts of class Phpactor\\WorseReflection\\Core\\Name constructor expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Name.php - - - - message: '#^Parameter \#1 \$value of static method Phpactor\\WorseReflection\\Core\\Name\:\:fromUnknown\(\) expects Phpactor\\WorseReflection\\Core\\Name\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Name.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 2 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 3 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Class Phpactor\\WorseReflection\\Core\\NameImports implements generic interface IteratorAggregate but does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\NameImports\:\:__construct\(\) has parameter \$nameImports with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\NameImports\:\:fromNames\(\) has parameter \$nameImports with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\NameImports\:\:getByAlias\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\NameImports\:\:getIterator\(\) return type with generic class ArrayIterator does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\NameImports\:\:hasAlias\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Parameter \#1 \$array of class ArrayIterator constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Parameter \#1 \$short of method Phpactor\\WorseReflection\\Core\\NameImports\:\:add\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Parameter \#2 \$item of method Phpactor\\WorseReflection\\Core\\NameImports\:\:add\(\) expects Phpactor\\WorseReflection\\Core\\Name, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\NameImports\:\:\$nameImports has no type specified\.$#' - identifier: missingType.property - count: 1 - path: lib/WorseReflection/Core/NameImports.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\NodeText\:\:__construct\(\) has parameter \$nodeText with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/NodeText.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\NodeText\:\:__toString\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/NodeText.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\:\:byMemberClass\(\) should return static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\) but returns static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/AbstractReflectionCollection.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\:\:fromReflections\(\) should return static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\) but returns static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/AbstractReflectionCollection.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\:\:merge\(\) should return static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\) but returns static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\AbstractReflectionCollection\\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/AbstractReflectionCollection.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/AbstractReflectionCollection.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ChainReflectionMemberCollection\:\:count\(\) should return int\<0, max\> but returns \(float\|int\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/ChainReflectionMemberCollection.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ChainReflectionMemberCollection\:\:getIterator\(\) return type with generic class AppendIterator does not specify its types\: TKey, TValue, TIterator$#' - identifier: missingType.generics - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/ChainReflectionMemberCollection.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ChainReflectionMemberCollection\:\:getIterator\(\) should return AppendIterator&iterable\ but returns AppendIterator\\>\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/ChainReflectionMemberCollection.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ChainReflectionMemberCollection\:\:keys\(\) should return array\<\(int\|string\)\> but returns array\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/ChainReflectionMemberCollection.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/ChainReflectionMemberCollection.php - - - - message: '#^PHPDoc tag @return contains generic type static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ClassLikeReflectionMemberCollection\) but class Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ClassLikeReflectionMemberCollection is not generic\.$#' - identifier: generics.notGeneric - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/ClassLikeReflectionMemberCollection.php - - - - message: '#^Parameter \#4 \$node of class Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\ReflectionConstant constructor expects Microsoft\\PhpParser\\Node\\ConstElement, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/ClassLikeReflectionMemberCollection.php - - - - message: '#^Class Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ReflectionConstant not found\.$#' - identifier: class.notFound - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/HomogeneousReflectionMemberCollection.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\HomogeneousReflectionMemberCollection\:\:fromMembers\(\) should return static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\HomogeneousReflectionMemberCollection\\) but returns static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\HomogeneousReflectionMemberCollection\\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/HomogeneousReflectionMemberCollection.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\HomogeneousReflectionMemberCollection\:\:map\(\) should return static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\HomogeneousReflectionMemberCollection\\) but returns static\(Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\HomogeneousReflectionMemberCollection\\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/HomogeneousReflectionMemberCollection.php - - - - message: '#^Parameter \#1 \$items of class Phpactor\\WorseReflection\\Core\\Reflection\\Collection\\ReflectionConstantCollection constructor expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/HomogeneousReflectionMemberCollection.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 1 - path: lib/WorseReflection/Core/Reflection/Collection/HomogeneousReflectionMemberCollection.php - - - - message: '#^Cannot call method getName\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: lib/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.php - - - - message: '#^Parameter \#3 \$parameter of class Phpactor\\WorseReflection\\Bridge\\TolerantParser\\Reflection\\ReflectionParameter constructor expects Microsoft\\PhpParser\\Node\\Parameter, mixed given\.$#' - identifier: argument.type - count: 2 - path: lib/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 2 - path: lib/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 3 - path: lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 3 - path: lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php - - - - message: '#^Parameter \#1 \$name of static method Phpactor\\WorseReflection\\Core\\ClassName\:\:fromString\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 3 - path: lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Reflector\\ClassReflector\\MemonizedReflector\:\:\$innerReflector is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: lib/WorseReflection/Core/Reflector/ClassReflector/MemonizedReflector.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflector\\CompositeReflector\:\:reflectMethodCall\(\) has parameter \$offset with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Reflector/CompositeReflector.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflector\\CompositeReflector\:\:reflectNode\(\) has parameter \$offset with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Reflector/CompositeReflector.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflector\\CompositeReflector\:\:reflectOffset\(\) has parameter \$offset with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Reflector/CompositeReflector.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectMethodCall\(\) expects int\|Phpactor\\TextDocument\\ByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflector/CompositeReflector.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectNode\(\) expects int\|Phpactor\\TextDocument\\ByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflector/CompositeReflector.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectOffset\(\) expects int\|Phpactor\\TextDocument\\ByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflector/CompositeReflector.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflector\\CoreReflector\:\:reflectNode\(\) has parameter \$offset with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Reflector/CoreReflector.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflector\\CoreReflector\:\:reflectNode\(\) has parameter \$sourceCode with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Reflector/CoreReflector.php - - - - message: '#^Parameter \#1 \$sourceCode of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectNode\(\) expects Phpactor\\TextDocument\\TextDocument, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflector/CoreReflector.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectNode\(\) expects int\|Phpactor\\TextDocument\\ByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflector/CoreReflector.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCode\\ContextualSourceCodeReflector\:\:reflectMethodCall\(\) has parameter \$offset with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCode\\ContextualSourceCodeReflector\:\:reflectNode\(\) has parameter \$offset with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCode\\ContextualSourceCodeReflector\:\:reflectOffset\(\) has parameter \$offset with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: lib/WorseReflection/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectMethodCall\(\) expects int\|Phpactor\\TextDocument\\ByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectNode\(\) expects int\|Phpactor\\TextDocument\\ByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php - - - - message: '#^Parameter \#2 \$offset of method Phpactor\\WorseReflection\\Core\\Reflector\\SourceCodeReflector\:\:reflectOffset\(\) expects int\|Phpactor\\TextDocument\\ByteOffset, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php - - - - message: '#^Cannot call method getExtension\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/SourceCodeLocator/BruteForceSourceLocator.php - - - - message: '#^Cannot call method isDir\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/SourceCodeLocator/BruteForceSourceLocator.php - - - - message: '#^Parameter \#1 \$file of method Phpactor\\WorseReflection\\Core\\SourceCodeLocator\\BruteForceSourceLocator\:\:buildClassMap\(\) expects SplFileInfo, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/SourceCodeLocator/BruteForceSourceLocator.php - - - - message: '#^Parameter \#1 \$file of method Phpactor\\WorseReflection\\Core\\SourceCodeLocator\\BruteForceSourceLocator\:\:buildFunctionMap\(\) expects SplFileInfo, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/SourceCodeLocator/BruteForceSourceLocator.php - - - - message: '#^Cannot call method __toString\(\) on Phpactor\\WorseReflection\\Core\\Type\|null\.$#' - identifier: method.nonObject - count: 2 - path: lib/WorseReflection/Core/Type/ArrayType.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Type\\BinLiteralType\:\:\$value \(string\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: lib/WorseReflection/Core/Type/BinLiteralType.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Type\\BooleanLiteralType\:\:\$value \(bool\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: lib/WorseReflection/Core/Type/BooleanLiteralType.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Type\\FloatLiteralType\:\:\$value \(float\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: lib/WorseReflection/Core/Type/FloatLiteralType.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Type\\HexLiteralType\:\:\$value \(string\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: lib/WorseReflection/Core/Type/HexLiteralType.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 1 - path: lib/WorseReflection/Core/Type/IntLiteralType.php - - - - message: '#^Binary operation "&" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/IntType.php - - - - message: '#^Binary operation "\<\<" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/IntType.php - - - - message: '#^Binary operation "\>\>" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/IntType.php - - - - message: '#^Binary operation "\^" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/IntType.php - - - - message: '#^Binary operation "\|" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/IntType.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 1 - path: lib/WorseReflection/Core/Type/IntType.php - - - - message: '#^Binary operation "%%" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/NumericType.php - - - - message: '#^Binary operation "\*" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/NumericType.php - - - - message: '#^Binary operation "\*\*" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/NumericType.php - - - - message: '#^Binary operation "\+" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/NumericType.php - - - - message: '#^Binary operation "\-" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/NumericType.php - - - - message: '#^Binary operation "/" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: lib/WorseReflection/Core/Type/NumericType.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Type\\OctalLiteralType\:\:\$value \(string\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: lib/WorseReflection/Core/Type/OctalLiteralType.php - - - - message: '#^Parameter \#1 \$type of method Phpactor\\WorseReflection\\Core\\Type\:\:accepts\(\) expects Phpactor\\WorseReflection\\Core\\Type, Phpactor\\WorseReflection\\Core\\Type\|null given\.$#' - identifier: argument.type - count: 2 - path: lib/WorseReflection/Core/Type/PseudoIterableType.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: lib/WorseReflection/Core/Type/ReflectedClassType.php - - - - message: '#^Instanceof between Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClassLike and Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClassLike will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Type/ReflectedClassType.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Type\\StringLiteralType\:\:\$value \(string\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: lib/WorseReflection/Core/Type/StringLiteralType.php - - - - message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: lib/WorseReflection/Core/Util/NodeUtil.php - - - - message: '#^Cannot call method __toString\(\) on Phpactor\\WorseReflection\\Core\\ClassName\|null\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Util/NodeUtil.php - - - - message: '#^Cannot call method getText\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: lib/WorseReflection/Core/Util/NodeUtil.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Node\\Statement\\ClassDeclaration\|Microsoft\\PhpParser\\Node\\Statement\\InterfaceDeclaration\|Microsoft\\PhpParser\\Node\\Statement\\TraitDeclaration and Microsoft\\PhpParser\\NamespacedNameInterface will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Util/NodeUtil.php - - - - message: '#^Instanceof between Microsoft\\PhpParser\\Token and Microsoft\\PhpParser\\Token will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: lib/WorseReflection/Core/Util/NodeUtil.php - - - - message: '#^Parameter \#1 \$type of static method Phpactor\\WorseReflection\\Core\\TypeFactory\:\:fromStringWithReflector\(\) expects string, Microsoft\\PhpParser\\ResolvedName\|string\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Util/NodeUtil.php - - - - message: '#^Parameter \#3 \$nodeOrToken of static method Phpactor\\WorseReflection\\Core\\Util\\NodeUtil\:\:typeFromQualfiedNameLike\(\) expects Microsoft\\PhpParser\\Node\|Microsoft\\PhpParser\\Token\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Util/NodeUtil.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 2 - path: lib/WorseReflection/Core/Util/NodeUtil.php - - - - message: '#^Parameter \#1 \$classLike of method Phpactor\\WorseReflection\\Core\\Util\\OriginalMethodResolver\:\:doResolveOriginalMember\(\) expects Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClassLike, Phpactor\\WorseReflection\\Core\\Reflection\\ReflectionClass\|null given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Util/OriginalMethodResolver.php - - - - message: '#^Parameter \#1 \$object of function get_class expects object, mixed given\.$#' - identifier: argument.type - count: 1 - path: lib/WorseReflection/Core/Util/QualifiedNameListUtil.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\:\:withDeclaringClass\(\) should return \$this\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\) but returns static\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Virtual/VirtualReflectionMember.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\:\:withInferredType\(\) should return \$this\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\) but returns static\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Virtual/VirtualReflectionMember.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\:\:withName\(\) should return \$this\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\) but returns static\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Virtual/VirtualReflectionMember.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\:\:withType\(\) should return \$this\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\) but returns static\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Virtual/VirtualReflectionMember.php - - - - message: '#^Method Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\:\:withVisibility\(\) should return \$this\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\) but returns static\(Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMember\)\.$#' - identifier: return.type - count: 1 - path: lib/WorseReflection/Core/Virtual/VirtualReflectionMember.php - - - - message: '#^Property Phpactor\\WorseReflection\\Core\\Virtual\\VirtualReflectionMethod\:\:\$type is unused\.$#' - identifier: property.unused - count: 1 - path: lib/WorseReflection/Core/Virtual/VirtualReflectionMethod.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: lib/WorseReflection/TypeUtil.php diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index 847aa80637..0000000000 --- a/phpstan.neon +++ /dev/null @@ -1,25 +0,0 @@ -parameters: - level: max - paths: - - lib - - excludePaths: - - */lib/*Tests/Workspace/* - - */lib/*Tests/Example/* - - */lib/Extension/*/Tests/Workspace/* - - */lib/Extension/*/Tests/Example/* - - */lib/WorseReflection/Tests/* - - lib/Completion/Tests/Unit/Core/Util/OffsetHelperTest.php - - */lib/ClassMover/Tests/Adapter/TolerantParser/examples/* - - lib/WorseReflection/Core/SourceCodeLocator/InternalStubs/* - - lib/Extension/LanguageServerCodeTransform/Tests/Stub/* - - lib/Extension/LanguageServerCodeTransform/Tests/Empty/* - - reportUnmatchedIgnoredErrors: false - inferPrivatePropertyTypeFromConstructor: true - -rules: - - Phpactor\Tests\PHPStan\Rule\NoDumpRule - -includes: - - phpstan-baseline.neon diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 21172ad710..0000000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - . - - - vendor/ - - - - - ./tests/System - ./tests/Unit - ./tests/Smoke - ./tests/Integration - - ./lib/*/Tests/ - ./lib/Extension/*/Tests/ - - - - - - diff --git a/plugin/phpactor.vim b/plugin/phpactor.vim deleted file mode 100644 index 4434f9f8af..0000000000 --- a/plugin/phpactor.vim +++ /dev/null @@ -1,154 +0,0 @@ -if exists('g:loaded_phpactor') - finish -endif - -let g:loaded_phpactor = 1 - -" Config {{{ -let g:phpactorpath = expand(':p:h') . '/..' -let g:phpactorbinpath = g:phpactorpath. '/bin/phpactor' -let g:phpactorInitialCwd = getcwd() -let g:phpactorCompleteLabelTruncateLength=50 - -"" -" Path to the PHP binary used by Phpactor -let g:phpactorPhpBin = get(g:, 'phpactorPhpBin', 'php') - -"" -" The Phpactor branch to use when calling @command(PhpactorUpdate) -let g:phpactorBranch = get(g:, 'phpactorBranch', 'master') - -"" -" Automatically import classes when using VIM native omni-completion -let g:phpactorOmniAutoClassImport = get(g:, 'phpactorOmniAutoClassImport', v:true) - -"" -" Ignore case when suggestion completion results -let g:phpactorCompletionIgnoreCase = get(g:, 'phpactorCompletionIgnoreCase', 1) - -"" -" Function to use when populating a list of code references. The default -" is to use the VIM quick-fix list. -let g:phpactorQuickfixStrategy = get(g:, 'phpactorQuickfixStrategy', 'phpactor#quickfix#vim') - -"" -" Function to use when presenting a user with a choice of options. The default -" is to use the VIM inputlist. -let g:phpactorInputListStrategy = get(g:, 'phpactorInputListStrategy', 'phpactor#input#list#inputlist') - -"" -" When jumping to a file location: if the target file open in a window, switch -" to that window instead of switching buffers. The default is false. -let g:phpactorUseOpenWindows = get(g:, 'phpactorUseOpenWindows', v:false) - -"" -" Each Phpactor request requires the project's root directory to be known. By -" default it will assume the directory in which you started VIM, but this may -" not suit all workflows. -" -" This setting allows |Funcref| to be specified. This function should return -" the working directory in whichever way is required. No arguments are passed -" to this function. -let g:PhpactorRootDirectoryStrategy = get(g:, 'PhpactorRootDirectoryStrategy', {-> g:phpactorInitialCwd}) - -" Config }}} - -" Commands {{{ - -"" -" Update Phpactor to the latest version using the branch -" defined with @setting(g:phpactorBranch) -command! -nargs=0 PhpactorUpdate call phpactor#Update() - -"" -" Clear the entire cache - this will take effect for all projects. -command! -nargs=0 PhpactorCacheClear call phpactor#CacheClear() - -"" -" Show some information about Phpactor's status -command! -nargs=0 PhpactorStatus call phpactor#Status() - -"" -" Dump Phpactor's configuration -command! -nargs=0 PhpactorConfig call phpactor#Config() - -"" -" Expand the class name under the cursor to it's fully-qualified-name -command! -nargs=0 PhpactorClassExpand call phpactor#ClassExpand() - -"" -" Create a new class. You will be offered a choice of templates. -command! -nargs=0 PhpactorClassNew call phpactor#ClassNew() - -"" -" @default target=`edit` -" -" Goto the definition of the symbol under the cursor. -" Opens in the [target] window, see @section(window-target) for -" the list of possible targets. -" || can be provided to the command to change how the window will be -" opened. -" -" Examples: -" > -" " Opens in the current buffer -" PhpactorGotoDefinition -" -" " Opens in a vertical split opened on the right side -" botright PhpactorGotoDefinition vsplit -" vertical botright PhpactorGotoDefinition split -" -" " Opens in a new tab -" PhpactorGotoDefinition tabnew -" < -command! -nargs=? -complete=customlist,s:CompleteWindowTarget PhpactorGotoDefinition call phpactor#GotoDefinition(, ) -"" -" deprecated, use @command(PhpactorGotoDefinition) instead -" -" As with @command(PhpactorGotoDefinition) but open in a vertical split. -command! -nargs=0 PhpactorGotoDefinitionVsplit - \ echoerr 'PhpactorGotoDefinitionVsplit is deprecated, use PhpactorGotoDefinition instead' | - \ PhpactorGotoDefinition vsplit -"" -" deprecated, use @command(PhpactorGotoDefinition) instead -" -" As with @command(PhpactorGotoDefinition) but open in an horizontal split. -command! -nargs=0 PhpactorGotoDefinitionHsplit - \ echoerr 'PhpactorGotoDefinitionHsplit is deprecated, use PhpactorGotoDefinition instead' | - \ PhpactorGotoDefinition hsplit -"" -" deprecated, use @command(PhpactorGotoDefinition) instead -" -" As with @command(PhpactorGotoDefinition) but open in a new tab. -command! -nargs=0 PhpactorGotoDefinitionTab - \ echoerr 'PhpactorGotoDefinitionTab is deprecated, use PhpactorGotoDefinition instead' | - \ PhpactorGotoDefinition new_tab - -"" -" @usage [target] -" -" Same as @command(PhpactorGotoDefinition) but goto the type of the symbol -" under the cursor. -command! -nargs=? -complete=customlist,s:CompleteWindowTarget PhpactorGotoType call phpactor#GotoType(, ) - -"" -" @usage [target] -" -" Same as @command(PhpactorGotoDefinition) but goto the implementation of the -" symbol under the cursor. -" -" If there is more than one result the quickfix strategy will be used and [target] -" will be ignored, see @setting(g:phpactorQuickfixStrategy). -command! -nargs=? -complete=customlist,s:CompleteWindowTarget PhpactorGotoImplementations call phpactor#GotoImplementations(, ) - -" Commands }}} - -" Functions {{{ - -function! s:CompleteWindowTarget(argLead, ...) abort - return filter(phpactor#windowTargets(), {k,v -> 0 == stridx(v, a:argLead)}) -endfunction - -" }}} - -" vim: et ts=4 sw=4 fdm=marker diff --git a/rector.php b/rector.php deleted file mode 100644 index f156b65c7c..0000000000 --- a/rector.php +++ /dev/null @@ -1,27 +0,0 @@ -withImportNames() - ->withPaths([ - __DIR__ . '/lib', - __DIR__ . '/tests', - ]) - ->withSkipPath('*/Workspace/*') - ->withSkipPath('/tests/Assets/*') - ->withSkipPath('/*/examples/*') - ->withSets([ - PHPUnitSetList::PHPUNIT_100, - ]) - ->withRules([ - ExplicitNullableParamTypeRector::class, - NewInInitializerRector::class, - FunctionFirstClassCallableRector::class, - ]); diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index ef458633ec..0000000000 --- a/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -docutils==0.18.1 -sphinx==6.0.0 -sphinx-tabs==3.4.1 -sphinx-autobuild==2021.3.14 -standard-imghdr==3.13.0 -setuptools==80.9.0 - - diff --git a/templates/code/7.4/Property.php.twig b/templates/code/7.4/Property.php.twig deleted file mode 100644 index 746817f0e2..0000000000 --- a/templates/code/7.4/Property.php.twig +++ /dev/null @@ -1,11 +0,0 @@ -{% set renderedType = render_type(prototype.type) %} -{% if prototype.docType != renderedType %} -/** - * @var {{ prototype.docType }} - */ -{% endif %} -{{ prototype.visibility }} -{%- if renderedType %} - {{ renderedType }} -{%- endif %} - ${{ prototype.name}}{% if prototype.defaultValue.notNone %} = {{ prototype.defaultValue.export }}{% endif %}; diff --git a/templates/code/Attribute.php.twig b/templates/code/Attribute.php.twig deleted file mode 100644 index 18120e61bb..0000000000 --- a/templates/code/Attribute.php.twig +++ /dev/null @@ -1 +0,0 @@ -#[{{ prototype.name }}{% if prototype.arguments|length > 0 %}({% for argument in prototype.arguments %}{{ argument.export()|raw }}{% if not loop.last %}, {% endif %}{% endfor %}){% endif %}] diff --git a/templates/code/Case_.php.twig b/templates/code/Case_.php.twig deleted file mode 100644 index 0c34b4361d..0000000000 --- a/templates/code/Case_.php.twig +++ /dev/null @@ -1 +0,0 @@ -case {{ prototype.name }}; diff --git a/templates/code/ClassPrototype.php.twig b/templates/code/ClassPrototype.php.twig deleted file mode 100644 index 3d2c3ddd44..0000000000 --- a/templates/code/ClassPrototype.php.twig +++ /dev/null @@ -1,26 +0,0 @@ -class {{ prototype.name }}{% if prototype.extendsClass.notNone %} extends {{ prototype.extendsClass }}{% endif %} -{% if prototype.implementsInterfaces|length %} - implements {% for interface in prototype.implementsInterfaces %}{{ interface }}{% if not loop.last %}, {% endif %}{% endfor %} -{% endif %} - -{ -{% for constant in prototype.constants %} -{{ generator.render(constant, variant)|indent(1)|raw }} -{% endfor %} -{% for property in prototype.properties %} -{% if loop.first and prototype.constants|length %} - -{% endif %} -{{ generator.render(property, variant)|indent(1)|raw }} -{% if not loop.last %} - -{% endif %} -{% endfor %} -{% for method in prototype.methods %} -{% if prototype.properties|length or not loop.first %} - -{% endif %} -{{ generator.render(method, variant)|indent(1)|raw }} -{{ generator.render(method.body)|indent(1)|raw }} -{% endfor %} -} diff --git a/templates/code/Constant.php.twig b/templates/code/Constant.php.twig deleted file mode 100644 index ff7926195a..0000000000 --- a/templates/code/Constant.php.twig +++ /dev/null @@ -1 +0,0 @@ -{% if prototype.visibility %}{{ prototype.visibility }} {%endif%}const {{ prototype.name}} = {{ prototype.value.export|raw }}; diff --git a/templates/code/EnumPrototype.php.twig b/templates/code/EnumPrototype.php.twig deleted file mode 100644 index 118d74b57e..0000000000 --- a/templates/code/EnumPrototype.php.twig +++ /dev/null @@ -1,13 +0,0 @@ -enum {{ prototype.name }} -{ -{% for case in prototype.cases %} -{{ generator.render(case, variant)|indent(1)|raw }} -{% endfor %} -{% for method in prototype.methods %} -{% if prototype.properties|length or not loop.first %} - -{% endif %} -{{ generator.render(method, variant)|indent(1)|raw }} -{{ generator.render(method.body)|indent(1)|raw }} -{% endfor %} -} diff --git a/templates/code/InterfacePrototype.php.twig b/templates/code/InterfacePrototype.php.twig deleted file mode 100644 index 6a7bee467e..0000000000 --- a/templates/code/InterfacePrototype.php.twig +++ /dev/null @@ -1,12 +0,0 @@ -interface {{ prototype.name }} -{% if prototype.extendsInterfaces|length %} - implements {% for interface in prototype.extendsInterfaces %}{{ interface }}{% if not loop.last %}, {% endif %}{% endfor %} -{% endif %} -{ -{% for method in prototype.methods %} -{{ generator.render(method, variant)|indent(1)|raw }}; -{% if not loop.last %} - -{% endif %} -{% endfor %} -} diff --git a/templates/code/Method.php.twig b/templates/code/Method.php.twig deleted file mode 100644 index db84f77d21..0000000000 --- a/templates/code/Method.php.twig +++ /dev/null @@ -1,14 +0,0 @@ -{% set renderedType = render_type(prototype.returnType.type) %} -{% if prototype.docblock.notNone %} -/** -{% for line in prototype.docblock.asLines %} - * {{ line|raw }} -{% endfor %} - */ -{% endif %} -{% if prototype.attributes %} -{% for attribute in prototype.attributes %} -{{ generator.render(attribute) }} -{% endfor %} -{% endif %} -{% if prototype.isAbstract %}abstract {%endif %}{{ prototype.visibility }} {% if prototype.isStatic %}static {%endif %}function {{ prototype.name}}({% for parameter in prototype.parameters %}{{ generator.render(parameter, variant)|raw }}{% if not loop.last %}, {% endif %}{% endfor %}){% if renderedType %}: {{ renderedType }}{% endif %} diff --git a/templates/code/MethodBody.php.twig b/templates/code/MethodBody.php.twig deleted file mode 100644 index a248d28731..0000000000 --- a/templates/code/MethodBody.php.twig +++ /dev/null @@ -1,5 +0,0 @@ -{ -{% for line in prototype.lines %} -{{ line|indent(1)|raw }} -{% endfor %} -} diff --git a/templates/code/MethodHeader.php.twig b/templates/code/MethodHeader.php.twig deleted file mode 100644 index 5427e8cae6..0000000000 --- a/templates/code/MethodHeader.php.twig +++ /dev/null @@ -1,13 +0,0 @@ -{% if prototype.docblock.notNone %} -/** -{% for line in prototype.docblock.asLines %} - * {{ line|raw }} -{% endfor %} - */ -{% endif %} -{% if prototype.isAbstract %}abstract {%endif %}{{ prototype.visibility }} {% if prototype.isStatic %}static {%endif %}function {{ prototype.name}}({% for parameter in prototype.parameters %} -{% if parameter.type.notNone %}{{ parameter.type }} {% endif %} -{% if true %}${{ parameter.name }}{% endif %} -{% if parameter.defaultValue.notNone %} = {{ parameter.defaultValue.export|raw }}{% endif %} -{% if not loop.last %}, {% endif %} -{% endfor %}){% if prototype.returnType.notNone %}: {{ prototype.returnType }}{% endif %} diff --git a/templates/code/Parameter.php.twig b/templates/code/Parameter.php.twig deleted file mode 100644 index ccf269f3bc..0000000000 --- a/templates/code/Parameter.php.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% set renderedType = render_type(prototype.type) %} -{% if prototype.visibility is not null %}{{ prototype.visibility }} {% endif %} -{% if renderedType %}{{ renderedType }} {% endif %} -{% if prototype.byReference %}&{% endif %}{% if prototype.isVariadic %}...{% endif %}{% if true %}${{ prototype.name }}{% endif %} -{% if prototype.defaultValue.notNone %} = {{ prototype.defaultValue.export|raw }}{% endif %} diff --git a/templates/code/Property.php.twig b/templates/code/Property.php.twig deleted file mode 100644 index 6eae4f4ddb..0000000000 --- a/templates/code/Property.php.twig +++ /dev/null @@ -1,6 +0,0 @@ -{% if prototype.docTypeOrType.notNone %} -/** - * @var {{ prototype.docTypeOrType }} - */ -{% endif %} -{{ prototype.visibility }} ${{ prototype.name}}{% if prototype.defaultValue.notNone %} = {{ prototype.defaultValue.export }}{% endif %}; diff --git a/templates/code/SourceCode.php.twig b/templates/code/SourceCode.php.twig deleted file mode 100644 index b3ecd309b9..0000000000 --- a/templates/code/SourceCode.php.twig +++ /dev/null @@ -1,27 +0,0 @@ - 50 %} - // and {{ members|length - 50 }} more... -{% endif %} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.twig deleted file mode 100644 index 480de4aee2..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/Collection/ReflectionParameterCollection.twig +++ /dev/null @@ -1,7 +0,0 @@ -{%- for parameter in object -%} -{% if parameter.isVariadic %} -{% if typeDefined(parameter.type) and attribute(parameter.type, 'iterableValueType') is defined %}{{- render(parameter.type.iterableValueType) }} {% endif %}...${{- parameter.name -}}{% if not loop.last %}, {% endif %} -{% else %} -{% if typeDefined(parameter.inferredType) %}{{- render(parameter.inferredType) }} {% endif %}${{- parameter.name -}}{% if not loop.last %}, {% endif %} -{% endif %} -{%- endfor -%} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionClass.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionClass.twig deleted file mode 100644 index eded81919b..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionClass.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }}{% endif %} -{% if object.isFinal %}final {% endif %}class {{ object.name.short }}{% if object.parent %} extends {{ object.parent.name.short }}{% endif -%} -{% if object.interfaces.count %} implements {% for interface in object.interfaces -%} - {{ interface.name.short }}{% if not loop.last %}, {% endif %} - {%- endfor %}{% endif %} { -{{ render(object.members) -}} -} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionClassLike.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionClassLike.twig deleted file mode 100644 index f76cc83193..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionClassLike.twig +++ /dev/null @@ -1,3 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }}{% endif %} -__{{ object.name.full }}__ -{{ object.docblock.formatted }} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionConstant.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionConstant.twig deleted file mode 100644 index e96286b0f3..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionConstant.twig +++ /dev/null @@ -1,3 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }}{% endif %} -{{- object.visibility ~ ' ' -}} -const {{ object.name }}{% if object.value %} = {{ object.value|json_encode(constant('JSON_UNESCAPED_SLASHES')) }}{% endif %}{{ ';' -}} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionDeclaredConstant.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionDeclaredConstant.twig deleted file mode 100644 index e758793653..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionDeclaredConstant.twig +++ /dev/null @@ -1 +0,0 @@ -define {{ object.name }} = {{ object.type }} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionEnum.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionEnum.twig deleted file mode 100644 index 248c0e1dda..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionEnum.twig +++ /dev/null @@ -1,6 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }}{% endif %} -enum {{ object.name.short }}{% if object.isBacked %}: {{ render(object.backedType) }}{% endif %} { -{% for member in object.members.byVisibilities(["public"]) -%} -{{ ' ' ~ render(member) }} -{% endfor -%} -} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionEnumCase.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionEnumCase.twig deleted file mode 100644 index a78c51e205..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionEnumCase.twig +++ /dev/null @@ -1,3 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }}{% endif %} -case {{ object.name }}{% if object.class.isBacked and object.value %} = {{ render(object.value) }}{% endif %}{{ ';' -}} - diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionFunction.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionFunction.twig deleted file mode 100644 index f3a4cbddb5..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionFunction.twig +++ /dev/null @@ -1,4 +0,0 @@ -function {{ object.name }}({{ render(object.parameters) }}) -{%- if typeDefined(object.inferredType) %}: {{ render(object.inferredType) }}{% endif -%} - - diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionInterface.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionInterface.twig deleted file mode 100644 index bbcffca662..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionInterface.twig +++ /dev/null @@ -1,11 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }}{% endif %} -interface {{ object.name.short }}{% if object.parents.count %} extends {% for interface in object.parents -%} - {{ interface.name.short }}{% if not loop.last %}, {% endif %} - {%- endfor %}{% endif %} { -{% for constant in object.constants -%} -{{ ' ' ~ render(constant) }} -{% endfor -%} -{% for method in object.methods -%} -{{ ' ' ~ render(method) }} -{% endfor -%} -} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionMethod.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionMethod.twig deleted file mode 100644 index 9c31318e49..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionMethod.twig +++ /dev/null @@ -1,18 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }} {% endif %} -{%- if object.declaringClass.isClass and object.class.isClass -%} - {%- if object.deprecation.isDefined %}⚠ {% endif -%} - {%- if object.declaringClass.parent -%} - {%- set parent = object.class.parent -%} - {%- if parent.methods.has(object.name) -%}Ⓒ {% endif -%} - {%- endif -%} - {%- for interface in object.declaringClass.interfaces|slice(0,1) -%} - {%- if interface.methods.has(object.name) -%}ⓘ {% endif -%} - {%- endfor -%} -{%- endif -%} -{% if object.isVirtual %}[virtual] {% endif -%} - {% if object.isAbstract -%}abstract {% endif -%} -{{- object.visibility ~ ' ' -}} -{%- if object.isStatic %}static {% endif %} -function {{ object.name }}({{ render(object.parameters) }}) -{%- if typeDefined(object.inferredType) %}: {{ render(object.inferredType) }}{% endif -%} - diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionOffset.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionOffset.twig deleted file mode 100644 index ffc4b04dff..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionOffset.twig +++ /dev/null @@ -1 +0,0 @@ -{{ render(object.nodeContext) }} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionProperty.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionProperty.twig deleted file mode 100644 index 1c446a1569..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionProperty.twig +++ /dev/null @@ -1,3 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }}{% endif %} -{% if object.isVirtual %}[virtual] {% endif -%} - {{- object.visibility}}{%- if typeDefined(object.inferredType) %} {{ render(object.inferredType) }}{% endif %} ${{ object.name ~ ';' -}} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionTrait.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionTrait.twig deleted file mode 100644 index 26827e4be7..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Reflection/ReflectionTrait.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% if object.deprecation.isDefined %}{{ render(object.deprecation) }}{% endif %} -trait {{ object.name.short }} { -{% for method in object.methods.byVisibilities(["public"]) -%} -{{ ' ' ~ render(method) }} -{% endfor -%} -} - diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Type.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Type.twig deleted file mode 100644 index 35d5838377..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Type.twig +++ /dev/null @@ -1 +0,0 @@ -{% if typeType(object) %}{{- typeType(object) }} {% endif %}{{- typeShortName(object) -}} diff --git a/templates/help/markdown/Phpactor/WorseReflection/Core/Type/UnionType.twig b/templates/help/markdown/Phpactor/WorseReflection/Core/Type/UnionType.twig deleted file mode 100644 index b1f86a26ff..0000000000 --- a/templates/help/markdown/Phpactor/WorseReflection/Core/Type/UnionType.twig +++ /dev/null @@ -1,3 +0,0 @@ -{% for type in object.types -%} -{{ render(type) -}}{% if not loop.last %}|{%endif%} -{% endfor %} diff --git a/tests/Assets/Projects/Animals/composer.json b/tests/Assets/Projects/Animals/composer.json deleted file mode 100644 index 289774be7c..0000000000 --- a/tests/Assets/Projects/Animals/composer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "autoload": { - "psr-4": { - "Animals\\": "lib/" - } - } -} diff --git a/tests/Assets/Projects/Animals/lib/Aardvark/Edentate.php b/tests/Assets/Projects/Animals/lib/Aardvark/Edentate.php deleted file mode 100644 index 537767031f..0000000000 --- a/tests/Assets/Projects/Animals/lib/Aardvark/Edentate.php +++ /dev/null @@ -1,7 +0,0 @@ -carnivorous = $carnivorous; - } - - public function badge() - { - $this->badge(); - } - - public function carnivorous() - { - } -} diff --git a/tests/Assets/Projects/Animals/lib/Badger/Carnivorous.php b/tests/Assets/Projects/Animals/lib/Badger/Carnivorous.php deleted file mode 100644 index 9bb0a455a9..0000000000 --- a/tests/Assets/Projects/Animals/lib/Badger/Carnivorous.php +++ /dev/null @@ -1,11 +0,0 @@ - - } -} diff --git a/tests/Assets/Projects/Symfony/composer.json b/tests/Assets/Projects/Symfony/composer.json deleted file mode 100644 index 5b32658df5..0000000000 --- a/tests/Assets/Projects/Symfony/composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "require": { - "symfony/framework-standard-edition": "3.0" - } -} diff --git a/tests/Benchmark/BaseBenchCase.php b/tests/Benchmark/BaseBenchCase.php deleted file mode 100644 index 94c47aa618..0000000000 --- a/tests/Benchmark/BaseBenchCase.php +++ /dev/null @@ -1,25 +0,0 @@ -workspaceDir())) { - $this->workspace()->reset(); - } - $process = Process::fromShellCommandline(__DIR__ . '/../../bin/phpactor ' . $command); - $process->setInput($stdin); - $process->setWorkingDirectory($this->workspaceDir()); - $process->run(); - return $process->getOutput(); - } -} diff --git a/tests/Benchmark/BaseLineBench.php b/tests/Benchmark/BaseLineBench.php deleted file mode 100644 index 6aa49c47e6..0000000000 --- a/tests/Benchmark/BaseLineBench.php +++ /dev/null @@ -1,31 +0,0 @@ -runCommand('--version'); - } - - public function benchRpcEcho(): void - { - $this->runCommand('rpc', '{"action":"echo","parameters":{"message":"hello"}'); - } -} diff --git a/tests/Benchmark/ClassSearchBench.php b/tests/Benchmark/ClassSearchBench.php deleted file mode 100644 index 42638ff7da..0000000000 --- a/tests/Benchmark/ClassSearchBench.php +++ /dev/null @@ -1,28 +0,0 @@ -workspace()->reset(); - $this->loadProject('Symfony'); - } - - public function benchClassSearch(): void - { - $this->runCommand('class:search Request'); - } -} diff --git a/tests/Benchmark/CompleteBench.php b/tests/Benchmark/CompleteBench.php deleted file mode 100644 index cd363e1127..0000000000 --- a/tests/Benchmark/CompleteBench.php +++ /dev/null @@ -1,30 +0,0 @@ -workspace()->reset(); - $this->loadProject('PhpUnit'); - } - - public function benchComplete(): void - { - $output = $this->runCommand('complete tests/FoobarTest.php 145'); //145? - Assert::assertStringContainsString('short_description:pub', $output); - } -} diff --git a/tests/Integration/ApplicationTest.php b/tests/Integration/ApplicationTest.php deleted file mode 100644 index d9d755e8d4..0000000000 --- a/tests/Integration/ApplicationTest.php +++ /dev/null @@ -1,80 +0,0 @@ -workspace()->reset(); - } - - public function tearDown(): void - { - } - - public function application(): Application - { - return new Application(__DIR__ . '/../../vendor'); - } - - public function testToleratesInvalidConfig(): void - { - file_put_contents( - $this->workspaceDir() . '/.phpactor.yml', - <<<'EOT' - foobar_invalid: something - EOT - ); - - chdir($this->workspaceDir()); - $output = new BufferedOutput(); - $application = $this->application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - $application->run(new ArrayInput([ - 'command' => 'class:reflect', - 'name' => 'asd', - '--format' => 'json', - ]), $output); - $this->addToAssertionCount(1); - } - - public function testSerializesExceptions(): void - { - $output = new BufferedOutput(); - - $application = $this->application(); - $application->setAutoExit(false); - $application->run(new ArrayInput([ - 'command' => 'class:reflect', - 'name' => 'asd', - '--format' => 'json', - ]), $output); - - $out = json_decode($output->fetch(), true); - $this->assertArrayHasKey('error', $out); - } - - public function testCwd(): void - { - $this->loadProject('Animals'); - $output = new BufferedOutput(); - - $application = $this->application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - $exitCode = $application->run(new ArrayInput([ - 'command' => 'config:dump', - '--working-dir' => $this->workspaceDir(), - ]), $output); - - $this->assertEquals(0, $exitCode); - $this->assertStringContainsString($this->workspaceDir(), $output->fetch()); - } -} diff --git a/tests/Integration/Extension/Core/CacheClearTest.php b/tests/Integration/Extension/Core/CacheClearTest.php deleted file mode 100644 index 8035f34c91..0000000000 --- a/tests/Integration/Extension/Core/CacheClearTest.php +++ /dev/null @@ -1,36 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest( - <<<'EOT' - // File: test.text - Hello World - // File: folder/test.text - Hello World - EOT - ); - $this->cacheClear = new CacheClear($this->workspaceDir()); - } - - public function testCacheClear(): void - { - $this->assertTrue($this->workspace()->exists('/test.text')); - $this->assertTrue($this->workspace()->exists('/folder/test.text')); - - $this->cacheClear->clearCache(); - - $this->assertFalse($this->workspace()->exists('/test.text')); - $this->assertFalse($this->workspace()->exists('/folder/test.text')); - } -} diff --git a/tests/Integration/Extension/Navigation/Navigator/WorseReflectionNavigatorTest.php b/tests/Integration/Extension/Navigation/Navigator/WorseReflectionNavigatorTest.php deleted file mode 100644 index 67cd85bd57..0000000000 --- a/tests/Integration/Extension/Navigation/Navigator/WorseReflectionNavigatorTest.php +++ /dev/null @@ -1,114 +0,0 @@ -create( - <<<'EOT' - // File:One.php - destinationsFor($this->workspaceDir() . '/Two.php'); - - $this->assertEquals([ - 'parent' => $this->workspaceDir() . '/One.php', - ], $destinations); - } - - public function testNavigateToInterfaces(): void - { - $navigator = $this->create( - <<<'EOT' - // File:One.php - destinationsFor($this->workspaceDir() . '/Three.php'); - - $this->assertEquals([ - 'interface:One' => $this->workspaceDir() . '/One.php', - 'interface:Two' => $this->workspaceDir() . '/Two.php', - ], $destinations); - } - - public function testNavigateFromInterfaceToParents(): void - { - $navigator = $this->create( - <<<'EOT' - // File:One.php - destinationsFor($this->workspaceDir() . '/Three.php'); - - $this->assertEquals([ - 'interface:One' => $this->workspaceDir() . '/One.php', - 'interface:Two' => $this->workspaceDir() . '/Two.php', - ], $destinations); - } - - private function create(string $manifest): WorseReflectionNavigator - { - $workspace = $this->workspace()->create($this->workspaceDir()); - $workspace->reset(); - $workspace->loadManifest($manifest); - $reflector = ReflectorBuilder::create()->addLocator( - new ClassToFileSourceLocator( - new SimpleClassToFile($this->workspaceDir()) - ) - )->build(); - - return new WorseReflectionNavigator($reflector); - } -} diff --git a/tests/IntegrationTestCase.php b/tests/IntegrationTestCase.php deleted file mode 100644 index 94d61da495..0000000000 --- a/tests/IntegrationTestCase.php +++ /dev/null @@ -1,109 +0,0 @@ - $cmd - */ - public function phpactor(array $cmd): Process - { - $p = new Process(array_merge( - [PHP_BINARY, __DIR__ . '/../bin/phpactor'], - $cmd - ), $this->workspace()->path()); - - return $p; - } - - protected function workspaceDir(): string - { - return __DIR__ . '/Assets/Workspace'; - } - - protected function workspace(): Workspace - { - return Workspace::create($this->workspaceDir()); - } - - protected function assertSuccess(Process $process): void - { - if (true === $process->isSuccessful()) { - $this->addToAssertionCount(1); - return; - } - - $this->fail(sprintf( - 'Process exited with code %d: %s %s', - $process->getExitCode(), - $process->getErrorOutput(), - $process->getOutput() - )); - } - - protected function assertFailure(Process $process, ?string $message): void - { - if (true === $process->isSuccessful()) { - $this->fail('Process was a success'); - } - - if (null !== $message) { - $this->assertStringContainsString($message, $process->getErrorOutput()); - } - - $this->addToAssertionCount(1); - } - - protected function loadProject(string $name): void - { - $filesystem = new Filesystem(); - - if (file_exists($this->cacheDir($name))) { - $filesystem->mirror($this->cacheDir($name), $this->workspaceDir()); - return; - } - - $filesystem->mirror(__DIR__ . '/Assets/Projects/' . $name, $this->workspaceDir()); - $currentDir = getcwd(); - chdir($this->workspaceDir()); - exec('git init'); - exec('git add *'); - exec('git commit -m "Test"'); - exec('composer install --quiet'); - chdir($currentDir); - $this->cacheWorkspace($name); - } - - protected function container(): Container - { - return Phpactor::boot(new ArrayInput([ - '--working-dir' => $this->workspaceDir(), - ]), new BufferedOutput(), __DIR__ . '/../vendor'); - } - - private function cacheDir(string $name): string - { - return __DIR__ . '/Assets/Cache/'.$name; - } - - private function cacheWorkspace(string $name): void - { - $filesystem = new Filesystem(); - $cacheDir = $this->cacheDir($name); - if (file_exists($cacheDir)) { - $filesystem->remove($cacheDir); - } - mkdir($cacheDir, 0777, true); - $filesystem->mirror($this->workspaceDir(), $this->cacheDir($name)); - } -} diff --git a/tests/PHPStan/Rule/NoDumpRule.php b/tests/PHPStan/Rule/NoDumpRule.php deleted file mode 100644 index 8e256f076f..0000000000 --- a/tests/PHPStan/Rule/NoDumpRule.php +++ /dev/null @@ -1,40 +0,0 @@ - - */ -class NoDumpRule implements Rule -{ - private const DISALLOWED_CALLS = ['dump', 'dd', 'var_dump', 'exit', 'die']; - - public function getNodeType(): string - { - return FuncCall::class; - } - - public function processNode(Node $node, Scope $scope): array - { - assert($node instanceof FuncCall); - - if (!$node->name instanceof Name) { - return []; - } - if (!in_array($node->name->__toString(), self::DISALLOWED_CALLS)) { - return []; - } - return [ - RuleErrorBuilder::message( - sprintf('Function call "%s" is not allowed', $node->name->__toString()) - )->build(), - ]; - } -} diff --git a/tests/Smoke/RpcHandlerTest.php b/tests/Smoke/RpcHandlerTest.php deleted file mode 100644 index a230e17497..0000000000 --- a/tests/Smoke/RpcHandlerTest.php +++ /dev/null @@ -1,53 +0,0 @@ -container()->expect('rpc.handler_registry', HandlerRegistry::class); - $registry->get($name); - $this->addToAssertionCount(1); - } - - /** - * @return Generator - */ - public static function provideName(): Generator - { - yield [ 'cache_clear' ]; - yield [ 'class_inflect' ]; - yield [ 'class_new' ]; - yield [ 'class_search' ]; - yield [ 'complete' ]; - yield [ 'config' ]; - yield [ 'context_menu' ]; - yield [ 'copy_class' ]; - yield [ 'echo' ]; - yield [ 'extract_constant' ]; - yield [ 'extract_expression' ]; - yield [ 'extract_method' ]; - yield [ 'file_info' ]; - yield [ 'generate_accessor' ]; - yield [ 'generate_method' ]; - yield [ 'goto_definition' ]; - yield [ 'import_class' ]; - yield [ 'move_class' ]; - yield [ 'navigate' ]; - yield [ 'offset_info' ]; - yield [ 'override_method' ]; - yield [ 'references' ]; - yield [ 'rename_variable' ]; - yield [ 'status' ]; - yield [ 'transform' ]; - yield [ 'hover' ]; - yield [ 'change_visibility' ]; - } -} diff --git a/tests/System/Configuration/ConfigSuggestCommandTest.php b/tests/System/Configuration/ConfigSuggestCommandTest.php deleted file mode 100644 index c89e50a93e..0000000000 --- a/tests/System/Configuration/ConfigSuggestCommandTest.php +++ /dev/null @@ -1,101 +0,0 @@ -workspace()->reset(); - } - - public function testSuggestWhereFileNotExisting(): void - { - $this->phpactor(['config:auto'])->mustRun(); - - $this->addToAssertionCount(1); - } - /** - * @param array $composerJson - * @param Closure(JsonConfig): void $assertion - */ - #[DataProvider('provideSuggest')] - public function testSuggest(array $composerJson, Closure $assertion): void - { - $this->workspace()->put('composer.lock', (string)json_encode($composerJson)); - $phpactor = $this->phpactor(['config:auto']); - $phpactor->mustRun(); - self::assertStringContainsString('1 changes applied', $phpactor->getErrorOutput()); - $this->addToAssertionCount(1); - $assertion(JsonConfig::fromPath($this->workspace()->path('.phpactor.json'))); - } - /** - * @return Generator,Closure(JsonConfig): void}> - */ - public static function provideSuggest(): Generator - { - yield 'phpstan' => [ - ['packages' => [['name' => 'phpstan/phpstan', 'version' => '^1.0']]], - function (JsonConfig $phpactorConfig): void { - self::assertTrue($phpactorConfig->has(LanguageServerPhpstanExtension::PARAM_ENABLED)); - } - ]; - yield 'psalm' => [ - ['packages' => [['name' => 'vimeo/psalm', 'version' => '^1.0']]], - function (JsonConfig $phpactorConfig): void { - self::assertTrue($phpactorConfig->has(LanguageServerPsalmExtension::PARAM_ENABLED)); - } - ]; - yield 'php-cs-fixer' => [ - ['packages' => [['name' => 'friendsofphp/php-cs-fixer', 'version' => '^1.0']]], - function (JsonConfig $phpactorConfig): void { - self::assertTrue($phpactorConfig->has(LanguageServerPhpCsFixerExtension::PARAM_ENABLED)); - } - ]; - yield 'prophecy' => [ - ['packages' => [['name' => 'phpspec/prophecy', 'version' => '^1.0']]], - function (JsonConfig $phpactorConfig): void { - self::assertTrue($phpactorConfig->has(ProphecyExtension::PARAM_ENABLED)); - } - ]; - yield 'behat' => [ - ['packages' => [['name' => 'behat/behat', 'version' => '^1.0']]], - function (JsonConfig $phpactorConfig): void { - self::assertTrue($phpactorConfig->has(BehatExtension::PARAM_ENABLED)); - } - ]; - } - - public function testSymfonyExtensionSuggestion(): void - { - $this->workspace()->put('var/cache/dev/App_KernelDevDebugContainer.xml', ''); - $phpactor = $this->phpactor(['config:auto']); - $phpactor->mustRun(); - self::assertStringContainsString('2 changes applied', $phpactor->getErrorOutput()); - $phpactorConfig = JsonConfig::fromPath($this->workspace()->path('.phpactor.json')); - self::assertTrue($phpactorConfig->has(SymfonyExtension::PARAM_ENABLED)); - } - - public function testDoNotSuggestPhpstanIfAlreadyDisabled(): void - { - $this->workspace()->put('composer.json', '{"require-dev": {"phpstan/phpstan": "^1.0"}}'); - $this->workspace()->put('.phpactor.json', sprintf('{"%s": false}', LanguageServerPhpstanExtension::PARAM_ENABLED)); - $phpactor = $this->phpactor(['config:auto']); - $phpactor->mustRun(); - self::assertStringContainsString('0 changes applied', $phpactor->getErrorOutput()); - $this->addToAssertionCount(1); - self::assertTrue(JsonConfig::fromPath($this->workspace()->path('.phpactor.json'))->has(LanguageServerPhpstanExtension::PARAM_ENABLED)); - } -} diff --git a/tests/System/Extension/ClassMover/Command/ClassCopyCommandTest.php b/tests/System/Extension/ClassMover/Command/ClassCopyCommandTest.php deleted file mode 100644 index 980b6306de..0000000000 --- a/tests/System/Extension/ClassMover/Command/ClassCopyCommandTest.php +++ /dev/null @@ -1,137 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - /** - * Application level smoke tests - */ - #[DataProvider('provideSmokeSuccess')] - public function testSmokeSuccess(string $command, array $fileMap = [], array $contentExpectations = []): void - { - $process = $this->phpactorFromStringArgs($command); - $this->assertSuccess($process); - - foreach ($fileMap as $filePath => $shouldExist) { - $exists = file_exists($this->workspaceDir() . '/' . $filePath); - - if ($shouldExist) { - $this->assertTrue($exists); - continue; - } - - $this->assertFalse($exists); - } - - foreach ($contentExpectations as $filePath => $contentExpectation) { - $path = $this->workspaceDir() . '/' . $filePath; - $contents = file_get_contents($path); - $this->assertStringContainsString($contentExpectation, $contents); - } - } - - public static function provideSmokeSuccess(): Generator - { - yield 'Copy file 1' => [ - 'class:copy lib/Badger/Carnivorous.php lib/Aardvark/Insectarian.php', - [], - [ - 'lib/Aardvark/Insectarian.php' => 'class Insectarian', - ] - ]; - - yield 'Copy file 2' => [ - 'class:copy lib/Aardvark/Edentate.php lib/Foobar.php', - [ - 'lib/Foobar.php' => true, - 'lib/Aardvark/Edentate.php' => true, - ], - ]; - yield 'Copy file non-existing folder' => [ - 'class:copy lib/Aardvark/Edentate.php lib/Hello/World/Foobar.php', - [ - 'lib/Hello/World/Foobar.php' => true, - ], - ]; - yield 'Copy file to folder' => [ - 'class:copy lib/Aardvark/Edentate.php lib/Hello/World/', - [ - 'lib/Hello/World/Edentate.php' => true, - ], - ]; - yield 'Copy file force' => [ - 'class:copy lib/Aardvark/Edentate.php lib/Foobar.php --type=file', - [], - [], - ]; - yield 'Copy folder 1' => [ - 'class:copy lib/Aardvark lib/Elephant', - [ - 'lib/Aardvark' => true, - 'lib/Elephant/Edentate.php' => true, - ], - ]; - yield 'Copy wildcard' => [ - 'class:copy "lib/*" lib/Foobar', - [ - 'lib/Foobar/Aardvark' => true, - 'lib/Foobar/Badger.php' => true, - 'lib/Badger.php' => true, - ], - ]; - yield 'Copy class by name 1' => [ - 'class:copy "Animals\\Badger\\Carnivorous" "Animals\\Badger\\Vicious"', - [], - [], - ]; - yield 'Copy class by name force' => [ - 'class:copy "Animals\\Badger\\Carnivorous" "Animals\\Badger\\Vicious" --type=class', - [], - [], - ]; - } - - /** - * Application level failures - */ - #[DataProvider('provideSmokeFailure')] - public function testSmokeFailure(string $command, ?string $expectedMessage = null): void - { - $process = $this->phpactorFromStringArgs($command); - $this->assertFailure($process, $expectedMessage); - } - - /** - * @return Generator - */ - public static function provideSmokeFailure(): Generator - { - yield 'Copy class by name force file' => [ - 'mv "Animals\\Badger\\Carnivorous" "Animals\\Badger\\Vicious" --type=file', - null, - ]; - yield 'Copy class by file force class' => [ - 'class:copy lib/Aardvark/Edentate.php lib/Foobar.php --type=class', - null, - ]; - yield 'Copy invalid type' => [ - 'class:copy lib/Aardvark/Edentate.php lib/Foobar.php --type=foobar', - 'Invalid type "foobar", must be one of: "auto", "file", "class"', - ]; - yield 'Copy non-existing' => [ - 'class:copy lib/Aardvark/Blah.php lib/Foobar.php', - 'does not exist', - ]; - } -} diff --git a/tests/System/Extension/ClassMover/Command/ClassMoveCommandTest.php b/tests/System/Extension/ClassMover/Command/ClassMoveCommandTest.php deleted file mode 100644 index b00306781a..0000000000 --- a/tests/System/Extension/ClassMover/Command/ClassMoveCommandTest.php +++ /dev/null @@ -1,151 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - /** - * Application level smoke tests - * - * @param array $fileMap - */ - #[DataProvider('provideSmokeSuccess')] - public function testSmokeSuccess(string $command, array $fileMap): void - { - $process = $this->phpactorFromStringArgs($command); - $this->assertSuccess($process); - - foreach ($fileMap as $filePath => $shouldExist) { - $absFilePath = $this->workspaceDir() . '/' . $filePath; - - if ($shouldExist) { - $this->assertFileExists($absFilePath); - continue; - } - - $this->assertFileDoesNotExist($absFilePath); - } - } - - /** - * @return Generator}> - */ - public static function provideSmokeSuccess(): Generator - { - yield 'Move file 1' => [ - 'class:move lib/Badger/Carnivorous.php lib/Aardvark/Insectarian.php', - [ - 'lib/Badger/Carnivorous.php' => false, - 'lib/Aardvark/Insectarian.php' => true, - ], - ]; - yield 'Move file 2' => [ - 'class:move lib/Aardvark/Edentate.php lib/Foobar.php', - [ - 'lib/Foobar.php' => true, - 'lib/Aardvark/Edentate.php' => false, - ], - ]; - yield 'Move file non-existing folder' => [ - 'class:move lib/Aardvark/Edentate.php lib/Hello/World/Foobar.php', - [ - 'lib/Hello/World/Foobar.php' => true, - ], - ]; - yield 'Move file to folder' => [ - 'class:move lib/Aardvark/Edentate.php lib/Hello/World/', - [ - 'lib/Hello/World/Edentate.php' => true, - ], - ]; - yield 'Move file force' => [ - 'class:move lib/Aardvark/Edentate.php lib/Foobar.php --type=file', - [], - ]; - yield'Move folder 1' => [ - 'class:move lib/Aardvark lib/Elephant', - [ - 'lib/Aardvark' => false, - 'lib/Elephant/Edentate.php' => true, - ], - ]; - yield 'Move wildcard' => [ - 'class:move "lib/*" lib/Foobar', - [ - 'lib/Foobar/Aardvark' => true, - 'lib/Foobar/Badger.php' => true, - 'lib/Badger.php' => false, - ], - ]; - yield 'Move class by name 1' => [ - 'class:move "Animals\\Badger\\Carnivorous" "Animals\\Badger\\Vicious"', - [], - ]; - yield 'Move class by name force' => [ - 'class:move "Animals\\Badger\\Carnivorous" "Animals\\Badger\\Vicious" --type=class', - [], - ]; - } - - public function testOutdatedGitIndex(): void - { - rename($this->workspaceDir() . '/lib/Badger.php', $this->workspaceDir() . '/lib/Crow.php'); - $process = $this->phpactorFromStringArgs('class:move lib/Badger/Carnivorous.php lib/Aardvark/Insectarian.php'); - $this->assertSuccess($process); - } - - public function testMovesRelatedFiles(): void - { - $this->workspace()->put('.phpactor.json', json_encode([ - 'navigator.destinations' => [ - 'source' => 'lib/.php', - 'test' => 'lib/Test.php' - ] - ], JSON_THROW_ON_ERROR)); - $this->workspace()->put('lib/BadgerTest.php', 'phpactorFromStringArgs('class:move lib/Badger.php lib/Fox.php --related'); - $this->assertSuccess($process); - $this->assertFileExists($this->workspace()->path('/lib/FoxTest.php')); - } - - /** - * Application level failures - */ - #[DataProvider('provideSmokeFailure')] - public function testSmokeFailure(string $command, ?string $expectedMessage = null): void - { - $process = $this->phpactorFromStringArgs($command); - $this->assertFailure($process, $expectedMessage); - } - - /** - * @return Generator - */ - public static function provideSmokeFailure(): Generator - { - yield 'Move class by name force file' => [ - 'mv "Animals\\Badger\\Carnivorous" "Animals\\Badger\\Vicious" --type=file', - null, - ]; - - yield 'Move class by file force class' => [ - 'class:move lib/Aardvark/Edentate.php lib/Foobar.php --type=class', - null, - ]; - - yield 'Move invalid type' => [ - 'class:move lib/Aardvark/Edentate.php lib/Foobar.php --type=foobar', - 'Invalid type "foobar", must be one of: "auto", "file", "class"', - ]; - } -} diff --git a/tests/System/Extension/ClassMover/Command/ReferencesClassCommandTest.php b/tests/System/Extension/ClassMover/Command/ReferencesClassCommandTest.php deleted file mode 100644 index cb6cfdca79..0000000000 --- a/tests/System/Extension/ClassMover/Command/ReferencesClassCommandTest.php +++ /dev/null @@ -1,61 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - #[TestDox('It should show all references to Badger')] - public function testReferences(): void - { - $process = $this->phpactorFromStringArgs('references:class "Animals\Badger"'); - $this->assertSuccess($process); - $this->assertStringContainsString('class ⟶Badger⟵', $process->getOutput()); - } - - #[TestDox('It should accept a format')] - public function testReferencesFormatted(): void - { - $process = $this->phpactorFromStringArgs('references:class "Animals\Badger" --format=json'); - $this->assertSuccess($process); - $this->assertStringContainsString('"line":"class Badger', $process->getOutput()); - } - - #[TestDox('It should replace class references')] - public function testReferencesReplace(): void - { - $process = $this->phpactorFromStringArgs('references:class "Animals\Badger" --replace="Kangaroo"'); - $this->assertSuccess($process); - $this->assertStringContainsString('class ⟶Kangaroo⟵', $process->getOutput()); - $this->assertStringContainsString('class Kangaroo', file_get_contents( - $this->workspaceDir() . '/lib/Badger.php' - )); - } - - #[TestDox('It should replace class references')] - public function testReferencesReplaceDryRun(): void - { - $process = $this->phpactorFromStringArgs('references:class "Animals\Badger" --dry-run --replace="Kangaroo"'); - $this->assertSuccess($process); - $this->assertStringContainsString('class ⟶Kangaroo⟵', $process->getOutput()); - $this->assertStringNotContainsString('class Kangaroo', file_get_contents( - $this->workspaceDir() . '/lib/Badger.php' - )); - } - - #[TestDox('It can use a different scope')] - public function testReferencesScope(): void - { - $process = $this->phpactorFromStringArgs('references:class "Animals\Badger" --filesystem=simple'); - $this->assertSuccess($process); - $this->assertStringContainsString('class ⟶Badger⟵', $process->getOutput()); - } -} diff --git a/tests/System/Extension/ClassMover/Command/ReferencesMemberCommandTest.php b/tests/System/Extension/ClassMover/Command/ReferencesMemberCommandTest.php deleted file mode 100644 index ab3c62f9ec..0000000000 --- a/tests/System/Extension/ClassMover/Command/ReferencesMemberCommandTest.php +++ /dev/null @@ -1,90 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - #[TestDox('It should show all references to Badger')] - public function testReferences(): void - { - $process = $this->phpactorFromStringArgs('references:member "Animals\Badger" badge'); - $this->assertSuccess($process); - $this->assertStringContainsString('$this->⟶badge⟵', $process->getOutput()); - } - - #[TestDox('When non-existing member given, suggest existing members with exception.')] - public function testNonExistingMember(): void - { - $process = $this->phpactorFromStringArgs('references:member "Animals\Badger" bad --type="method"'); - $this->assertEquals(255, $process->getExitCode()); - $this->assertStringContainsString('Class has no member named "bad"', $process->getErrorOutput()); - } - - #[TestDox('Find all members for class')] - public function testFindAllForClass(): void - { - $process = $this->phpactorFromStringArgs('references:member "Animals\Badger"'); - $this->assertSuccess($process); - } - - #[TestDox('Find all members')] - public function testFindAll(): void - { - $process = $this->phpactorFromStringArgs('references:member'); - $this->assertSuccess($process); - } - - #[TestDox('Replace member')] - public function testReplace(): void - { - $process = $this->phpactorFromStringArgs('references:member "Animals\Badger" badge --replace=dodge'); - $this->assertSuccess($process); - $this->assertStringContainsString('this->dodge()', file_get_contents( - $this->workspaceDir() . '/lib/Badger.php' - )); - } - - #[TestDox('Replace dry run')] - public function testReplaceDryRun(): void - { - $process = $this->phpactorFromStringArgs('references:member "Animals\Badger" badge --replace=dodge --dry-run'); - $this->assertSuccess($process); - $this->assertStringContainsString('this->badge()', file_get_contents( - $this->workspaceDir() . '/lib/Badger.php' - )); - } - - #[TestDox('It can use a different scope')] - public function testReferencesScope(): void - { - $process = $this->phpactorFromStringArgs('references:member "Animals\Badger" badge --filesystem=composer'); - $this->assertSuccess($process); - $this->assertStringContainsString('⟶badge⟵', $process->getOutput()); - } - - #[TestDox('By property')] - public function testByTypeProperty(): void - { - $process = $this->phpactorFromStringArgs('references:member "Animals\Badger" carnivorous --type=property'); - $this->assertSuccess($process); - $this->assertStringContainsString('⟶carnivorous⟵', $process->getOutput()); - } - - #[TestDox('Find member name shared by different types')] - public function testDifferentTypes(): void - { - $process = $this->phpactorFromStringArgs('references:member "Animals\Badger" carnivorous'); - $this->assertSuccess($process); - $this->assertStringContainsString('$this->⟶carnivorous⟵ = $carnivorous', $process->getOutput()); - $this->assertStringContainsString('public function ⟶carnivorous⟵(', $process->getOutput()); - } -} diff --git a/tests/System/Extension/ClassToFile/Command/FileInfoCommandTest.php b/tests/System/Extension/ClassToFile/Command/FileInfoCommandTest.php deleted file mode 100644 index b8c119c7f1..0000000000 --- a/tests/System/Extension/ClassToFile/Command/FileInfoCommandTest.php +++ /dev/null @@ -1,31 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - #[TestDox('It provides information about the file.')] - public function testProvideInformationForOffset(): void - { - $process = $this->phpactorFromStringArgs('file:info lib/Badger.php'); - $this->assertSuccess($process); - $this->assertStringContainsString('class:Animals\Badger', $process->getOutput()); - } - - #[TestDox('It provides information about the file as JSON')] - public function testProvideInformationForOffsetAsJson(): void - { - $process = $this->phpactorFromStringArgs('file:info lib/Badger.php --format=json'); - $this->assertSuccess($process); - $this->assertStringContainsString('{"class":"Animals', $process->getOutput()); - } -} diff --git a/tests/System/Extension/CodeTransform/Command/ClassInflectCommandTest.php b/tests/System/Extension/CodeTransform/Command/ClassInflectCommandTest.php deleted file mode 100644 index 11f6a6c094..0000000000 --- a/tests/System/Extension/CodeTransform/Command/ClassInflectCommandTest.php +++ /dev/null @@ -1,96 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - /** - * Application level smoke tests - */ - #[DataProvider('provideInflectClass')] - public function testInflectClass(string $command, string $expectedFilePath, string $expectedContents): void - { - $process = $this->phpactorFromStringArgs($command); - $this->assertSuccess($process); - - $expectedFilePath = $this->workspaceDir() . '/' . $expectedFilePath; - $this->assertSuccess($process); - $this->assertFileExists($expectedFilePath); - $this->assertStringContainsString($expectedContents, (string) file_get_contents($expectedFilePath)); - } - - /** - * @return Generator - */ - public static function provideInflectClass(): Generator - { - yield 'Glob' => [ - 'class:inflect "lib/Badger/*.php" lib/Badger/Api interface', - 'lib/Badger/Api/Carnivorous.php', - <<<'EOT' - interface Carnivorous - EOT - ]; - - yield 'Glob with directories' => [ - 'class:inflect "lib/*" lib/Api interface', - 'lib/Api/Badger.php', - <<<'EOT' - interface Badger - EOT - ]; - - yield 'Inflect class' => [ - 'class:inflect lib/Badger/Carnivorous.php lib/Badger/Api/CarnivorousInterface.php interface', - 'lib/Badger/Api/CarnivorousInterface.php', - <<<'EOT' - interface CarnivorousInterface - EOT - ]; - } - - #[TestDox('It does not overwrite existing file unless forced.')] - public function testInflectClassExistingAndForce(): void - { - $filePath = 'lib/Badger/Carnivorous.php'; - $process = $this->phpactorFromStringArgs('class:inflect '.$filePath. ' ' . $filePath . ' interface --no-interaction'); - $this->assertSuccess($process); - $this->assertStringContainsString('exists:true', $process->getOutput()); - $this->assertStringNotContainsString('interface', (string) file_get_contents($filePath)); - - $process = $this->phpactorFromStringArgs('class:inflect '.$filePath. ' ' . $filePath . ' interface --force'); - $this->assertStringContainsString('interface', (string) file_get_contents($filePath)); - } - - /** - * Application level failures - */ - #[DataProvider('provideSmokeFailure')] - public function testSmokeFailure(string $command, ?string $expectedMessage = null): void - { - $process = $this->phpactorFromStringArgs($command); - $this->assertFailure($process, $expectedMessage); - } - - /** - * @return Generator - */ - public static function provideSmokeFailure(): Generator - { - yield 'non-existing' => [ - 'class:inflect lib/Badger/BooNotExist.php lib/Badger/Api/CarnivorousInterface.php interface', - 'does not exist', - ]; - } -} diff --git a/tests/System/Extension/CodeTransform/Command/ClassNewCommandTest.php b/tests/System/Extension/CodeTransform/Command/ClassNewCommandTest.php deleted file mode 100644 index 934798b6e7..0000000000 --- a/tests/System/Extension/CodeTransform/Command/ClassNewCommandTest.php +++ /dev/null @@ -1,70 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - $this->workspace()->put('.phpactor/templates/foobar/SourceCode.php.twig', 'Foobar'); - $this->workspace()->put( - '.phpactor.json', - <<phpactorFromStringArgs($command); - $this->assertSuccess($process); - - $this->assertStringContainsString($expected, trim($process->getOutput())); - $this->assertFileExists($this->workspaceDir() . $expectedFile); - } - - /** - * @return Generator - */ - public static function provideNewClass(): Generator - { - yield 'New class' => [ - 'class:new lib/Badger/Teeth.php --no-interaction --force', - <<<'EOT' - src:lib/Badger/Teeth.php - EOT - , '/lib/Badger/Teeth.php' - ]; - - yield 'New class with variant' => [ - 'class:new lib/Badger/Teeth.php --variant=foobar --no-interaction --force', - <<<'EOT' - src:lib/Badger/Teeth.php - EOT - , '/lib/Badger/Teeth.php' - ]; - - yield 'New class from FQN' => [ - 'class:new "Animals\\Pigeon" --no-interaction', - <<<'EOT' - lib/Pigeon.php - EOT - , '/lib/Pigeon.php' - ]; - } -} diff --git a/tests/System/Extension/CodeTransform/Command/ClassTransformCommandTest.php b/tests/System/Extension/CodeTransform/Command/ClassTransformCommandTest.php deleted file mode 100644 index dda194bff9..0000000000 --- a/tests/System/Extension/CodeTransform/Command/ClassTransformCommandTest.php +++ /dev/null @@ -1,80 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - file_put_contents( - $this->workspace()->path('lib/Foobar.php'), - <<<'EOT' - phpactorFromStringArgs($command); - - if ($error) { - $this->assertStringContainsString($expectedOutput, $process->getErrorOutput()); - return; - } - - $this->assertSuccess($process); - $this->assertStringContainsString($expectedOutput, $process->getOutput()); - } - - /** - * @return Generator - */ - public static function provideSmokeSuccess(): Generator - { - yield 'No arguments' => [ - 'class:transform lib/Foobar.php', - '0 files affected', - ]; - - yield 'Implement contracts' => [ - 'class:transform lib/Foobar.php --transform=implement_contracts', - '1 files affected', - ]; - - yield 'Glob' => [ - 'class:transform "lib/*.php" --transform=implement_contracts', - '1 files affected', - ]; - - yield 'Dry run' => [ - 'class:transform "lib/**/*.php" --dry-run --transform=implement_contracts', - '1 files affected (dry run)', - ]; - - yield 'Diff' => [ - 'class:transform "lib/*.php" --diff --transform=implement_contracts', - 'public function count', - ]; - - yield 'Non-existing file' => [ - 'class:transform "lib/BarNotExisting.php" --diff --transform=implement_contracts', - 'does not exist', - true - ]; - } -} diff --git a/tests/System/Extension/Completion/Application/CompleteTest.php b/tests/System/Extension/Completion/Application/CompleteTest.php deleted file mode 100644 index 0b1272faee..0000000000 --- a/tests/System/Extension/Completion/Application/CompleteTest.php +++ /dev/null @@ -1,290 +0,0 @@ -complete($source)['suggestions']; - usort($suggestions, function ($one, $two) { - return $one['name'] <=> $two['name']; - }); - - if (!$expected) { - $this->assertEmpty($suggestions); - } - - $this->assertGreaterThanOrEqual(count($expected), count($suggestions), 'Got more suggestions than expected'); - foreach ($expected as $index => $expectedSuggestion) { - $this->assertArraySubset($expectedSuggestion, $suggestions[$index]); - } - } - /** - * @return Generator> - */ - public static function provideComplete(): Generator - { - yield 'Public property' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => 'property', - 'name' => 'foo', - 'short_description' => 'pub $foo', - ] - ] - ]; - yield 'Private property' => [ - <<<'EOT' - <> - - EOT - , - [ ] - ]; - yield 'Public property access' => [ - <<<'EOT' - foo-><> - - EOT - , [ - [ - 'type' => 'property', - 'name' => 'bar', - 'short_description' => 'pub $bar', - ] - ] - ]; - yield 'Public method with parameters' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => 'method', - 'name' => 'foo', - 'short_description' => 'pub foo(string $zzzbar = \'bar\', $def): Barbar', - ] - ] - ]; - yield 'Public method multiple return types' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => 'method', - 'name' => 'foo', - 'short_description' => 'pub foo(): Foobar|Barbar', - ] - ] - ]; - yield 'Private method' => [ - <<<'EOT' - <> - - EOT - , [ - ] - ]; - yield 'Static property' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => 'property', - 'name' => '$foo', - 'short_description' => 'pub static $foo', - ], - [ - 'type' => 'constant', - 'name' => 'class', - 'short_description' => 'Foobar', - ], - ] - ]; - yield 'Static property with previous arrow accessor' => [ - <<<'EOT' - me::<> - - EOT - , [ - [ - 'type' => 'property', - 'name' => '$foo', - 'short_description' => 'pub static $foo', - ], - [ - 'type' => 'constant', - 'name' => 'class', - 'short_description' => 'Foobar', - ], - ] - ]; - yield 'Complete from static call' => [ - <<<'EOT' - - - EOT - , [ - [ - 'type' => 'constant', - 'name' => 'BARFOO', - 'short_description' => 'BARFOO = "barfoo"', - ], - [ - 'type' => 'constant', - 'name' => 'FOOBAR', - 'short_description' => 'FOOBAR = "foobar"', - ], - ], - ]; - yield 'Accessor on new line' => [ - <<<'EOT' - <> - - EOT - , [ - [ - 'type' => 'property', - 'name' => 'foobar', - 'short_description' => 'pub $foobar', - ], - ], - ]; - } - /** - * @return array{suggestions:array>,issues:array} - */ - private function complete(string $source): array - { - [$source, $offset] = ExtractOffset::fromSource($source); - $complete = $this->container()->get('application.complete'); - assert($complete instanceof Complete); - $result = $complete->complete($source, $offset); - - return $result; - } -} diff --git a/tests/System/Extension/Completion/Command/CompleteCommandTest.php b/tests/System/Extension/Completion/Command/CompleteCommandTest.php deleted file mode 100644 index 438321b73d..0000000000 --- a/tests/System/Extension/Completion/Command/CompleteCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - #[DataProvider('provideComplete')] - public function testComplete(string $command, string $expected): void - { - $process = $this->phpactorFromStringArgs($command); - $this->assertSuccess($process); - $this->assertStringContainsString($expected, trim($process->getOutput())); - } - - /** - * @return Generator - */ - public static function provideComplete(): Generator - { - yield 'Complete' => [ - 'complete lib/Badger.php 181', - <<<'EOT' - suggestions: - EOT - ]; - yield 'Complete with type' => [ - 'complete lib/Badger.php 181 --type=cucumber', - <<<'EOT' - suggestions: - EOT - ]; - } -} diff --git a/tests/System/Extension/Core/Command/CacheClearCommandTest.php b/tests/System/Extension/Core/Command/CacheClearCommandTest.php deleted file mode 100644 index f074d53b6a..0000000000 --- a/tests/System/Extension/Core/Command/CacheClearCommandTest.php +++ /dev/null @@ -1,21 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - public function testCacheClear(): void - { - $process = $this->phpactorFromStringArgs('cache:clear'); - $this->assertSuccess($process); - $this->assertStringContainsString('Cache cleared', $process->getOutput()); - } -} diff --git a/tests/System/Extension/Core/Command/ConfigDumpCommandTest.php b/tests/System/Extension/Core/Command/ConfigDumpCommandTest.php deleted file mode 100644 index b518a3a75b..0000000000 --- a/tests/System/Extension/Core/Command/ConfigDumpCommandTest.php +++ /dev/null @@ -1,25 +0,0 @@ -phpactorFromStringArgs('config:dump'); - $this->assertSuccess($process); - $this->assertStringContainsString('Config files', $process->getOutput()); - } - - #[TestDox('It should dump only configuration')] - public function testConfigDumpOnly(): void - { - $process = $this->phpactorFromStringArgs('config:dump --config-only'); - $this->assertSuccess($process); - $config = json_decode($process->getOutput(), true); - $this->assertIsArray($config); - } -} diff --git a/tests/System/Extension/Core/Command/ConfigInitializeCommandTest.php b/tests/System/Extension/Core/Command/ConfigInitializeCommandTest.php deleted file mode 100644 index 23732bcb2d..0000000000 --- a/tests/System/Extension/Core/Command/ConfigInitializeCommandTest.php +++ /dev/null @@ -1,14 +0,0 @@ -phpactorFromStringArgs('config:initialize'); - $this->assertSuccess($process); - } -} diff --git a/tests/System/Extension/Core/Command/ConfigSetCommandTest.php b/tests/System/Extension/Core/Command/ConfigSetCommandTest.php deleted file mode 100644 index 7d3e37ed9c..0000000000 --- a/tests/System/Extension/Core/Command/ConfigSetCommandTest.php +++ /dev/null @@ -1,14 +0,0 @@ -phpactorFromStringArgs('config:set foo true'); - $this->assertSuccess($process); - } -} diff --git a/tests/System/Extension/Core/Command/ContainerDumpCommandTest.php b/tests/System/Extension/Core/Command/ContainerDumpCommandTest.php deleted file mode 100644 index 527431465b..0000000000 --- a/tests/System/Extension/Core/Command/ContainerDumpCommandTest.php +++ /dev/null @@ -1,14 +0,0 @@ -phpactorFromStringArgs('container:dump --services --tags --tag=worse_reflection.source_locator'); - $this->assertSuccess($process); - } -} diff --git a/tests/System/Extension/Core/Command/StatusCommandTest.php b/tests/System/Extension/Core/Command/StatusCommandTest.php deleted file mode 100644 index d9c6b10642..0000000000 --- a/tests/System/Extension/Core/Command/StatusCommandTest.php +++ /dev/null @@ -1,14 +0,0 @@ -phpactorFromStringArgs('status'); - $this->assertSuccess($process); - } -} diff --git a/tests/System/Extension/Core/Command/TrustCommandTest.php b/tests/System/Extension/Core/Command/TrustCommandTest.php deleted file mode 100644 index 8cfcc61383..0000000000 --- a/tests/System/Extension/Core/Command/TrustCommandTest.php +++ /dev/null @@ -1,14 +0,0 @@ -phpactorFromStringArgs('config:trust --trust'); - $this->assertSuccess($process); - } -} diff --git a/tests/System/Extension/Rpc/Command/RpcCommandTest.php b/tests/System/Extension/Rpc/Command/RpcCommandTest.php deleted file mode 100644 index fd3c2d698b..0000000000 --- a/tests/System/Extension/Rpc/Command/RpcCommandTest.php +++ /dev/null @@ -1,77 +0,0 @@ - 'echo', - 'parameters' => [ - 'message' => 'Hello World', - ], - ]); - - $process = $this->phpactorFromStringArgs('rpc', $stdin); - $this->assertSuccess($process); - - $response = json_decode($process->getOutput(), true); - - $this->assertEquals([ - 'action' => 'echo', - 'parameters' => [ - 'message' => 'Hello World', - ], - 'version' => RpcVersion::asString(), - ], $response); - } - - public function testPrettyPrintsOutput(): void - { - $stdin = json_encode([ - 'action' => 'echo', - 'parameters' => [ - 'message' => 'Hello World', - ], - ]); - - $process = $this->phpactorFromStringArgs('rpc --pretty', $stdin); - $this->assertSuccess($process); - } - - public function testReplaysLastRequest(): void - { - // enable the feature - file_put_contents($this->workspace()->path('.phpactor.yml'), 'rpc.store_replay: true'); - - $randomString = md5(rand(0, 100000)); - $stdin = json_encode([ - 'action' => 'echo', - 'parameters' => [ - 'message' => $randomString, - ], - ]); - - $process = $this->phpactorFromStringArgs('rpc', $stdin); - $this->assertSuccess($process); - - $process = $this->phpactorFromStringArgs('rpc --replay'); - $this->assertSuccess($process); - $response = json_decode($process->getOutput(), true); - - $this->assertEquals([ - 'action' => 'echo', - 'parameters' => [ - 'message' => $randomString, - ], - 'version' => RpcVersion::asString(), - ], $response); - } -} diff --git a/tests/System/Extension/SourceCodeFilesystem/Command/ClassSearchCommandTest.php b/tests/System/Extension/SourceCodeFilesystem/Command/ClassSearchCommandTest.php deleted file mode 100644 index 5bc4a59f04..0000000000 --- a/tests/System/Extension/SourceCodeFilesystem/Command/ClassSearchCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - #[TestDox('It should return information baesd on a class "short" name.')] - public function testSearchName(): void - { - $process = $this->phpactorFromStringArgs('class:search "Badger"'); - $this->assertSuccess($process); - $this->assertStringContainsString('Badger.php', $process->getOutput()); - } - - #[TestDox('It should return information baesd on a class "short" name.')] - public function testSearchNameJson(): void - { - $process = $this->phpactorFromStringArgs('class:search "Badger" --format=json'); - $this->assertSuccess($process); - $this->assertStringContainsString('Badger.php"', $process->getOutput()); - } - - public function testSearchByQualifiedName(): void - { - $process = $this->phpactorFromStringArgs('class:search "Badger\\Carnivorous" --format=json'); - $this->assertSuccess($process); - $this->assertStringContainsString('Carnivorous.php"', $process->getOutput()); - } - - #[TestDox('It should return information baesd on a class "short" name.')] - public function testSearchNameInternalName(): void - { - $process = $this->phpactorFromStringArgs('class:search "DateTime" --format=json'); - $this->assertSuccess($process); - $this->assertStringContainsString('DateTime', $process->getOutput()); - } -} diff --git a/tests/System/Extension/WorseReflection/Command/ClassReflectorCommandTest.php b/tests/System/Extension/WorseReflection/Command/ClassReflectorCommandTest.php deleted file mode 100644 index 880bad0b56..0000000000 --- a/tests/System/Extension/WorseReflection/Command/ClassReflectorCommandTest.php +++ /dev/null @@ -1,35 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - #[TestDox('Test reflection')] - public function testReflectCommand(): void - { - $process = $this->phpactorFromStringArgs('class:reflect lib/Badger.php'); - $this->assertSuccess($process); - $output = $process->getOutput(); - $this->assertStringContainsString('Animals\Badger', $output); - $this->assertStringContainsString('methods', $output); - } - - #[TestDox('Test for class')] - public function testReflectCommandWithClass(): void - { - $process = $this->phpactorFromStringArgs('class:reflect "Animals\\Badger"'); - $this->assertSuccess($process); - $output = $process->getOutput(); - $this->assertStringContainsString('Animals\Badger', $output); - $this->assertStringContainsString('methods', $output); - } -} diff --git a/tests/System/Extension/WorseReflection/Command/OffsetInfoCommandTest.php b/tests/System/Extension/WorseReflection/Command/OffsetInfoCommandTest.php deleted file mode 100644 index a98838d8b2..0000000000 --- a/tests/System/Extension/WorseReflection/Command/OffsetInfoCommandTest.php +++ /dev/null @@ -1,32 +0,0 @@ -workspace()->reset(); - $this->loadProject('Animals'); - } - - #[TestDox('It provides information about the thing under the cursor.')] - public function testProvideInformationForOffset(): void - { - $process = $this->phpactorFromStringArgs('offset:info lib/Badger.php 163'); - $this->assertSuccess($process); - $this->assertStringContainsString('type:Animals\Badger\Carnivorous', $process->getOutput()); - $this->assertStringContainsString('Badger/Carnivorous.php', $process->getOutput()); - } - - #[TestDox('It provides information about the thing under the cursor as JSON')] - public function testProvideInformationForOffsetAsJson(): void - { - $process = $this->phpactorFromStringArgs('offset:info lib/Badger.php 137 --format=json'); - $this->assertSuccess($process); - $this->assertStringContainsString('{"symbol":"__construct', $process->getOutput()); - } -} diff --git a/tests/System/SystemTestCase.php b/tests/System/SystemTestCase.php deleted file mode 100644 index e947d0d080..0000000000 --- a/tests/System/SystemTestCase.php +++ /dev/null @@ -1,34 +0,0 @@ -workspaceDir()); - - $bin = __DIR__ . '/../../bin/phpactor --no-ansi --verbose '; - $process = Process::fromShellCommandline(sprintf( - '%s %s %s', - PHP_BINARY, - $bin, - $args - ), null, [ - 'XDG_CACHE_HOME' => __DIR__ . '/../Assets/Cache', - 'XDG_DATA_HOME' => $this->workspace()->path(), - 'PHPACTOR_UNCONDITIONAL_TRUST' => true, - ]); - - if ($stdin) { - $process->setInput($stdin); - } - - $process->run(); - - return $process; - } -} diff --git a/tests/Unit/Extension/ClassMover/Application/Finder/FileFinderTest.php b/tests/Unit/Extension/ClassMover/Application/Finder/FileFinderTest.php deleted file mode 100644 index 877117f6d9..0000000000 --- a/tests/Unit/Extension/ClassMover/Application/Finder/FileFinderTest.php +++ /dev/null @@ -1,114 +0,0 @@ - - */ - private ObjectProphecy $filesystem; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $fileList; - - public function setUp(): void - { - $this->filesystem = $this->prophesize(Filesystem::class); - $this->fileList = $this->prophesize(FileList::class); - } - - public function testReturnsAllPhpFilesIfNoMemberNameGiven(): void - { - $this->setupAllFiles(); - $class = $this->reflectClass('class Foobar {}', 'Foobar'); - $files = $this->filesFor($class, null); - $this->assertEquals($this->fileList->reveal(), $files); - } - - public function testReturnsAllPhpFilesIfNoClassReflectionGiven(): void - { - $this->setupAllFiles(); - $files = $this->filesFor(null, null); - $this->assertEquals($this->fileList->reveal(), $files); - } - - public function testThrowsExceptionIfClassHasNoMembersByName(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Class has no member named "foobar"'); - $this->setupAllFiles(); - - $class = $this->reflectClass('class Foobar { public function bar() {} }', 'Foobar'); - $files = $this->filesFor($class, 'foobar'); - $this->assertEquals($this->fileList->reveal(), $files); - } - - public function testReturnsAllPhpFilesFilteredByMemberIfMemberIsPublic(): void - { - $this->setupAllFiles(); - $class = $this->reflectClass('class Foobar { public function abcde() {} }', 'Foobar'); - $this->fileList->filter(Argument::type(Closure::class))->willReturn($this->fileList->reveal()); - $files = $this->filesFor($class, 'abcde'); - $this->assertEquals($this->fileList->reveal(), $files); - } - - public function testReturnsClassAndTraitFilePathsIfMemberIsPrivate(): void - { - $class = $this->reflectClass( - TextDocumentBuilder::create('uri('file:///barfoo.php')->build(), - 'Foobar' - ); - $files = $this->filesFor($class, 'foobar'); - $this->assertEquals(FileList::fromFilePaths(['barfoo', 'barfoo']), $files); - } - - public function testParentsTraitsAndInterfacesIfMemberIsProtected(): void - { - $class = $this->reflectClass( - TextDocumentBuilder::create('uri('file:///barfoo')->build(), - 'Foobar' - ); - $files = $this->filesFor($class, 'foobar'); - $this->assertEquals(FileList::fromFilePaths(['barfoo', 'barfoo', 'barfoo', 'barfoo']), $files); - } - - private function filesFor(?ReflectionClassLike $class = null, ?string $memberName = null): FileList - { - return (new FileFinder())->filesFor($this->filesystem->reveal(), $class, $memberName); - } - - private function setupAllFiles(): void - { - $this->filesystem->fileList()->willReturn($this->fileList->reveal()); - $this->fileList->existing()->willReturn($this->fileList->reveal()); - $this->fileList->phpFiles()->willReturn($this->fileList->reveal()); - } - - private function reflectClass(string|TextDocument $source, string $name): ReflectionClassLike - { - if (is_string($source)) { - $source = 'addSource(TextDocumentBuilder::fromUnknown($source)); - return $builder->build()->reflectClassLike($name); - } -} diff --git a/tests/Unit/Extension/ClassMover/Command/Logger/SymfonyConsoleMoveLoggerTest.php b/tests/Unit/Extension/ClassMover/Command/Logger/SymfonyConsoleMoveLoggerTest.php deleted file mode 100644 index 7fd673966a..0000000000 --- a/tests/Unit/Extension/ClassMover/Command/Logger/SymfonyConsoleMoveLoggerTest.php +++ /dev/null @@ -1,80 +0,0 @@ -output = new BufferedOutput(); - $this->logger = new SymfonyConsoleMoveLogger($this->output); - } - - public function testReplacing(): void - { - $references = new FoundReferences( - TextDocumentBuilder::create( - <<<'EOT' - source; - } - - public function targetName(): FullyQualifiedName - { - return $this->name; - } - - public function references(): NamespacedClassRefList - { - return $this->references; - } - } - EOT - )->build(), - FullyQualifiedName::fromString('Acme'), - NamespacedClassReferences::fromNamespaceAndClassRefs( - NamespaceReference::fromNameAndPosition(Namespace_::fromString('Foobar'), Position::fromStartAndEnd(10, 20)), - [ - ClassReference::fromNameAndPosition( - QualifiedName::fromString('Hello'), - FullyQualifiedName::fromString('Foobar\Hello'), - Position::fromStartAndEnd(18, 20), - ImportedNameReference::none(), - false - ) - ] - ) - ); - - $target = FullyQualifiedName::fromString('Hello\World'); - $this->logger->replacing(FilePath::fromString('/path/to/file/Something.php'), $references, $target); - $output = $this->output->fetch(); - $this->assertStringContainsString('Hello => World', $output); - } -} diff --git a/tests/Unit/Extension/ClassMover/Rpc/ClassCopyHandlerTest.php b/tests/Unit/Extension/ClassMover/Rpc/ClassCopyHandlerTest.php deleted file mode 100644 index dcfe3e7092..0000000000 --- a/tests/Unit/Extension/ClassMover/Rpc/ClassCopyHandlerTest.php +++ /dev/null @@ -1,77 +0,0 @@ - - */ - private ObjectProphecy $classCopy; - - public function setUp(): void - { - $this->classCopy = $this->prophesize(ClassCopy::class); - } - - public function createHandler(): Handler - { - return new ClassCopyHandler( - $this->classCopy->reveal() - ); - } - - #[TestDox('It should request the dest path if none is given.')] - public function testNoDestPath(): void - { - /** @var InputCallbackAction $action */ - $action = $this->handle('copy_class', [ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => null, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $this->assertCount(1, $inputs); - $this->assertInstanceOf(TextInput::class, reset($inputs)); - $this->assertInstanceOf(Request::class, $action->callbackAction()); - $this->assertEquals('copy_class', $action->callbackAction()->name()); - $this->assertEquals([ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => null, - ], $action->callbackAction()->parameters()); - } - - public function testCopyClass(): void - { - $this->classCopy->copy( - Argument::type(NullLogger::class), - self::SOURCE_PATH, - self::DEST_PATH - )->shouldBeCalled(); - - /** @var $action InputCallbackAction */ - $action = $this->handle('copy_class', [ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => self::DEST_PATH, - ]); - - $this->assertInstanceOf(OpenFileResponse::class, $action); - } -} diff --git a/tests/Unit/Extension/ClassMover/Rpc/ClassMoveHandlerTest.php b/tests/Unit/Extension/ClassMover/Rpc/ClassMoveHandlerTest.php deleted file mode 100644 index deb4db47d2..0000000000 --- a/tests/Unit/Extension/ClassMover/Rpc/ClassMoveHandlerTest.php +++ /dev/null @@ -1,167 +0,0 @@ -classMover = $this->prophesize(ClassMover::class); - $this->classMover->getRelatedFiles(self::SOURCE_PATH)->willReturn([]); - } - - public function createHandler(): Handler - { - return new ClassMoveHandler( - $this->classMover->reveal(), - SourceCodeFilesystemExtension::FILESYSTEM_GIT - ); - } - - public function testNotConfirmed(): void - { - /** @var InputCallbackAction $action */ - $action = $this->handle('move_class', [ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => null, - 'confirmed' => false, - ]); - - $this->assertInstanceOf(EchoResponse::class, $action); - $this->assertStringContainsString('Cancelled', $action->message()); - } - - public function testConfirmChallenge(): void - { - /** @var $action InputCallbackAction */ - $action = $this->handle('move_class', [ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => self::DEST_PATH, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $this->assertCount(1, $inputs); - $this->assertInstanceOf(ConfirmInput::class, reset($inputs)); - $this->assertInstanceOf(Request::class, $action->callbackAction()); - $this->assertEquals('move_class', $action->callbackAction()->name()); - } - - #[TestDox('It should request the dest path if none is given.')] - public function testNoDestPath(): void - { - /** @var $action InputCallbackAction */ - $action = $this->handle('move_class', [ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => null, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $this->assertCount(1, $inputs); - $this->assertInstanceOf(TextInput::class, reset($inputs)); - $this->assertInstanceOf(Request::class, $action->callbackAction()); - $this->assertEquals('move_class', $action->callbackAction()->name()); - $this->assertEquals([ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => null, - 'confirmed' => null, - 'move_related' => null - ], $action->callbackAction()->parameters()); - } - - public function testItShouldAskForConfirmation(): void - { - $this->classMover->move( - Argument::type(ClassMoverLogger::class), - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - self::SOURCE_PATH, - self::DEST_PATH, - false - )->shouldBeCalled(); - - /** @var $action StackAction */ - $action = $this->handle('move_class', [ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => self::DEST_PATH, - 'confirmed' => true, - ]); - - $this->assertInstanceOf(CollectionResponse::class, $action); - $actions = $action->actions(); - - $action = array_shift($actions); - $this->assertInstanceOf(OpenFileResponse::class, $action); - $this->assertEquals(self::DEST_PATH, $action->path()); - - $action = array_shift($actions); - $this->assertInstanceOf(CloseFileResponse::class, $action); - $this->assertEquals(self::SOURCE_PATH, $action->path()); - } - - public function testItAskIfRelatedFilesShouldBeMoved(): void - { - $this->classMover->getRelatedFiles(self::SOURCE_PATH)->willReturn([ - 'foobar.php', - ]); - - /** @var $action StackAction */ - $action = $this->handle('move_class', [ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => self::DEST_PATH, - 'confirmed' => true, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $input = reset($inputs); - $this->assertInstanceOf(ConfirmInput::class, $input); - $this->assertEquals('move_related', $input->name()); - } - - public function testMovesRelatedFiles(): void - { - $this->classMover->move( - Argument::type(ClassMoverLogger::class), - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - self::SOURCE_PATH, - self::DEST_PATH, - true - )->shouldBeCalled(); - - $this->classMover->getRelatedFiles(self::SOURCE_PATH)->willReturn([ - 'foobar.php', - ]); - - /** @var $action StackAction */ - $action = $this->handle('move_class', [ - 'source_path' => self::SOURCE_PATH, - 'dest_path' => self::DEST_PATH, - 'confirmed' => true, - 'move_related' => true, - ]); - } -} diff --git a/tests/Unit/Extension/ClassMover/Rpc/ReferencesHandlerTest.php b/tests/Unit/Extension/ClassMover/Rpc/ReferencesHandlerTest.php deleted file mode 100644 index 3f365f694e..0000000000 --- a/tests/Unit/Extension/ClassMover/Rpc/ReferencesHandlerTest.php +++ /dev/null @@ -1,488 +0,0 @@ -classReferences = $this->prophesize(ClassReferences::class); - $this->classMemberReferences = $this->prophesize(ClassMemberReferences::class); - $this->logger = new ArrayLogger(); - $this->reflector = ReflectorBuilder::create()->addSource(TextDocumentBuilder::fromUri(__FILE__)->build())->withLogger($this->logger)->build(); - $this->filesystemRegistry = $this->prophesize(FilesystemRegistry::class); - } - - public function createHandler(): Handler - { - return new ReferencesHandler( - $this->reflector, - $this->classReferences->reveal(), - $this->classMemberReferences->reveal(), - $this->filesystemRegistry->reveal() - ); - } - - public function testFilesystemSelection(): void - { - $this->filesystemRegistry->names()->willReturn(['one', 'two']); - - $action = $this->handle('references', [ - 'source' => ' 2173, - 'path' => self::TEST_PATH, - 'filesystem' => null, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $this->assertCount(1, $inputs); - $input = reset($inputs); - $this->assertEquals(ReferencesHandler::PARAMETER_FILESYSTEM, $input->name()); - $this->assertEquals([ 'one' => 'one', 'two' => 'two' ], $input->choices()); - $this->assertEquals('git', $input->default()); - } - - public function testInvalidSymbolType(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Cannot find references for symbol'); - - $action = $this->handle('references', [ - 'source' => ' 1, - 'filesystem' => 'git', - 'path' => self::TEST_PATH, - ]); - } - - public function testClassReturnNoneFound(): void - { - $this->classReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - 'stdClass', - null - )->willReturn([ - 'references' => [], - 'risky_references' => [], - ]); - - $action = $this->handle('references', [ - 'source' => ' 15, - 'filesystem' => 'git', - 'path' => self::TEST_PATH, - ]); - - $this->assertInstanceOf(EchoResponse::class, $action); - } - - public function testClassReferences(): void - { - $this->classReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - 'stdClass', - null - )->willReturn($this->exampleClassResponse()); - - $action = $this->handle('references', [ - 'source' => ' 15, - 'filesystem' => 'git', - 'path' => self::TEST_PATH, - ]); - - $this->assertInstanceOf(CollectionResponse::class, $action); - - $actions = $action->actions(); - - $first = array_shift($actions); - $this->assertInstanceOf(EchoResponse::class, $first); - - $second = array_shift($actions); - $this->assertEquals([ - 'file_references' => [ - [ - 'file' => 'barfoo', - 'references' => [ - [ - 'start' => 10, - 'end' => 20, - 'line' => '', - 'line_no' => 10, - 'col_no' => 12, - ] - ], - ] - ], - ], $second->parameters()); - } - - public function testReplaceClassReferences(): void - { - $source = 'classReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - 'stdClass', - 'newClass', - null - )->willReturn($this->exampleClassResponse()); - - $this->classReferences->replaceInSource( - $source, - 'stdClass', - 'newClass' - )->willReturn($source); - - $action = $this->handle('references', [ - 'source' => $source, - 'offset' => 15, - 'filesystem' => 'git', - 'path' => self::TEST_PATH, - 'mode' => ReferencesHandler::MODE_REPLACE, - 'replacement' => 'newClass', - ]); - - $this->assertInstanceOf(CollectionResponse::class, $action); - } - - public function testMemberReturnNoneFound(): void - { - $this->classMemberReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - __CLASS__, - 'testMemberReturnNoneFound', - ClassMemberQuery::TYPE_METHOD, - null - )->willReturn([ - 'references' => [], - ]); - - $action = $this->handle('references', [ - 'source' => $std = 'testMemberReturnNoneFound();', - 'offset' => 104, - 'path' => self::TEST_PATH, - 'filesystem' => 'git', - ]); - - $this->assertInstanceOf(EchoResponse::class, $action); - } - - public function testMemberReferences(): void - { - $this->classMemberReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - __CLASS__, - 'testMemberReferences', - ClassMemberQuery::TYPE_METHOD, - null - )->willReturn([ - 'references' => [ - [ - 'file' => 'barfoo', - 'references' => [ - [ - 'start' => 10, - 'line_no' => 10, - 'end' => 20, - 'line' => '', - 'col_no' => 12, - ], - ], - ] - ], - ]); - - $action = $this->handle('references', [ - 'source' => $std = 'testMemberReferences();', - 'offset' => 104, - 'path' => self::TEST_PATH, - 'filesystem' => 'git', - ]); - - $this->assertInstanceOf(CollectionResponse::class, $action); - - $actions = $action->actions(); - - $first = array_shift($actions); - $this->assertInstanceOf(EchoResponse::class, $first); - - $second = array_shift($actions); - $this->assertEquals([ - 'file_references' => [ - [ - 'file' => 'barfoo', - 'references' => [ - [ - 'start' => 10, - 'end' => 20, - 'line' => '', - 'line_no' => 10, - 'col_no' => 12, - ] - ], - ] - ], - ], $second->parameters()); - } - - public function testReplaceMemberDemandReplacement(): void - { - $replacement = 'foobar'; - - $this->classMemberReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - __CLASS__, - 'testMemberReferences', - ClassMemberQuery::TYPE_METHOD, - $replacement - )->willReturn($this->exampleMemberRiskyResponse()); - - $action = $this->handle('references', [ - 'source' => 'testMemberReferences();', - 'offset' => 104, - 'path' => self::TEST_PATH, - 'filesystem' => 'git', - 'mode' => ReferencesHandler::MODE_REPLACE, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $textInput = $action->inputs()[0]; - $this->assertInstanceOf(TextInput::class, $textInput); - $this->assertEquals('testMemberReferences', $textInput->default()); - } - - public function testReplaceMember(): void - { - $replacement = 'foobar'; - $source = 'testMemberReferences();'; - - $this->classMemberReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - __CLASS__, - 'testMemberReferences', - ClassMemberQuery::TYPE_METHOD, - $replacement - )->willReturn($this->exampleMemberRiskyResponse()); - - $this->classMemberReferences->replaceInSource( - $source, - __CLASS__, - 'testMemberReferences', - ClassMemberQuery::TYPE_METHOD, - $replacement - )->willReturn('handle('references', [ - 'source' => $source, - 'path' => self::TEST_PATH, - 'offset' => 104, - 'filesystem' => 'git', - 'mode' => ReferencesHandler::MODE_REPLACE, - 'replacement' => $replacement, - ]); - - assert($action instanceof CollectionResponse); - $first = $action->actions()[0]; - $this->assertInstanceOf(EchoResponse::class, $first); - $second = $action->actions()[1]; - $this->assertInstanceOf(UpdateFileSourceResponse::class, $second); - assert($second instanceof UpdateFileSourceResponse); - $third = $action->actions()[2]; - $this->assertEquals('newSource()); - $this->assertInstanceOf(FileReferencesResponse::class, $third); - } - - public function testMemberReferencesWithRisky(): void - { - $this->classMemberReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - __CLASS__, - 'testMemberReferences', - ClassMemberQuery::TYPE_METHOD, - null - )->willReturn($this->exampleMemberRiskyResponse()); - - $action = $this->handle('references', [ - 'source' => $std = 'testMemberReferences();', - 'path' => self::TEST_PATH, - 'offset' => 104, - 'filesystem' => 'git', - ]); - - $this->assertInstanceOf(CollectionResponse::class, $action); - - $actions = $action->actions(); - - $first = array_shift($actions); - $this->assertInstanceOf(EchoResponse::class, $first); - $this->assertStringContainsString('risky', $first->message()); - } - - public function testReferencesAreSorted(): void - { - $this->classMemberReferences->findOrReplaceReferences( - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - __CLASS__, - 'testMemberReferences', - ClassMemberQuery::TYPE_METHOD, - null - )->willReturn([ - 'references' => [[ - 'file' => 'foobar', - 'references' => [ - [ - 'start' => 10, - 'line_no' => 8, - 'end' => 20, - 'line' => '', - 'col_no' => 12, - ], - ], - ], [ - 'file' => 'barfoo', - 'references' => [ - [ - 'start' => 13, - 'line_no' => 10, - 'end' => 20, - 'line' => '', - 'col_no' => 15, - ], [ - 'start' => 10, - 'line_no' => 10, - 'end' => 20, - 'line' => '', - 'col_no' => 12, - ], - ], - ]], - ]); - - $action = $this->handle('references', [ - 'source' => $std = 'testMemberReferences();', - 'offset' => 104, - 'path' => self::TEST_PATH, - 'filesystem' => 'git', - ]); - - $this->assertInstanceOf(CollectionResponse::class, $action); - - $actions = $action->actions(); - - $first = array_shift($actions); - $this->assertInstanceOf(EchoResponse::class, $first); - - $second = array_shift($actions); - $this->assertEquals([ - 'file_references' => [[ - 'file' => 'barfoo', - 'references' => [ - [ - 'start' => 10, - 'line_no' => 10, - 'end' => 20, - 'line' => '', - 'col_no' => 12, - ], [ - 'start' => 13, - 'line_no' => 10, - 'end' => 20, - 'line' => '', - 'col_no' => 15, - ], - ], - ], [ - 'file' => 'foobar', - 'references' => [ - [ - 'start' => 10, - 'line_no' => 8, - 'end' => 20, - 'line' => '', - 'col_no' => 12, - ], - ], - ]], - ], $second->parameters()); - } - - private function exampleMemberRiskyResponse(): array - { - return [ - 'references' => [ - [ - 'file' => 'barfoo', - 'references' => [ - [ - 'start' => 10, - 'line_no' => 10, - 'end' => 20, - 'col_no' => 12, - ], - ], - 'risky_references' => [ - [ - 'start' => 10, - 'line_no' => 10, - 'end' => 20, - 'col_no' => 12, - ], - ], - ] - ], - ]; - } - - private function exampleClassResponse(): array - { - return [ - 'references' => [ - [ - 'file' => 'barfoo', - 'references' => [ - [ - 'start' => 10, - 'line_no' => 10, - 'end' => 20, - 'col_no' => 12, - ], - ], - ] - ], - ]; - } -} diff --git a/tests/Unit/Extension/ClassToFile/Rpc/FileInfoHandlerTest.php b/tests/Unit/Extension/ClassToFile/Rpc/FileInfoHandlerTest.php deleted file mode 100644 index 520ef865eb..0000000000 --- a/tests/Unit/Extension/ClassToFile/Rpc/FileInfoHandlerTest.php +++ /dev/null @@ -1,42 +0,0 @@ -fileInfo = $this->prophesize(FileInfo::class); - } - - public function testReturnsAResponseWithAFileInfo(): void - { - $path = 'src/Controller/BlogController.php'; - $result = [ - 'class' => 'App\Controller\BlogController', - 'class_name' => 'BlogController', - 'class_namespace' => 'App\Controller', - ]; - - $this->fileInfo->infoForFile($path)->willReturn($result); - - $response = $this->handle('file_info', ['path' => $path]); - - $this->assertInstanceOf(ReturnResponse::class, $response); - $this->assertEquals($result, $response->parameters()['value']); - } - - protected function createHandler(): Handler - { - return new FileInfoHandler($this->fileInfo->reveal()); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandlerTest.php deleted file mode 100644 index f6663f1449..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/ChangeVisiblityHandlerTest.php +++ /dev/null @@ -1,43 +0,0 @@ -changeVisibility = $this->prophesize(ChangeVisiblity::class); - } - - public function testChangeVisiblity(): void - { - $expectedSource = SourceCode::fromStringAndPath(self::EXAMPLE_SOURCE, self::EXAMPLE_PATH); - $this->changeVisibility->changeVisiblity($expectedSource, self::EXAMPLE_OFFSET)->willReturn($expectedSource); - - $response = $this->handle('change_visibility', [ - 'source' => self::EXAMPLE_SOURCE, - 'path' => self::EXAMPLE_PATH, - 'offset' => self::EXAMPLE_OFFSET, - ]); - $this->assertInstanceof(UpdateFileSourceResponse::class, $response); - } - - protected function createHandler(): Handler - { - return new ChangeVisiblityHandler($this->changeVisibility->reveal()); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractConstantHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractConstantHandlerTest.php deleted file mode 100644 index 7fe28e0a99..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractConstantHandlerTest.php +++ /dev/null @@ -1,75 +0,0 @@ -extractConstant = $this->prophesize(ExtractConstant::class); - } - - public function createHandler(): Handler - { - return new ExtractConstantHandler($this->extractConstant->reveal()); - } - - public function testDemandConstantName(): void - { - $action = $this->handle('extract_constant', [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'offset' => self::OFFSET, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $this->assertCount(1, $inputs); - $firstInput = reset($inputs); - $this->assertEquals(ExtractConstantHandler::NAME, $action->callbackAction()->name()); - - $this->assertInstanceOf(TextInput::class, $firstInput); - $this->assertEquals('constant_name', $firstInput->name()); - } - - public function testExtractConstant(): void - { - $this->extractConstant->extractConstant( - self::SOURCE, - self::OFFSET, - self::CONSTANT_NAME - )->willReturn(new TextDocumentEdits( - TextDocumentUri::fromString('file://'. self::PATH), - TextEdits::one(TextEdit::create(6, 10, 'newMethod()')) - )); - - $action = $this->handle('extract_constant', [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'offset' => self::OFFSET, - 'constant_name' => self::CONSTANT_NAME, - ]); - - $this->assertInstanceof(UpdateFileSourceResponse::class, $action); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandlerTest.php deleted file mode 100644 index 43b29a067d..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractExpressionHandlerTest.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ - private ObjectProphecy $extractExpression; - - public function setUp(): void - { - $this->extractExpression = $this->prophesize(ExtractExpression::class); - } - - public function createHandler(): Handler - { - return new ExtractExpressionHandler($this->extractExpression->reveal()); - } - - public function testDemandMethodName(): void - { - $action = $this->handle('extract_expression', [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'offset_start' => self::OFFSET_START, - 'offset_end' => self::OFFSET_END, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - assert($action instanceof InputCallbackResponse); - $inputs = $action->inputs(); - $this->assertCount(1, $inputs); - $firstInput = reset($inputs); - $this->assertEquals(ExtractExpressionHandler::NAME, $action->callbackAction()->name()); - - $this->assertInstanceOf(TextInput::class, $firstInput); - $this->assertEquals('variable_name', $firstInput->name()); - } - - public function testExtractExpression(): void - { - $this->extractExpression->extractExpression( - self::SOURCE, - self::OFFSET_START, - self::OFFSET_END, - self::VARIABLE_NAME - ) - ->shouldBeCalled() - ->willReturn(TextEdits::one(TextEdit::create(6, 5, '$newVar = "foo"'))); - - $action = $this->handle('extract_expression', [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'offset_start' => self::OFFSET_START, - 'offset_end' => self::OFFSET_END, - 'variable_name' => self::VARIABLE_NAME, - ]); - - $this->assertInstanceOf(UpdateFileSourceResponse::class, $action); - assert($action instanceof UpdateFileSourceResponse); - self::assertEquals('newSource()); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractMethodHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractMethodHandlerTest.php deleted file mode 100644 index 451a96055d..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/ExtractMethodHandlerTest.php +++ /dev/null @@ -1,85 +0,0 @@ - - */ - private ObjectProphecy $extractMethod; - - public function setUp(): void - { - $this->extractMethod = $this->prophesize(ExtractMethod::class); - } - - public function createHandler(): Handler - { - return new ExtractMethodHandler($this->extractMethod->reveal()); - } - - public function testDemandMethodName(): void - { - $action = $this->handle('extract_method', [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'offset_start' => self::OFFSET_START, - 'offset_end' => self::OFFSET_END, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $this->assertCount(1, $inputs); - $firstInput = reset($inputs); - $this->assertEquals(ExtractMethodHandler::NAME, $action->callbackAction()->name()); - - $this->assertInstanceOf(TextInput::class, $firstInput); - $this->assertEquals('method_name', $firstInput->name()); - } - - public function testExtractMethod(): void - { - $this->extractMethod->extractMethod( - self::SOURCE, - self::OFFSET_START, - self::OFFSET_END, - self::METHOD_NAME - ) - ->willReturn(new TextDocumentEdits( - TextDocumentUri::fromString('file://'. self::PATH), - TextEdits::one(TextEdit::create(6, 10, 'newMethod()')) - )); - - $action = $this->handle('extract_method', [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'offset_start' => self::OFFSET_START, - 'offset_end' => self::OFFSET_END, - 'method_name' => self::METHOD_NAME, - ]); - - $this->assertInstanceof(UpdateFileSourceResponse::class, $action); - assert($action instanceof UpdateFileSourceResponse); - self::assertEquals('newSource()); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/GenerateMethodHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/GenerateMethodHandlerTest.php deleted file mode 100644 index a5a96afdb4..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/GenerateMethodHandlerTest.php +++ /dev/null @@ -1,92 +0,0 @@ -generateMethod = $this->prophesize(GenerateMember::class); - } - - public function testProvidesOriginalSourceFromDiskIfPathIsNotTheGivenPath(): void - { - $handler = $this->createHandler(); - $source = SourceCode::fromStringAndPath(self::EXAMPLE_SOURCE, self::EXAMPLE_PATH); - $thisFileContents = file_get_contents(__FILE__); - - // @phpstan-ignore-next-line - $this->generateMethod->generateMember( - $source, - self::EXAMPLE_OFFSET - )->willReturn(new TextDocumentEdits( - TextDocumentUri::fromString(__FILE__), - TextEdits::one(TextEdit::create(strlen($thisFileContents) - 1, 1, substr($thisFileContents, -1) .'1')) - )); - - $response = $handler->handle([ - GenerateMethodHandler::PARAM_PATH => self::EXAMPLE_PATH, - GenerateMethodHandler::PARAM_SOURCE => self::EXAMPLE_SOURCE, - GenerateMethodHandler::PARAM_OFFSET => self::EXAMPLE_OFFSET, - ]); - - $this->assertInstanceOf(UpdateFileSourceResponse::class, $response); - assert($response instanceof UpdateFileSourceResponse); - $this->assertEquals(Path::canonicalize(__FILE__), $response->path()); - $this->assertEquals($thisFileContents, $response->oldSource()); - $this->assertEquals($thisFileContents.'1', $response->newSource()); - } - - public function testProvidesGivenSourceIfTransformedPathSameAsGivenPath(): void - { - $handler = $this->createHandler(); - $source = SourceCode::fromStringAndPath(self::EXAMPLE_SOURCE, self::EXAMPLE_PATH); - - // @phpstan-ignore-next-line - $this->generateMethod->generateMember( - $source, - self::EXAMPLE_OFFSET - )->willReturn(new TextDocumentEdits( - TextDocumentUri::fromString('file://'. self::EXAMPLE_PATH), - TextEdits::one(TextEdit::create(19, 0, ' 1')) - )); - - $response = $handler->handle([ - GenerateMethodHandler::PARAM_PATH => self::EXAMPLE_PATH, - GenerateMethodHandler::PARAM_SOURCE => self::EXAMPLE_SOURCE, - GenerateMethodHandler::PARAM_OFFSET => self::EXAMPLE_OFFSET, - ]); - - $this->assertInstanceOf(UpdateFileSourceResponse::class, $response); - assert($response instanceof UpdateFileSourceResponse); - $this->assertEquals(self::EXAMPLE_PATH, $response->path()); - $this->assertEquals(self::EXAMPLE_SOURCE, $response->oldSource()); - $this->assertEquals(self::EXAMPLE_TRANSFORMED_SOURCE, $response->newSource()); - } - - protected function createHandler(): Handler - { - // @phpstan-ignore-next-line - return new GenerateMethodHandler($this->generateMethod->reveal()); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/ImportClassHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/ImportClassHandlerTest.php deleted file mode 100644 index e2305339ef..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/ImportClassHandlerTest.php +++ /dev/null @@ -1,180 +0,0 @@ -importName = $this->prophesize(ImportName::class); - $this->classSearch = $this->prophesize(ClassSearch::class); - } - - public function testReturnsSuggestionsIfMultipleTargetsFound(): void - { - $this->classSearch->classSearch('composer', self::TEST_NAME)->willReturn([ - [ - 'class' => 'Foobar', - ], - [ - 'class' => 'Barfoo', - ], - ]); - - /** @var InputCallbackResponse $response */ - $response = $this->handle('import_class', [ - ImportClassHandler::PARAM_OFFSET => self::TEST_OFFSET, - ImportClassHandler::PARAM_PATH => self::TEST_PATH, - ImportClassHandler::PARAM_SOURCE => self::TEST_SOURCE - ]); - $this->assertInstanceOf(InputCallbackResponse::class, $response); - $inputs = $response->inputs(); - $this->assertCount(1, $inputs); - /** @var ListInput $input */ - $input = reset($inputs); - $this->assertCount(2, $input->choices()); - } - - public function testShowsMessageIfNoClassesFound(): void - { - $this->classSearch->classSearch('composer', self::TEST_NAME)->willReturn([]); - - /** @var EchoResponse $response */ - $response = $this->handle('import_class', [ - ImportClassHandler::PARAM_OFFSET => self::TEST_OFFSET, - ImportClassHandler::PARAM_PATH => self::TEST_PATH, - ImportClassHandler::PARAM_SOURCE => self::TEST_SOURCE - ]); - $this->assertInstanceOf(EchoResponse::class, $response); - } - - public function testImportsClassIfOnlyOneSuggestion(): void - { - $this->classSearch->classSearch('composer', self::TEST_NAME)->willReturn([ - [ - 'class' => self::TEST_NAME - ], - ]); - $transformed = TextEdits::one(TextEdit::create(0, 0, 'hello')); - $this->importName->importName( - SourceCode::fromStringAndPath(self::TEST_SOURCE, self::TEST_PATH), - ByteOffset::fromInt(self::TEST_OFFSET), - NameImport::forClass(self::TEST_NAME) - )->willReturn($transformed); - - /** @var EchoResponse $response */ - $response = $this->handle('import_class', [ - ImportClassHandler::PARAM_OFFSET => self::TEST_OFFSET, - ImportClassHandler::PARAM_PATH => self::TEST_PATH, - ImportClassHandler::PARAM_SOURCE => self::TEST_SOURCE - ]); - - $this->assertInstanceOf(CollectionResponse::class, $response); - } - - public function testAsksForAliasIfClassAlreadyUsed(): void - { - $this->importName->importName( - SourceCode::fromStringAndPath(self::TEST_SOURCE, self::TEST_PATH), - ByteOffset::fromInt(self::TEST_OFFSET), - NameImport::forClass(self::TEST_NAME) - )->willThrow(new AliasAlreadyUsedException(NameImport::forClass(self::TEST_NAME, self::TEST_ALIAS))); - - /** @var EchoResponse $response */ - $response = $this->handle('import_class', [ - ImportClassHandler::PARAM_QUALIFIED_NAME => self::TEST_NAME, - ImportClassHandler::PARAM_OFFSET => self::TEST_OFFSET, - ImportClassHandler::PARAM_PATH => self::TEST_PATH, - ImportClassHandler::PARAM_SOURCE => self::TEST_SOURCE - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $response); - $inputs = $response->inputs(); - $this->assertCount(1, $inputs); - /** @var TextInput $input */ - $input = reset($inputs); - $this->assertInstanceOf(TextInput::class, $input); - } - - public function testUsesGivenAlias(): void - { - $transformed = TextEdits::one(TextEdit::create(0, 0, 'hello')); - $this->importName->importName( - SourceCode::fromStringAndPath(self::TEST_SOURCE, self::TEST_PATH), - ByteOffset::fromInt(self::TEST_OFFSET), - NameImport::forClass(self::TEST_NAME, self::TEST_ALIAS) - )->willReturn($transformed); - - /** @var EchoResponse $response */ - $response = $this->handle('import_class', [ - ImportClassHandler::PARAM_ALIAS => self::TEST_ALIAS, - ImportClassHandler::PARAM_QUALIFIED_NAME => self::TEST_NAME, - ImportClassHandler::PARAM_OFFSET => self::TEST_OFFSET, - ImportClassHandler::PARAM_PATH => self::TEST_PATH, - ImportClassHandler::PARAM_SOURCE => self::TEST_SOURCE - ]); - - $this->assertInstanceOf(CollectionResponse::class, $response); - } - - public function testShowsMessageIfSelectedClassIsAlreadyImported(): void - { - $this->importName->importName( - SourceCode::fromStringAndPath(self::TEST_SOURCE, self::TEST_PATH), - ByteOffset::fromInt(self::TEST_OFFSET), - NameImport::forClass(self::TEST_NAME) - )->willThrow(new NameAlreadyImportedException( - NameImport::forClass(self::TEST_NAME), - self::TEST_NAME, - 'ExistingFqn' - )); - - /** @var EchoResponse $response */ - $response = $this->handle('import_class', [ - ImportClassHandler::PARAM_QUALIFIED_NAME => self::TEST_NAME, - ImportClassHandler::PARAM_OFFSET => self::TEST_OFFSET, - ImportClassHandler::PARAM_PATH => self::TEST_PATH, - ImportClassHandler::PARAM_SOURCE => self::TEST_SOURCE - ]); - - $this->assertInstanceOf(EchoResponse::class, $response); - } - - protected function createHandler(): Handler - { - return new ImportClassHandler( - $this->importName->reveal(), - $this->classSearch->reveal(), - 'composer' - ); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandlerTest.php deleted file mode 100644 index 52852e0098..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/ImportMissingClassesHandlerTest.php +++ /dev/null @@ -1,64 +0,0 @@ -requestHandler = $this->container()->get(RpcExtension::SERVICE_REQUEST_HANDLER); - } - - public function testZeroUnresolvedClasses(): void - { - $reflector = ReflectorBuilder::create()->addDiagnosticProvider(new InMemoryDiagnosticProvider([]))->build(); - $tester = new HandlerTester(new ImportMissingClassesHandler( - $this->requestHandler, - $reflector, - )); - $response = $tester->handle(ImportMissingClassesHandler::NAME, [ - ImportMissingClassesHandler::PARAM_PATH => self::EXAMPLE_PATH, - ImportMissingClassesHandler::PARAM_SOURCE => self::EXAMPLE_SOURCE, - ]); - - $this->assertInstanceOf(EchoResponse::class, $response); - } - - public function testImportsUnresolvedClasses(): void - { - $reflector = ReflectorBuilder::create()->addDiagnosticProvider( - new InMemoryDiagnosticProvider([ - UnresolvableNameDiagnostic::forClass(ByteOffsetRange::fromInts(1, 1), FullyQualifiedName::fromString('foo')) - ]) - )->build(); - $tester = new HandlerTester(new ImportMissingClassesHandler( - $this->requestHandler, - $reflector, - )); - $response = $tester->handle(ImportMissingClassesHandler::NAME, [ - ImportMissingClassesHandler::PARAM_PATH => self::EXAMPLE_PATH, - ImportMissingClassesHandler::PARAM_SOURCE => self::EXAMPLE_SOURCE, - ]); - - $this->assertInstanceOf(CollectionResponse::class, $response); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/OverrideMethodHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/OverrideMethodHandlerTest.php deleted file mode 100644 index 3000fd2b50..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/OverrideMethodHandlerTest.php +++ /dev/null @@ -1,119 +0,0 @@ -reflector = ReflectorBuilder::create()->addSource('build(); - $this->overrideMethod = $this->prophesize(OverrideMethod::class); - } - - public function createHandler(): Handler - { - return new OverrideMethodHandler( - $this->reflector, - $this->overrideMethod->reveal() - ); - } - - public function testSuggestsPossibleMethods(): void - { - $action = $this->handle('override_method', [ - 'class_name' => 'ChildClass', - 'path' => __FILE__, - 'source' => <<<'EOT' - inputs(); - $input = reset($input); - $this->assertInstanceOf(ListInput::class, $input); - $choices = $input->choices(); - $this->assertCount(2, $choices); - } - - public function testOverrideAMethodGivenAsAString(): void - { - $source = <<<'EOT' - overrideMethod->overrideMethod( - $source, - 'ChildClass', - 'foobar' - )->willReturn(TextEdits::fromTextEdits([PhpactorTextEdit::create(0, strlen($source), 'hello')])); - - $action = $this->handle('override_method', [ - 'class_name' => 'ChildClass', - 'method_name' => 'foobar', - 'path' => __FILE__, - 'source' => $source - ]); - - $this->assertInstanceOf(UpdateFileSourceResponse::class, $action); - $this->assertEquals('hello', $action->newSource()); - } - - public function testOverrideMethodsGivenAsArray(): void - { - $source = <<<'EOT' - overrideMethod->overrideMethod($source, 'ChildClass', 'foobar') - ->willReturn(TextEdits::fromTextEdits([PhpactorTextEdit::create(0, strlen($source), $foobarTransformedCode)])) - ->shouldBeCalledTimes(1); - $this->overrideMethod->overrideMethod($foobarTransformedCode, 'ChildClass', 'barfoo') - ->willReturn(TextEdits::fromTextEdits([PhpactorTextEdit::create(0, strlen($foobarTransformedCode), $barfooTransformedCode)])) - ->shouldBeCalledTimes(1); - - $action = $this->handle('override_method', [ - 'class_name' => 'ChildClass', - 'method_name' => ['foobar', 'barfoo'], - 'path' => __FILE__, - 'source' => $source - ]); - - /** @var UpdateFileSourceResponse $action */ - $this->assertInstanceOf(UpdateFileSourceResponse::class, $action); - $this->assertEquals((string) $barfooTransformedCode, $action->newSource()); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandlerTest.php deleted file mode 100644 index 5682b60d9d..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/PropertyAccessGeneratorHandlerTest.php +++ /dev/null @@ -1,172 +0,0 @@ - self::FOO_NAME, self::BAR_NAME => self::BAR_NAME]; - const GENERATE_ACCESSOR_ACTION = 'generate_accessor'; - const CURSOR_OFFSET = 57; - - private ObjectProphecy $generateAccessor; - - private Reflector $reflector; - - public function setUp(): void - { - $this->reflector = ReflectorBuilder::create()->addSource(self::SOURCE)->build(); - $this->generateAccessor = $this->prophesize(PropertyAccessGenerator::class); - } - - public function createHandler(): Handler - { - return new PropertyAccessGeneratorHandler( - 'generate_accessor', - $this->reflector, - $this->generateAccessor->reveal() - ); - } - - public function testSuggestsPossibleProperties(): void - { - $action = $this->handle(self::GENERATE_ACCESSOR_ACTION, [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'offset' => self::CURSOR_OFFSET, - ]); - - /** @var InputCallbackResponse $action */ - $this->assertInstanceOf(InputCallbackResponse::class, $action); - - $inputs = $action->inputs(); - $input = reset($inputs); - - /** @var ListInput $input */ - $this->assertInstanceOf(ListInput::class, $input); - - $this->assertEquals(self::PROPERTIES_CHOICES, $input->choices()); - } - - public function testGeneratesAccessorIfSpecificPropertyIsSelected(): void - { - [ $source, $offset ] = ExtractOffset::fromSource( - <<<'EOT' - foo; - } - EOT - ); - - $edits = TextEdits::fromTextEdits([TextEdit::create(ByteOffset::fromInt(0), 0, 'foobar')]); - $this->generateAccessor->generate($source, ['foo'], $offset) - ->willReturn($edits) - ->shouldBeCalledTimes(1); - - $action = $this->handle(self::GENERATE_ACCESSOR_ACTION, [ - 'source' => $source, - 'path' => self::PATH, - 'offset' => $offset, - ]); - - /** @var InputCallbackResponse $action */ - $this->assertInstanceOf(UpdateFileSourceResponse::class, $action); - } - - public function testGenerateAccessorFromAPropertyName(): void - { - $oldSource = SourceCode::fromStringAndPath(self::SOURCE, self::PATH); - $newSource = SourceCode::fromStringAndPath('asd', self::PATH); - - $edits = TextEdits::fromTextEdits([ - TextEdit::create( - 0, - mb_strlen(self::SOURCE), - 'asd' - ) - ]); - - $this->generateAccessor->generate($oldSource, [self::FOO_NAME], self::CURSOR_OFFSET) - ->willReturn($edits) - ->shouldBeCalledTimes(1); - - $action = $this->handle(self::GENERATE_ACCESSOR_ACTION, [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'names' => self::FOO_NAME, - 'offset' => self::CURSOR_OFFSET, - ]); - - /** @var UpdateFileSourceResponse $action */ - $this->assertInstanceof(UpdateFileSourceResponse::class, $action); - $this->assertSame((string) $oldSource, $action->oldSource()); - $this->assertSame((string) $newSource, $action->newSource()); - $this->assertSame(self::PATH, $action->path()); - } - - public function testGenerateAccessorsFromMultiplePropertyName(): void - { - $oldSource = SourceCode::fromStringAndPath(self::SOURCE, self::PATH); - - $temporarySource = SourceCode::fromStringAndPath('asd', self::PATH); - - $edits = TextEdits::fromTextEdits([ - TextEdit::create( - 0, - mb_strlen(self::SOURCE), - 'asd' - ) - ]); - $this->generateAccessor->generate($oldSource, [ - self::FOO_NAME, - self::BAR_NAME - ], self::CURSOR_OFFSET) - ->willReturn($edits) - ->shouldBeCalledTimes(1); - - $newSource = SourceCode::fromStringAndPath((string) $temporarySource, self::PATH); - - $action = $this->handle(self::GENERATE_ACCESSOR_ACTION, [ - 'source' => self::SOURCE, - 'path' => self::PATH, - 'names' => [self::FOO_NAME, self::BAR_NAME], - 'offset' => self::CURSOR_OFFSET, - ]); - - /** @var UpdateFileSourceResponse $action */ - $this->assertInstanceof(UpdateFileSourceResponse::class, $action); - $this->assertSame((string) $oldSource, $action->oldSource()); - $this->assertSame((string) $temporarySource, $action->newSource()); - $this->assertSame(self::PATH, $action->path()); - } -} diff --git a/tests/Unit/Extension/CodeTransformExtra/Rpc/RenameVariableHandlerTest.php b/tests/Unit/Extension/CodeTransformExtra/Rpc/RenameVariableHandlerTest.php deleted file mode 100644 index 830d108fb4..0000000000 --- a/tests/Unit/Extension/CodeTransformExtra/Rpc/RenameVariableHandlerTest.php +++ /dev/null @@ -1,92 +0,0 @@ -renameVariable = $this->prophesize(RenameVariable::class); - } - - public function createHandler(): Handler - { - return new RenameVariableHandler($this->renameVariable->reveal()); - } - - public function testDemandVariableName(): void - { - $action = $this->handle(RenameVariableHandler::NAME, [ - RenameVariableHandler::PARAM_SOURCE => self::SOURCE, - RenameVariableHandler::PARAM_PATH => self::PATH, - RenameVariableHandler::PARAM_OFFSET => self::OFFSET, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $this->assertCount(2, $inputs); - $this->assertEquals(RenameVariableHandler::NAME, $action->callbackAction()->name()); - - array_shift($inputs); - $firstInput = array_shift($inputs); - $this->assertInstanceOf(TextInput::class, $firstInput); - $this->assertEquals('name', $firstInput->name()); - } - - public function testDemandScope(): void - { - $action = $this->handle(RenameVariableHandler::NAME, [ - RenameVariableHandler::PARAM_SOURCE => self::SOURCE, - RenameVariableHandler::PARAM_PATH => self::PATH, - RenameVariableHandler::PARAM_OFFSET => self::OFFSET, - RenameVariableHandler::PARAM_NAME => self::VARIABLE_NAME, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $inputs = $action->inputs(); - $this->assertCount(1, $inputs); - $firstInput = reset($inputs); - $this->assertEquals(RenameVariableHandler::NAME, $action->callbackAction()->name()); - - $this->assertInstanceOf(ChoiceInput::class, $firstInput); - $this->assertEquals('scope', $firstInput->name()); - } - - public function testRenameVariable(): void - { - $this->renameVariable->renameVariable( - self::SOURCE, - self::OFFSET, - self::VARIABLE_NAME, - RenameVariable::SCOPE_FILE - )->willReturn(SourceCode::fromStringAndPath('asd', '/path')); - - $action = $this->handle(RenameVariableHandler::NAME, [ - RenameVariableHandler::PARAM_SOURCE => self::SOURCE, - RenameVariableHandler::PARAM_PATH => self::PATH, - RenameVariableHandler::PARAM_OFFSET => self::OFFSET, - RenameVariableHandler::PARAM_NAME => self::VARIABLE_NAME, - RenameVariableHandler::PARAM_SCOPE => RenameVariable::SCOPE_FILE - ]); - - $this->assertInstanceof(UpdateFileSourceResponse::class, $action); - } -} diff --git a/tests/Unit/Extension/Completion/Rpc/HoverHandlerTest.php b/tests/Unit/Extension/Completion/Rpc/HoverHandlerTest.php deleted file mode 100644 index a508d0bb33..0000000000 --- a/tests/Unit/Extension/Completion/Rpc/HoverHandlerTest.php +++ /dev/null @@ -1,148 +0,0 @@ -reflector = ReflectorBuilder::create()->enableContextualSourceLocation()->build(); - $this->formatter = new ObjectFormatter([]); - } - - #[DataProvider('provideHover')] - public function testHover(string $source, string $expectedMessage): void - { - [ $source, $offset ] = ExtractOffset::fromSource($source); - - $response = $this->handle(HoverHandler::NAME, [ - 'source' => $source, - 'offset' => $offset, - ]); - - $this->assertEquals($expectedMessage, $response->message()); - } - /** - * @return Generator - */ - public static function provideHover(): Generator - { - yield 'method' => [ - 'obar() { } }', - 'method foobar' - ]; - - yield 'property' => [ - 'obar; }', - 'property foobar', - ]; - - yield 'constant' => [ - 'obar = 123; }', - 'constant foobar', - ]; - - yield 'class' => [ - 'lass Foobar {}', - 'class Foobar', - ]; - - yield 'variable' => [ - 'oo = "bar"', - 'variable foo', - ]; - - yield 'unknown' => [ - ' $foo = "bar"', - ' InlineHtml', - ]; - } - - #[DataProvider('provideHoverWithFormatter')] - public function testHoverWithFormatter(string $source, string $expectedMessage): void - { - $this->formatter = new ObjectFormatter([ - new MethodFormatter(), - new ClassFormatter(), - new VariableFormatter(), - ]); - - [ $source, $offset ] = ExtractOffset::fromSource($source); - - $response = $this->handle(HoverHandler::NAME, [ - 'source' => $source, - 'offset' => $offset, - ]); - - $this->assertEquals($expectedMessage, $response->message()); - } - /** - * @return Generator - */ - public static function provideHoverWithFormatter(): Generator - { - yield 'method' => [ - 'obar() { } }', - 'pub foobar()', - ]; - - yield 'method with documentation' => [ - <<<'EOT' - obar() { } - } - EOT - , - <<<'EOT' - pub foobar() - EOT - ]; - - yield 'class with documentation' => [ - <<<'EOT' - oobar {} - EOT - , - <<<'EOT' - Foobar - EOT - ]; - - yield 'unknown' => [ - ' $foo = "bar"', - ' InlineHtml', - ]; - } - - protected function createHandler(): Handler - { - return new HoverHandler($this->reflector, $this->formatter); - } -} diff --git a/tests/Unit/Extension/ContextMenu/Handler/ContextMenuHandlerTest.php b/tests/Unit/Extension/ContextMenu/Handler/ContextMenuHandlerTest.php deleted file mode 100644 index 28c978154f..0000000000 --- a/tests/Unit/Extension/ContextMenu/Handler/ContextMenuHandlerTest.php +++ /dev/null @@ -1,229 +0,0 @@ -reflector = ReflectorBuilder::create()->addSource(TextDocumentBuilder::fromUri(__FILE__)->build())->build(); - $this->offsetFinder = $this->prophesize(InterestingOffsetFinder::class); - $this->classFileNormalizer = $this->prophesize(ClassFileNormalizer::class); - $this->container = $this->prophesize(Container::class); - $this->requestHandler = $this->prophesize(RequestHandler::class); - } - - public function createHandler(): Handler - { - return new ContextMenuHandler( - $this->reflector, - $this->offsetFinder->reveal(), - $this->classFileNormalizer->reveal(), - $this->menu, - $this->container->reveal() - ); - } - - public function testNoActionsAvailable(): void - { - $this->menu = ContextMenu::fromArray([ - 'actions' => [ - Symbol::VARIABLE => [ - 'action' => self::VARIABLE_ACTION, - 'parameters' => [ - 'one' => 1, - ], - ] - ], - 'contexts' => [ - Symbol::VARIABLE => [ - ] - ] - ]); - $source = TextDocumentBuilder::create('uri('/hello.php')->build(); - $offset = ByteOffset::fromInt(4); - - $this->offsetFinder->find($source, $offset) - ->willReturn($offset); - - $action = $this->handle(ContextMenuHandler::NAME, [ - 'source' => (string) $source, - 'offset' => $offset->toInt(), - 'current_path' => $source->uri()?->path(), - ]); - - $this->assertInstanceOf(EchoResponse::class, $action); - $this->assertStringContainsString('No context actions', $action->message()); - } - - public function testReturnMenu(): void - { - $this->menu = ContextMenu::fromArray([ - 'actions' => [ - Symbol::VARIABLE => [ - 'action' => self::VARIABLE_ACTION, - 'parameters' => [ - 'one' => 1, - ], - ] - ], - 'contexts' => [ - Symbol::VARIABLE => [ - Symbol::VARIABLE - ] - ] - ]); - - $source = TextDocumentBuilder::create( - 'uri( - '/hello.php', - )->build(); - $offset = ByteOffset::fromInt(self::ORIGINAL_OFFSET); - - $this->offsetFinder->find($source, $offset) - ->willReturn($offset); - - $action = $this->handle(ContextMenuHandler::NAME, [ - 'source' => (string) $source, - 'offset' => $offset->toInt(), - 'current_path' => $source->uri()->path(), - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $this->assertInstanceOf(Request::class, $action->callbackAction()); - $this->assertEquals(ContextMenuHandler::NAME, $action->callbackAction()->name()); - } - - public function testReturnMenuWithOriginalOffset(): void - { - $this->menu = ContextMenu::fromArray([ - 'actions' => [ - Symbol::VARIABLE => [ - 'action' => self::VARIABLE_ACTION, - 'parameters' => [ - 'one' => 1, - ], - ] - ], - 'contexts' => [ - Symbol::VARIABLE => [ - Symbol::VARIABLE - ] - ] - ]); - - $source = TextDocumentBuilder::create( - 'uri( - '/hello.php' - )->build(); - $offset = ByteOffset::fromInt(self::ORIGINAL_OFFSET); - - $this->offsetFinder->find($source, $offset) - ->willReturn(ByteOffset::fromInt(self::FOUND_OFFSET)); - - $action = $this->handle(ContextMenuHandler::NAME, [ - 'source' => (string) $source, - 'offset' => self::ORIGINAL_OFFSET, - 'current_path' => $source->uri()?->path(), - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $action); - $this->assertEquals(self::ORIGINAL_OFFSET, $action->callbackAction()->parameters()['offset']); - } - - public function testReplaceTokens(): void - { - $this->container->get(ContextMenuExtension::SERVICE_REQUEST_HANDLER)->willReturn( - $this->requestHandler->reveal() - ); - - $this->classFileNormalizer->classToFile('string')->willReturn(__FILE__); - - $source = TextDocumentBuilder::create(self::SOURCE)->uri('/hello.php')->build(); - $offset = ByteOffset::fromInt(self::ORIGINAL_OFFSET); - - $this->offsetFinder->find($source, $offset) - ->willReturn($offset); - - $this->requestHandler->handle( - Request::fromNameAndParameters( - self::VARIABLE_ACTION, - [ - 'some_source' => (string) $source, - 'some_offset' => $offset->toInt(), - 'some_path' => __FILE__ - ] - ) - )->willReturn( - EchoResponse::fromMessage('Hello') - ); - - $this->menu = ContextMenu::fromArray([ - 'actions' => [ - self::VARIABLE_ACTION => [ - 'action' => self::VARIABLE_ACTION, - 'parameters' => [ - 'some_source' => '%source%', - 'some_offset' => '%offset%', - 'some_path' => '%path%', - ], - ] - ], - 'contexts' => [ - Symbol::VARIABLE => [ - self::VARIABLE_ACTION - ] - ] - ]); - - $action = $this->handle(ContextMenuHandler::NAME, [ - 'action' => self::VARIABLE_ACTION, - 'source' => (string) $source, - 'offset' => $offset->toInt(), - 'current_path' => $source->uri()?->path(), - ]); - - $parameters = $action->parameters(); - $this->assertEquals([ - 'message' => 'Hello', - ], $parameters); - } -} diff --git a/tests/Unit/Extension/ContextMenu/Model/ContextMenuTest.php b/tests/Unit/Extension/ContextMenu/Model/ContextMenuTest.php deleted file mode 100644 index 0952a25156..0000000000 --- a/tests/Unit/Extension/ContextMenu/Model/ContextMenuTest.php +++ /dev/null @@ -1,75 +0,0 @@ - [ - 'do_something' => [ - 'action' => 'blah', - 'key' => 'a', - 'parameters' => [ - 'path' => '%path%', - ], - ], - ], - 'contexts' => [ - 'class' => [ - 'do_something', - ], - ], - ]); - self::assertNotNull($menu); - } - - public function testExceptionIfKeyIsRepeatedInContext(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Key "b" in context "foo" mapped by action "action2" is already used by action "action1"'); - ContextMenu::fromArray([ - 'actions' => [ - 'action1' => [ - 'action' => 'blah', - 'key' => 'b', - ], - 'action2' => [ - 'action' => 'blah', - 'key' => 'b', - ], - ], - 'contexts' => [ - 'foo' => [ - 'action1', - 'action2', - ], - ], - ]); - } - - public function testActionDoesNotExist(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Action "a" used in context "foo" does not exist'); - ContextMenu::fromArray([ - 'actions' => [ - 'b' => [ - 'action' => 'blah', - 'key' => 'b', - ], - ], - 'contexts' => [ - 'foo' => [ - 'a', - 'b', - ], - ], - ]); - } -} diff --git a/tests/Unit/Extension/Core/Application/StatusTest.php b/tests/Unit/Extension/Core/Application/StatusTest.php deleted file mode 100644 index 09afd4b25b..0000000000 --- a/tests/Unit/Extension/Core/Application/StatusTest.php +++ /dev/null @@ -1,64 +0,0 @@ - */ - private ObjectProphecy $registry; - - /** @var ObjectProphecy */ - private ObjectProphecy $resolver; - - private PathCandidates $paths; - - private Status $status; - - public function setUp(): void - { - $this->registry = $this->prophesize(FilesystemRegistry::class); - $this->resolver = $this->prophesize(PhpVersionResolver::class); - $this->paths = new PathCandidates([]); - $this->status = new Status( - $this->registry->reveal(), - $this->paths, - '/path/to/here', - $this->resolver->reveal(), - new Trust([], null), - ); - } - - public function testStatusNoComposerOrGit(): void - { - $this->registry->names()->willReturn(['simple']); - $diagnostics = $this->status->check(); - - // should be git and composer error +/- xdebug warning - $this->assertGreaterThanOrEqual(2, $diagnostics['bad']); - } - - public function testStatusComposerOrGit(): void - { - $this->registry->names()->willReturn([ - SourceCodeFilesystemExtension::FILESYSTEM_SIMPLE, - SourceCodeFilesystemExtension::FILESYSTEM_GIT, - SourceCodeFilesystemExtension::FILESYSTEM_COMPOSER, - ]); - $diagnostics = $this->status->check(); - - // should be git and composer error +/- xdebug warning - $this->assertGreaterThanOrEqual(2, $diagnostics['good']); - } -} diff --git a/tests/Unit/Extension/Core/Console/Dumper/DumperRegistryTest.php b/tests/Unit/Extension/Core/Console/Dumper/DumperRegistryTest.php deleted file mode 100644 index 11cd85433d..0000000000 --- a/tests/Unit/Extension/Core/Console/Dumper/DumperRegistryTest.php +++ /dev/null @@ -1,52 +0,0 @@ -expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unknown dumper "foobar", known dumpers: "dumper1"'); - $registry = $this->create([ - 'dumper1' => $this->prophesize(Dumper::class)->reveal(), - ]); - - $registry->get('foobar'); - } - - #[TestDox('It returns the requested dumper.')] - public function testGetDumper(): void - { - $registry = $this->create([ - 'foobar' => $dumper = $this->prophesize(Dumper::class)->reveal(), - ]); - - $this->assertSame($dumper, $registry->get('foobar')); - } - - #[TestDox('It should use default if no argument given.')] - public function testDefault(): void - { - $registry = $this->create([ - 'foobar' => $dumper = $this->prophesize(Dumper::class)->reveal(), - ], 'foobar'); - - $this->assertSame($dumper, $registry->get()); - } - - private function create(array $dumpers, $default = 'default') - { - return new DumperRegistry($dumpers, $default); - } -} diff --git a/tests/Unit/Extension/Core/Console/Dumper/DumperTestCase.php b/tests/Unit/Extension/Core/Console/Dumper/DumperTestCase.php deleted file mode 100644 index 90827b7158..0000000000 --- a/tests/Unit/Extension/Core/Console/Dumper/DumperTestCase.php +++ /dev/null @@ -1,19 +0,0 @@ -dumper()->dump($output, $data); - - return $output->fetch(); - } - - abstract protected function dumper(); -} diff --git a/tests/Unit/Extension/Core/Console/Dumper/IndentedDumperTest.php b/tests/Unit/Extension/Core/Console/Dumper/IndentedDumperTest.php deleted file mode 100644 index 2b67df2cc7..0000000000 --- a/tests/Unit/Extension/Core/Console/Dumper/IndentedDumperTest.php +++ /dev/null @@ -1,44 +0,0 @@ -dump([ - 'hello' => 'test', - 'one' => [ - 'two' => 3, - 'four' => 5, - 'size' => [ - 'seven' => 'eight', - ] - ], - 'two' => [ - 'hai' => 'ho', - ], - ]); - $this->assertEquals(<<<'EOT' - hello:test - one: - two:3 - four:5 - size: - seven:eight - two: - hai:ho - - EOT - , $output); - } - - protected function dumper() - { - return new IndentedDumper(); - } -} diff --git a/tests/Unit/Extension/Core/Console/Dumper/JsonDumperTest.php b/tests/Unit/Extension/Core/Console/Dumper/JsonDumperTest.php deleted file mode 100644 index 0ced299baa..0000000000 --- a/tests/Unit/Extension/Core/Console/Dumper/JsonDumperTest.php +++ /dev/null @@ -1,21 +0,0 @@ -dump(['hello' => 'test']); - $this->assertEquals('{"hello":"test"}'."\n", $output); - } - - protected function dumper() - { - return new JsonDumper(); - } -} diff --git a/tests/Unit/Extension/Core/Console/Prompt/ChainPromptTest.php b/tests/Unit/Extension/Core/Console/Prompt/ChainPromptTest.php deleted file mode 100644 index 9afc37644d..0000000000 --- a/tests/Unit/Extension/Core/Console/Prompt/ChainPromptTest.php +++ /dev/null @@ -1,63 +0,0 @@ - - */ - private ObjectProphecy $prompt1; - - /** - * @var ObjectProphecy - */ - private ObjectProphecy $prompt2; - - private ChainPrompt $chainPrompt; - - public function setUp(): void - { - $this->prompt1 = $this->prophesize(Prompt::class); - $this->prompt1->name()->willReturn('prompt1'); - $this->prompt2 = $this->prophesize(Prompt::class); - $this->prompt2->name()->willReturn('prompt2'); - $this->chainPrompt = new ChainPrompt([ - $this->prompt1->reveal(), - $this->prompt2->reveal(), - ]); - } - - #[TestDox('It delegates to a supporting prompt')] - public function testDelegateToSupporting(): void - { - $this->prompt1->isSupported()->willReturn(false); - $this->prompt2->isSupported()->willReturn(true); - - $this->prompt2->prompt('Hello', 'World')->willReturn('Goodbye'); - - $response = $this->chainPrompt->prompt('Hello', 'World'); - $this->assertEquals('Goodbye', $response); - } - - #[TestDox('It throws an exception if no prompts are supported.')] - public function testPromptsNotSupported(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not prompt'); - $this->prompt1->isSupported()->willReturn(false); - $this->prompt2->isSupported()->willReturn(false); - - $this->chainPrompt->prompt('Hello', 'World'); - } -} diff --git a/tests/Unit/Extension/Core/Rpc/CacheClearHandlerTest.php b/tests/Unit/Extension/Core/Rpc/CacheClearHandlerTest.php deleted file mode 100644 index f32fdf7543..0000000000 --- a/tests/Unit/Extension/Core/Rpc/CacheClearHandlerTest.php +++ /dev/null @@ -1,34 +0,0 @@ - - */ - private ObjectProphecy $clearCache; - - public function setUp(): void - { - $this->clearCache = $this->prophesize(CacheClear::class); - } - - public function createHandler(): Handler - { - return new CacheClearHandler($this->clearCache->reveal()); - } - - public function testClearCache(): void - { - $this->clearCache->clearCache()->shouldBeCalled(); - $this->clearCache->cachePath()->willReturn('/path/to'); - $this->handle('cache_clear', []); - } -} diff --git a/tests/Unit/Extension/Core/Rpc/ConfigHandlerTest.php b/tests/Unit/Extension/Core/Rpc/ConfigHandlerTest.php deleted file mode 100644 index b07d536c84..0000000000 --- a/tests/Unit/Extension/Core/Rpc/ConfigHandlerTest.php +++ /dev/null @@ -1,24 +0,0 @@ - 'value1', - ]); - } - - public function testStatus(): void - { - $response = $this->handle('config', []); - $this->assertInstanceOf(InformationResponse::class, $response); - } -} diff --git a/tests/Unit/Extension/Core/Rpc/StatusHandlerTest.php b/tests/Unit/Extension/Core/Rpc/StatusHandlerTest.php deleted file mode 100644 index f5460b7c79..0000000000 --- a/tests/Unit/Extension/Core/Rpc/StatusHandlerTest.php +++ /dev/null @@ -1,90 +0,0 @@ - - */ - private ObjectProphecy $status; - - private ObjectProphecy $paths; - - public function setUp(): void - { - $this->status = $this->prophesize(Status::class); - $this->paths = $this->prophesize(PathCandidates::class); - } - - public function createHandler(): Handler - { - return new StatusHandler( - $this->status->reveal(), - $this->paths->reveal() - ); - } - - public function testMessageStatus(): void - { - $this->status->check()->willReturn([ - 'php_version' => '7.1', - 'phpactor_version' => 'version one', - 'cwd' => '/path/to/here', - 'good' => [ 'i am good' ], - 'bad' => [ 'i am bad' ], - ]); - $this->paths->getIterator()->will(function () { - yield new AbsolutePathCandidate('/config/file1.yml', 'yml'); - yield new AbsolutePathCandidate('/config/file2.yml', 'yml'); - }); - - $response = $this->handle('status', []); - $this->assertInstanceOf(EchoResponse::class, $response); - } - - public function testDetailStatus(): void - { - $this->status->check()->willReturn([ - 'php_version' => '7.1', - 'phpactor_version' => 'version one', - 'cwd' => '/path/to/here', - 'good' => ['i am good'], - 'bad' => ['i am bad'], - 'config_files' => [ - '/config/file1.yml' => true, - '/config/file2.yml' => false, - ], - 'filesystems' => ['git', 'simple'], - ]); - - $expected = [ - 'php_version' => '7.1', - 'phpactor_version' => 'version one', - 'cwd' => '/path/to/here', - 'config_files' => [ - '/config/file1.yml' => true, - '/config/file2.yml' => false, - ], - 'filesystems' => ['git', 'simple'], - 'diagnostics' => [ - 'i am good' => true, - 'i am bad' => false, - ], - ]; - - $response = $this->handle('status', ['type' => 'detailed']); - $this->assertInstanceOf(ReturnResponse::class, $response); - $this->assertEquals($expected, $response->value()); - } -} diff --git a/tests/Unit/Extension/Navigation/Navigator/ChainNavigatorTest.php b/tests/Unit/Extension/Navigation/Navigator/ChainNavigatorTest.php deleted file mode 100644 index 705c2b1e89..0000000000 --- a/tests/Unit/Extension/Navigation/Navigator/ChainNavigatorTest.php +++ /dev/null @@ -1,57 +0,0 @@ -navigator1 = $this->prophesize(Navigator::class); - $this->navigator2 = $this->prophesize(Navigator::class); - } - - public function testReturnsEmptyArrayWhenNoNavigators(): void - { - $navigator = $this->create([]); - $destinations = $navigator->destinationsFor(self::TEST_PATH); - $this->assertEquals([], $destinations); - } - - public function testMergesResultsOfTwoNavigators(): void - { - $navigator = $this->create([ - $this->navigator1->reveal(), - $this->navigator2->reveal(), - ]); - - $this->navigator1->destinationsFor(self::TEST_PATH)->willReturn([ 'dest1' => self::TEST_DESTINATION_1 ]); - $this->navigator2->destinationsFor(self::TEST_PATH)->willReturn([ 'dest2' => self::TEST_DESTINATION_2 ]); - - $destinations = $navigator->destinationsFor(self::TEST_PATH); - - $this->assertEquals([ - 'dest1' => self::TEST_DESTINATION_1, - 'dest2' => self::TEST_DESTINATION_2, - ], $destinations); - } - - private function create(array $navigators): Navigator - { - return new ChainNavigator($navigators); - } -} diff --git a/tests/Unit/Extension/Navigation/Navigator/PathFinderNavigatorTest.php b/tests/Unit/Extension/Navigation/Navigator/PathFinderNavigatorTest.php deleted file mode 100644 index 972677224b..0000000000 --- a/tests/Unit/Extension/Navigation/Navigator/PathFinderNavigatorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -pathFinder = $this->prophesize(PathFinder::class); - $this->navigator = new PathFinderNavigator($this->pathFinder->reveal()); - } - - public function testDelegatesToPathFinder(): void - { - $destinations = ['one' => 'two']; - $this->pathFinder->destinationsFor(self::TEST_PATH)->willReturn($destinations); - $result = $this->navigator->destinationsFor(self::TEST_PATH); - - $this->assertEquals($destinations, $result); - } -} diff --git a/tests/Unit/Extension/Navigation/Rpc/NavigateHandlerTest.php b/tests/Unit/Extension/Navigation/Rpc/NavigateHandlerTest.php deleted file mode 100644 index 1a6d866fda..0000000000 --- a/tests/Unit/Extension/Navigation/Rpc/NavigateHandlerTest.php +++ /dev/null @@ -1,80 +0,0 @@ -navigator = $this->prophesize(Navigator::class); - $this->destinations = [ - self::TEST_DEST1 => '/path/to/dest1', - 'dest2' => '/path/to/dest2', - ]; - } - - public function createHandler(): Handler - { - return new NavigateHandler($this->navigator->reveal()); - } - - public function testDestinations(): void - { - $this->navigator->destinationsFor(self::TEST_PATH)->willReturn($this->destinations); - $response = $this->handle('navigate', [ - NavigateHandler::PARAM_SOURCE_PATH => self::TEST_PATH, - ]); - - /** @var InputCallbackResponse $response */ - $this->assertInstanceOf(InputCallbackResponse::class, $response); - $inputs = $response->inputs(); - $input = reset($inputs); - $this->assertInstanceOf(ChoiceInput::class, $input); - } - - public function testCanCreateConfirm(): void - { - $this->navigator->destinationsFor(self::TEST_PATH)->willReturn($this->destinations); - $this->navigator->canCreateNew(self::TEST_PATH, self::TEST_DEST1)->willReturn(true); - - $response = $this->handle('navigate', [ - NavigateHandler::PARAM_SOURCE_PATH => self::TEST_PATH, - NavigateHandler::PARAM_DESTINATION => self::TEST_DEST1, - ]); - - $this->assertInstanceOf(InputCallbackResponse::class, $response); - $inputs = $response->inputs(); - $input = reset($inputs); - $this->assertInstanceOf(ConfirmInput::class, $input); - } - - public function testOpenFile(): void - { - $this->navigator->destinationsFor(self::TEST_PATH)->willReturn($this->destinations); - $this->navigator->canCreateNew(self::TEST_PATH, self::TEST_DEST1)->willReturn(false); - - $response = $this->handle('navigate', [ - NavigateHandler::PARAM_SOURCE_PATH => self::TEST_PATH, - NavigateHandler::PARAM_DESTINATION => self::TEST_DEST1, - ]); - - $this->assertInstanceOf(OpenFileResponse::class, $response); - } -} diff --git a/tests/Unit/Extension/PhpVersionResolver/Model/ChainResolverTest.php b/tests/Unit/Extension/PhpVersionResolver/Model/ChainResolverTest.php deleted file mode 100644 index 968f84b91b..0000000000 --- a/tests/Unit/Extension/PhpVersionResolver/Model/ChainResolverTest.php +++ /dev/null @@ -1,27 +0,0 @@ -expectException(RuntimeException::class); - (new ChainResolver())->resolve(); - } - - public function testResolvesVersion(): void - { - $resolver = $this->prophesize(PhpVersionResolver::class); - $resolver->resolve()->willReturn('7.1'); - self::assertEquals('7.1', (new ChainResolver($resolver->reveal()))->resolve()); - } -} diff --git a/tests/Unit/Extension/PhpVersionResolver/Model/ComposerPhpVersionResolverTest.php b/tests/Unit/Extension/PhpVersionResolver/Model/ComposerPhpVersionResolverTest.php deleted file mode 100644 index e901be7fdc..0000000000 --- a/tests/Unit/Extension/PhpVersionResolver/Model/ComposerPhpVersionResolverTest.php +++ /dev/null @@ -1,48 +0,0 @@ -workspace()->reset(); - $this->workspace()->loadManifest( - <<<'EOT' - // File: composer.json - { - "require": { - "php": "^7.1" - } - } - EOT - ); - $resolver = new ComposerPhpVersionResolver($this->workspace()->path('/composer.json')); - self::assertEquals('7.1', $resolver->resolve()); - } - - public function testReturnsPlatformWithHigherPrio(): void - { - $this->workspace()->reset(); - $this->workspace()->loadManifest( - <<<'EOT' - // File: composer.json - { - "require": { - "php": "^7.1" - }, - "config": { - "platform": { - "php": "7.3" - } - } - } - EOT - ); - $resolver = new ComposerPhpVersionResolver($this->workspace()->path('/composer.json')); - self::assertEquals('7.3', $resolver->resolve()); - } -} diff --git a/tests/Unit/Extension/Rpc/HandlerTestCase.php b/tests/Unit/Extension/Rpc/HandlerTestCase.php deleted file mode 100644 index 8e4f099c4a..0000000000 --- a/tests/Unit/Extension/Rpc/HandlerTestCase.php +++ /dev/null @@ -1,32 +0,0 @@ - $parameters */ - protected function handle(string $actionName, array $parameters): Response - { - $registry = new ActiveHandlerRegistry([ - $this->createHandler() - ]); - $requestHandler = new RequestHandler($registry); - $request = Request::fromNameAndParameters($actionName, $parameters); - - return $requestHandler->handle($request); - } -} diff --git a/tests/Unit/Extension/SourceCodeFilesystem/Rpc/ClassSearchHandlerTest.php b/tests/Unit/Extension/SourceCodeFilesystem/Rpc/ClassSearchHandlerTest.php deleted file mode 100644 index 5eedfe9b68..0000000000 --- a/tests/Unit/Extension/SourceCodeFilesystem/Rpc/ClassSearchHandlerTest.php +++ /dev/null @@ -1,90 +0,0 @@ -classSearch = $this->prophesize(ClassSearch::class); - } - - public function createHandler(): Handler - { - return new ClassSearchHandler( - $this->classSearch->reveal() - ); - } - - /** - * If not results are found, echo a message - */ - public function testNoResults(): void - { - $this->classSearch->classSearch('composer', 'AAA') - ->willReturn([]); - - $action = $this->handle('class_search', [ - 'short_name' => 'AAA', - ]); - - $this->assertInstanceOf(EchoResponse::class, $action); - $this->assertStringContainsString('No classes found', $action->message()); - } - - /** - * If 1 result is found, return the value. - */ - public function testOneResult(): void - { - $this->classSearch->classSearch('composer', 'AAA') - ->willReturn([ - [ - 'class' => 'Foobar', - ] - ]); - - $action = $this->handle('class_search', [ - 'short_name' => 'AAA', - ]); - - $this->assertInstanceOf(ReturnResponse::class, $action); - $this->assertEquals([ - 'class' => 'Foobar', - ], $action->value()); - } - - /** - * Many results, show a choice - */ - public function testManyResult(): void - { - $this->classSearch->classSearch('composer', 'AAA') - ->willReturn([ - [ - 'class' => 'AAA', - ], - [ - 'class' => 'BBB', - ], - ]); - - $action = $this->handle('class_search', [ - 'short_name' => 'AAA', - ]); - - $this->assertInstanceOf(ReturnChoiceResponse::class, $action); - $this->assertCount(2, $action->options()); - } -} diff --git a/tests/Unit/Extension/WorseReflection/Rpc/OffsetInfoHandlerTest.php b/tests/Unit/Extension/WorseReflection/Rpc/OffsetInfoHandlerTest.php deleted file mode 100644 index 648947ba91..0000000000 --- a/tests/Unit/Extension/WorseReflection/Rpc/OffsetInfoHandlerTest.php +++ /dev/null @@ -1,36 +0,0 @@ -addSource(self::SOURCE)->build() - ); - } - - public function testOffsetInfo(): void - { - $action = $this->createHandler()->handle([ - 'offset' => 19, - 'source' => self::SOURCE - ]); - - $this->assertInstanceOf(InformationResponse::class, $action); - $this->assertStringContainsString('symbol', $action->information()); - } -} diff --git a/tests/Unit/PhpactorTest.php b/tests/Unit/PhpactorTest.php deleted file mode 100644 index a8af2f3742..0000000000 --- a/tests/Unit/PhpactorTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertEquals($isFile, Phpactor::isFile($example)); - } - - /** - * @return Generator - */ - public static function provideIsFile(): Generator - { - yield [ 'Hello.php', true ]; - yield [ 'Hello\\Bar', false ]; - yield [ 'Hello', false ]; - yield [ './Hello/Bar', true ]; - yield [ 'Foobar/*', true ]; - yield [ 'lib/Badger.php', true ]; - } -} diff --git a/tests/VimPlugin/add_missing_assignments.vader b/tests/VimPlugin/add_missing_assignments.vader deleted file mode 100644 index ab576f7d1a..0000000000 --- a/tests/VimPlugin/add_missing_assignments.vader +++ /dev/null @@ -1,33 +0,0 @@ -Given php (): - phpactor = new Phpactor(); - $this->foobar = 'string'; - } - } -Do (put the cursor over an existing class name and add use): - :call phpactor#Transform("add_missing_properties")\ - -Expect php (assignments to be added): - phpactor = new Phpactor(); - $this->foobar = 'string'; - } - } - diff --git a/tests/VimPlugin/apply_text_edits/replace_a_line.vader b/tests/VimPlugin/apply_text_edits/replace_a_line.vader deleted file mode 100644 index 4b9fff8e2c..0000000000 --- a/tests/VimPlugin/apply_text_edits/replace_a_line.vader +++ /dev/null @@ -1,43 +0,0 @@ -Include: utils.vader - -Given php (Original source): - foo(); - } - } - -Execute (Goes to the method line and changes the visibility): - 10 - call Apply( - \ Delete(10), - \ Insert(10, ' public function foo()') - \ ) - -Then (The cursor should not have moved): - call AssertCursorDidntMove() - -Expect php (The visibility shoud have changed): - foo(); - } - } diff --git a/tests/VimPlugin/apply_text_edits/replace_line_which_move.vader b/tests/VimPlugin/apply_text_edits/replace_line_which_move.vader deleted file mode 100644 index 362e2c2a3f..0000000000 --- a/tests/VimPlugin/apply_text_edits/replace_line_which_move.vader +++ /dev/null @@ -1,49 +0,0 @@ -Include: utils.vader - -Given php (Original source): - foo(); - } - } - -Execute (Goes to the method line and changes the visibility): - 10 - call Apply( - \ Delete(10), - \ Insert(10, ' /**'), - \ Insert(11, ' * @var int $i'), - \ Insert(12, ' */'), - \ Insert(13, ' private function foo(int $i)') - \ ) - -Then (Then cursor should still be on the method signature): - call AssertCursorOnLine(13) - -Expect php (The docblock should be added and the signature changed): - foo(); - } - } diff --git a/tests/VimPlugin/apply_text_edits/utils.vader b/tests/VimPlugin/apply_text_edits/utils.vader deleted file mode 100644 index a0e22a000a..0000000000 --- a/tests/VimPlugin/apply_text_edits/utils.vader +++ /dev/null @@ -1,39 +0,0 @@ -Before: - Save g:applyTextEditsPosBefore - - set nofoldenable - - function! Apply(...) - let g:applyTextEditsPosBefore = getpos('.') - let ApplyTextEdits = function('phpactor#_applyTextEdits', [expand('%:p')]) - - call ApplyTextEdits(a:000) - endfunction - - function! Position(line) - return { 'line': a:line, 'character': 0 } - endfunction - - function! Delete(startLine, ...) - let startLine = a:startLine - 1 - let endLine = 0 < a:0 ? a:1 : a:startLine - - return { 'start': Position(startLine), 'end': Position(endLine), 'text': '' } - endfunction - - function! Insert(line, text) - let line = a:line - 1 - - return { 'start': Position(line), 'end': Position(line), 'text': a:text } - endfunction - - function! AssertCursorDidntMove() - AssertEqual g:applyTextEditsPosBefore, getpos('.') - endfunction - - function! AssertCursorOnLine(line) - AssertEqual a:line, line('.') - endfunction - -After: - Restore diff --git a/tests/VimPlugin/complete.vader b/tests/VimPlugin/complete.vader deleted file mode 100644 index 8b141ffc9a..0000000000 --- a/tests/VimPlugin/complete.vader +++ /dev/null @@ -1,34 +0,0 @@ -Given php (single): - fz" - AssertEqual 22, phpactor#Complete(1,0) - -Given php (name method call): - add(Suggestion::cre('k', 'implements ', '')); - $suggestions->add(Suggestion::cre('k', 'implements ', '')); - -Execute: - execute "normal! /cre\ll" - AssertEqual 30, phpactor#Complete(1,0) diff --git a/tests/VimPlugin/get_class_full_name.vader b/tests/VimPlugin/get_class_full_name.vader deleted file mode 100644 index a1356afef3..0000000000 --- a/tests/VimPlugin/get_class_full_name.vader +++ /dev/null @@ -1,4 +0,0 @@ -Execute: - execute "edit lib/Foo/Bar.php" - let fqn = phpactor#GetClassFullName() - AssertEqual 'Phpactor\Foo\Bar', fqn diff --git a/tests/VimPlugin/get_namespace.vader b/tests/VimPlugin/get_namespace.vader deleted file mode 100644 index cd4ffcbcb0..0000000000 --- a/tests/VimPlugin/get_namespace.vader +++ /dev/null @@ -1,4 +0,0 @@ -Execute: - execute "edit lib/Foo/Bar.php" - let namespace = phpactor#GetNamespace() - AssertEqual 'Phpactor\Foo', namespace diff --git a/tests/VimPlugin/goto_reference.vader b/tests/VimPlugin/goto_reference.vader deleted file mode 100644 index 9726afdc33..0000000000 --- a/tests/VimPlugin/goto_reference.vader +++ /dev/null @@ -1,13 +0,0 @@ -Given php: - nn - :call phpactor#GotoDefinition()\ - -Then: - AssertEqual "Phpactor.php", expand('%:t') diff --git a/tests/VimPlugin/goto_type.vader b/tests/VimPlugin/goto_type.vader deleted file mode 100644 index 73b571f201..0000000000 --- a/tests/VimPlugin/goto_type.vader +++ /dev/null @@ -1,13 +0,0 @@ -Given php: - nn - :call phpactor#GotoType()\ - -Then: - AssertEqual "Phpactor.php", expand('%:t') diff --git a/tests/VimPlugin/insert_use.vader b/tests/VimPlugin/insert_use.vader deleted file mode 100644 index cf3f78f9e4..0000000000 --- a/tests/VimPlugin/insert_use.vader +++ /dev/null @@ -1,24 +0,0 @@ -Given php (source with no namespace): - - :call phpactor#UseAdd()\ - -Expect php (the use statement to be inserted): -