diff --git a/.editorconfig b/.editorconfig
index 5d66bc427b..92ecb85c25 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,7 +6,7 @@ insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
-[*.{php,phpt}]
+[*.{php,phpt,stub}]
indent_style = tab
indent_size = 4
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index d04f75155f..0000000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-version: 2
-updates:
-- package-ecosystem: composer
- directory: "/build-cs"
- schedule:
- interval: weekly
- open-pull-requests-limit: 10
-- package-ecosystem: composer
- directory: "/compiler"
- schedule:
- interval: weekly
- open-pull-requests-limit: 10
-- package-ecosystem: github-actions
- directory: "/"
- schedule:
- interval: monthly
- open-pull-requests-limit: 10
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 0000000000..e23901f133
--- /dev/null
+++ b/.github/renovate.json
@@ -0,0 +1,33 @@
+{
+ "extends": [
+ "config:base",
+ "schedule:weekly"
+ ],
+ "rangeStrategy": "update-lockfile",
+ "packageRules": [
+ {
+ "matchPackagePatterns": ["*"],
+ "enabled": false
+ },
+ {
+ "matchPaths": ["+(composer.json)"],
+ "enabled": true,
+ "groupName": "root-composer"
+ },
+ {
+ "matchPaths": ["build-cs/**"],
+ "enabled": true,
+ "groupName": "build-cs"
+ },
+ {
+ "matchPaths": ["compiler/**"],
+ "enabled": true,
+ "groupName": "compiler"
+ },
+ {
+ "matchPaths": [".github/**"],
+ "enabled": true,
+ "groupName": "github-actions"
+ }
+ ]
+}
diff --git a/.github/workflows/backward-compatibility.yml b/.github/workflows/backward-compatibility.yml
index 1e60de1f30..b1bdee7855 100644
--- a/.github/workflows/backward-compatibility.yml
+++ b/.github/workflows/backward-compatibility.yml
@@ -6,16 +6,17 @@ on:
pull_request:
push:
branches:
- - "master"
+ - "**"
env:
- COMPOSER_ROOT_VERSION: "0.12.x-dev"
+ COMPOSER_ROOT_VERSION: "1.5.x-dev"
jobs:
backward-compatibility:
name: "Backward Compatibility"
runs-on: "ubuntu-latest"
+ timeout-minutes: 60
steps:
- name: "Checkout"
@@ -30,13 +31,13 @@ jobs:
php-version: "8.0"
- name: "Install dependencies"
- run: "composer install --no-dev --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-dev --no-interaction --no-progress"
- name: "Install BackwardCompatibilityCheck"
run: |
composer global config minimum-stability dev
composer global config prefer-stable true
- composer global require --dev ondrejmirtes/backward-compatibility-check:^5.0.7
+ composer global require --dev ondrejmirtes/backward-compatibility-check:^5.0.8
- name: "Check"
run: "$(composer global config bin-dir --absolute)/roave-backward-compatibility-check"
diff --git a/.github/workflows/compiler-tests.yml b/.github/workflows/compiler-tests.yml
index dd87983600..3333d665fe 100644
--- a/.github/workflows/compiler-tests.yml
+++ b/.github/workflows/compiler-tests.yml
@@ -6,16 +6,17 @@ on:
pull_request:
push:
branches:
- - "master"
+ - "**"
env:
- COMPOSER_ROOT_VERSION: "0.12.x-dev"
+ COMPOSER_ROOT_VERSION: "1.5.x-dev"
jobs:
compiler-tests:
name: "Compiler Tests"
runs-on: "ubuntu-latest"
+ timeout-minutes: 60
steps:
- name: "Checkout"
@@ -28,10 +29,7 @@ jobs:
php-version: "8.0"
- name: "Install dependencies"
- run: "composer install --no-dev --no-interaction --no-progress --no-suggest"
-
- - name: "Transform source code"
- run: php bin/transform-source.php
+ run: "composer install --no-interaction --no-progress"
- name: "Tests"
run: |
@@ -44,5 +42,26 @@ jobs:
- uses: actions/upload-artifact@v2
with:
- name: phpstan.phar
+ name: phar-file
path: tmp/phpstan.phar
+
+ integration-tests:
+ if: github.event_name == 'pull_request'
+ needs: compiler-tests
+ uses: phpstan/phpstan/.github/workflows/integration-tests.yml@1.5.x
+ with:
+ ref: 1.5.x
+
+ extension-tests:
+ if: github.event_name == 'pull_request'
+ needs: compiler-tests
+ uses: phpstan/phpstan/.github/workflows/extension-tests.yml@1.5.x
+ with:
+ ref: 1.5.x
+
+ other-tests:
+ if: github.event_name == 'pull_request'
+ needs: compiler-tests
+ uses: phpstan/phpstan/.github/workflows/other-tests.yml@1.5.x
+ with:
+ ref: 1.5.x
diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
index 649cfc9cfc..2c7ff7add9 100644
--- a/.github/workflows/e2e-tests.yml
+++ b/.github/workflows/e2e-tests.yml
@@ -4,24 +4,29 @@ name: "E2E Tests"
on:
pull_request:
+ paths-ignore:
+ - 'compiler/**'
push:
branches:
- - "master"
+ - "**"
+ paths-ignore:
+ - 'compiler/**'
env:
- COMPOSER_ROOT_VERSION: "0.12.x-dev"
+ COMPOSER_ROOT_VERSION: "1.5.x-dev"
jobs:
result-cache-e2e-tests:
name: "Result cache E2E tests"
runs-on: ${{ matrix.operating-system }}
+ timeout-minutes: 60
strategy:
fail-fast: false
matrix:
php-version:
- - "7.4"
+ - "8.0"
operating-system: [ubuntu-latest, windows-latest]
steps:
@@ -37,7 +42,7 @@ jobs:
ini-values: memory_limit=256M
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- name: "Tests"
run: |
@@ -46,6 +51,7 @@ jobs:
e2e-tests:
name: "E2E tests"
runs-on: "ubuntu-latest"
+ timeout-minutes: 60
strategy:
matrix:
@@ -68,12 +74,12 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
- php-version: "7.4"
+ php-version: "8.0"
tools: ${{ matrix.tools }}
extensions: ${{ matrix.extensions }}
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- name: "Test"
run: ${{ matrix.script }}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 22c3bd4f36..0d64be5317 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -6,15 +6,16 @@ on:
pull_request:
push:
branches:
- - "master"
+ - "**"
env:
- COMPOSER_ROOT_VERSION: "0.12.x-dev"
+ COMPOSER_ROOT_VERSION: "1.5.x-dev"
jobs:
lint:
name: "Lint"
runs-on: "ubuntu-latest"
+ timeout-minutes: 60
strategy:
fail-fast: false
@@ -25,6 +26,7 @@ jobs:
- "7.3"
- "7.4"
- "8.0"
+ - "8.1"
steps:
- name: "Checkout"
@@ -40,11 +42,25 @@ jobs:
run: "composer validate"
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
+
+ - name: "Install PHP for code transform"
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: 8.0
- name: "Transform source code"
- if: matrix.php-version != '7.4' && matrix.php-version != '8.0'
- run: php bin/transform-source.php
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ run: "build/transform-source ${{ matrix.php-version }}"
+
+ - name: "Reinstall matrix PHP version"
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
- name: "Lint"
run: "make lint"
@@ -53,6 +69,7 @@ jobs:
name: "Coding Standard"
runs-on: "ubuntu-latest"
+ timeout-minutes: 60
strategy:
matrix:
@@ -73,7 +90,7 @@ jobs:
run: "composer validate"
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- name: "Lint"
run: "make lint"
@@ -85,6 +102,7 @@ jobs:
name: "Dependency Analysis"
runs-on: "ubuntu-latest"
+ timeout-minutes: 60
strategy:
matrix:
@@ -102,7 +120,7 @@ jobs:
php-version: "${{ matrix.php-version }}"
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- name: "Composer Require Checker"
run: "make composer-require-checker"
diff --git a/.github/workflows/merge-bot-pr.yml b/.github/workflows/merge-bot-pr.yml
new file mode 100644
index 0000000000..aa8c91d70f
--- /dev/null
+++ b/.github/workflows/merge-bot-pr.yml
@@ -0,0 +1,29 @@
+# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
+# https://github.com/WyriHaximus/github-action-wait-for-status
+
+name: Merge bot PR
+on:
+ pull_request:
+ types:
+ - opened
+jobs:
+ automerge:
+ name: Automerge PRs
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Wait for status checks'
+ if: github.event.pull_request.user.login == 'phpstan-bot'
+ id: waitforstatuschecks
+ uses: "WyriHaximus/github-action-wait-for-status@v1"
+ with:
+ ignoreActions: automerge
+ checkInterval: 13
+ env:
+ GITHUB_TOKEN: "${{ secrets.PHPSTAN_BOT_TOKEN }}"
+ - name: Merge Pull Request
+ uses: juliangruber/merge-pull-request-action@v1
+ if: steps.waitforstatuschecks.outputs.status == 'success'
+ with:
+ github-token: "${{ secrets.PHPSTAN_BOT_TOKEN }}"
+ number: "${{ github.event.number }}"
+ method: rebase
diff --git a/.github/workflows/phar.yml b/.github/workflows/phar.yml
index a1a99ee8a6..3952d78e56 100644
--- a/.github/workflows/phar.yml
+++ b/.github/workflows/phar.yml
@@ -5,14 +5,17 @@ name: "Compile PHAR"
on:
push:
branches:
- - "master"
+ - "1.5.x"
tags:
- - '*'
+ - '1.5.*'
+
+concurrency: phar
jobs:
compile:
name: "Compile PHAR"
runs-on: "ubuntu-latest"
+ timeout-minutes: 60
steps:
- name: "Checkout"
@@ -27,13 +30,10 @@ jobs:
php-version: "8.0"
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- name: "Install compiler dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest --working-dir=compiler"
-
- - name: "Transform source code"
- run: php bin/transform-source.php
+ run: "composer install --no-interaction --no-progress --working-dir=compiler"
- name: "Compile PHAR"
run: php compiler/bin/compile
@@ -53,6 +53,7 @@ jobs:
repository: phpstan/phpstan
path: phpstan-dist
token: ${{ secrets.PAT }}
+ ref: 1.5.x
- name: "cp PHAR"
run: cp tmp/phpstan.phar phpstan-dist/phpstan.phar
@@ -79,13 +80,13 @@ jobs:
git config user.email "ondrej@mirtes.cz" && \
git config user.name "Ondrej Mirtes"
- - name: "Commit PHAR - master"
+ - name: "Commit PHAR - development"
working-directory: phpstan-dist
if: "!startsWith(github.ref, 'refs/tags/')"
run: |
git add phpstan.phar phpstan.phar.asc && \
git commit -S -m "Updated PHPStan to commit ${{ github.event.after }}" -m "${{ steps.git-log.outputs.log }}" && \
- git push --quiet origin master
+ git push --quiet origin 1.5.x
- name: "Commit PHAR - tag"
working-directory: phpstan-dist
@@ -93,6 +94,6 @@ jobs:
run: |
git add phpstan.phar phpstan.phar.asc && \
git commit -S -m "PHPStan ${GITHUB_REF#refs/tags/}" -m "${{ steps.git-log.outputs.log }}" && \
- git push --quiet origin master && \
+ git push --quiet origin 1.5.x && \
git tag -s ${GITHUB_REF#refs/tags/} -m "${GITHUB_REF#refs/tags/}" && \
git push --quiet origin ${GITHUB_REF#refs/tags/}
diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index 7e829b1f7b..7a2e880070 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -4,17 +4,22 @@ name: "Static Analysis"
on:
pull_request:
+ paths-ignore:
+ - 'compiler/**'
push:
branches:
- - "master"
+ - "**"
+ paths-ignore:
+ - 'compiler/**'
env:
- COMPOSER_ROOT_VERSION: "0.12.x-dev"
+ COMPOSER_ROOT_VERSION: "1.5.x-dev"
jobs:
static-analysis:
name: "PHPStan"
runs-on: ${{ matrix.operating-system }}
+ timeout-minutes: 60
strategy:
fail-fast: false
@@ -25,6 +30,7 @@ jobs:
- "7.3"
- "7.4"
- "8.0"
+ - "8.1"
operating-system: [ubuntu-latest, windows-latest]
script:
- "make phpstan"
@@ -43,15 +49,32 @@ jobs:
extensions: mbstring
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- - name: "Downgrade PHPUnit"
- if: matrix.php-version == '7.1' || matrix.php-version == '7.2'
- run: "composer require --dev phpunit/phpunit:^7.5.20 brianium/paratest:^4.0 --update-with-dependencies"
+ - name: "Install PHP for code transform"
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: 8.0
+ extensions: mbstring
- name: "Transform source code"
- if: matrix.php-version != '7.4' && matrix.php-version != '8.0'
- run: php bin/transform-source.php
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ shell: bash
+ run: "build/transform-source ${{ matrix.php-version }}"
+
+ - name: "Reinstall matrix PHP version"
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+ extensions: mbstring
+
+ - name: "Downgrade PHPUnit"
+ if: matrix.php-version == '7.1' || matrix.php-version == '7.2'
+ run: "composer require --dev phpunit/phpunit:^7.5.20 brianium/paratest:^4.0 --update-with-dependencies --ignore-platform-reqs"
- name: "PHPStan"
run: ${{ matrix.script }}
@@ -59,12 +82,13 @@ jobs:
static-analysis-with-result-cache:
name: "PHPStan with result cache"
- runs-on: ubuntu-latest
+ runs-on: "ubuntu-latest"
+ timeout-minutes: 60
strategy:
matrix:
php-version:
- - "7.4"
+ - "8.0"
steps:
- name: "Checkout"
@@ -78,7 +102,7 @@ jobs:
extensions: mbstring
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- name: "Cache Result cache"
uses: actions/cache@v2
@@ -105,11 +129,12 @@ jobs:
name: "Generate baseline"
runs-on: "ubuntu-latest"
+ timeout-minutes: 60
strategy:
matrix:
php-version:
- - "7.4"
+ - "8.0"
steps:
- name: "Checkout"
@@ -122,7 +147,7 @@ jobs:
php-version: "${{ matrix.php-version }}"
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- name: "Generate baseline"
run: |
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 63439d983c..cfdbc37dfe 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -4,17 +4,22 @@ name: "Tests"
on:
pull_request:
+ paths-ignore:
+ - 'compiler/**'
push:
branches:
- - "master"
+ - "**"
+ paths-ignore:
+ - 'compiler/**'
env:
- COMPOSER_ROOT_VERSION: "0.12.x-dev"
+ COMPOSER_ROOT_VERSION: "1.5.x-dev"
jobs:
tests:
name: "Tests"
runs-on: ${{ matrix.operating-system }}
+ timeout-minutes: 60
strategy:
fail-fast: false
@@ -23,6 +28,7 @@ jobs:
- "7.3"
- "7.4"
- "8.0"
+ - "8.1"
operating-system: [ ubuntu-latest, windows-latest ]
script:
- "make tests"
@@ -43,11 +49,30 @@ jobs:
ini-values: memory_limit=640M
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
+
+ - name: "Install PHP for code transform"
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: 8.0
+ extensions: mbstring
- name: "Transform source code"
- if: matrix.php-version != '7.4' && matrix.php-version != '8.0'
- run: php bin/transform-source.php
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ shell: bash
+ run: "build/transform-source ${{ matrix.php-version }}"
+
+ - name: "Reinstall matrix PHP version"
+ if: matrix.php-version != '8.0' && matrix.php-version != '8.1'
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+ tools: pecl
+ extensions: ds,mbstring
+ ini-values: memory_limit=640M
- name: "Tests"
run: "${{ matrix.script }}"
@@ -55,6 +80,7 @@ jobs:
tests-old-phpunit:
name: "Tests with old PHPUnit"
runs-on: ${{ matrix.operating-system }}
+ timeout-minutes: 60
strategy:
fail-fast: false
@@ -82,44 +108,29 @@ jobs:
ini-values: memory_limit=640M
- name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ run: "composer install --no-interaction --no-progress"
- - name: "Downgrade PHPUnit"
- run: "composer require --dev phpunit/phpunit:^7.5.20 brianium/paratest:^4.0 --update-with-dependencies"
+ - name: "Install PHP for code transform"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "8.0"
- name: "Transform source code"
- run: php bin/transform-source.php
+ shell: bash
+ run: "build/transform-source ${{ matrix.php-version }}"
- - name: "Tests"
- run: "${{ matrix.script }}"
-
- tests-code-coverage:
- name: "Tests with code coverage"
-
- runs-on: "ubuntu-latest"
-
- steps:
- - name: "Checkout"
- uses: "actions/checkout@v2"
-
- - name: "Install PHP"
+ - name: "Reinstall matrix PHP version"
uses: "shivammathur/setup-php@v2"
with:
- coverage: "pcov"
- php-version: "7.4"
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
tools: pecl
- extensions: ds
+ extensions: ds,mbstring
+ ini-values: memory_limit=640M
- - name: "Install dependencies"
- run: "composer install --no-interaction --no-progress --no-suggest"
+ - name: "Downgrade PHPUnit"
+ run: "composer require --dev phpunit/phpunit:^7.5.20 brianium/paratest:^4.0 --update-with-dependencies --ignore-platform-reqs"
- name: "Tests"
- run: |
- php -dpcov.enabled=1 -dpcov.directory=. -dpcov.exclude="~vendor~" vendor/bin/phpunit
-
- - name: "Coveralls"
- env:
- COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- composer require twinh/php-coveralls --dev && \
- vendor/bin/php-coveralls --verbose --coverage_clover=tests/tmp/clover.xml --json_path=tests/tmp/coveralls-upload.json
+ run: "${{ matrix.script }}"
diff --git a/.github/workflows/update-phpstorm-stubs.yml b/.github/workflows/update-phpstorm-stubs.yml
new file mode 100644
index 0000000000..b1618055b0
--- /dev/null
+++ b/.github/workflows/update-phpstorm-stubs.yml
@@ -0,0 +1,50 @@
+# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
+
+name: "Update PhpStorm stubs"
+on:
+ workflow_dispatch:
+ schedule:
+ # * is a special character in YAML so you have to quote this string
+ - cron: '0 0 * * 2'
+
+jobs:
+ update-phpstorm-stubs:
+ name: "Update PhpStorm stubs"
+ if: ${{ github.repository == 'phpstan/phpstan-src' }}
+ runs-on: "ubuntu-latest"
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+ with:
+ ref: ${{ github.head_ref }}
+ fetch-depth: '0'
+ token: ${{ secrets.PHPSTAN_BOT_TOKEN }}
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "8.0"
+ - name: "Install dependencies"
+ run: "composer install --no-interaction --no-progress"
+ - name: "Checkout stubs"
+ uses: "actions/checkout@v2"
+ with:
+ path: "phpstorm-stubs"
+ repository: "jetbrains/phpstorm-stubs"
+ - name: "Update stubs"
+ run: "composer require jetbrains/phpstorm-stubs:dev-master#$(git -C phpstorm-stubs rev-parse HEAD)"
+ - name: "Remove stubs repo"
+ run: "rm -r phpstorm-stubs"
+ - name: "Update function metadata"
+ run: "./bin/generate-function-metadata.php"
+ - name: "Create Pull Request"
+ id: create-pr
+ uses: peter-evans/create-pull-request@v3
+ with:
+ token: ${{ secrets.PHPSTAN_BOT_TOKEN }}
+ branch-suffix: random
+ delete-branch: true
+ title: "Update PhpStorm stubs"
+ body: "Update PhpStorm stubs"
+ committer: "phpstan-bot "
+ commit-message: "Update PhpStorm stubs"
diff --git a/.gitignore b/.gitignore
index 625cace6ae..65cd70f464 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
/compiler/vendor
/conf/config.local.yml
/vendor
-/.idea
+/.idea/*
+!.idea/icon.png
/tests/tmp
/tests/.phpunit.result.cache
+tmp/.memory_limit
diff --git a/.idea/icon.png b/.idea/icon.png
new file mode 100644
index 0000000000..5f346e71c1
Binary files /dev/null and b/.idea/icon.png differ
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 162e250c6d..7ab0db920a 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,74 +1,134 @@
-# Contributor Code of Conduct
+
+# Contributor Covenant Code of Conduct
## Our Pledge
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to making participation in our project and
-our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, gender identity and expression, level of experience,
-nationality, personal appearance, race, religion, or sexual identity and
-orientation.
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
## Our Standards
-Examples of behavior that contributes to creating a positive environment
-include:
+Examples of behavior that contributes to a positive environment for our
+community include:
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the overall
+ community
-Examples of unacceptable behavior by participants include:
+Examples of unacceptable behavior include:
-* The use of sexualized language or imagery and unwelcome sexual attention or
-advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
+* The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
-* Publishing others' private information, such as a physical or electronic
- address, without explicit permission
+* Publishing others' private information, such as a physical or email address,
+ without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
-## Our Responsibilities
+## Enforcement Responsibilities
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
## Scope
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community. Examples of
-representing a project or community include using an official project e-mail
-address, posting via an official social media account, or acting as an appointed
-representative at an online or offline event. Representation of a project may be
-further defined and clarified by project maintainers.
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project maintainer at . All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. The project team is
-obligated to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
+reported to the community leaders responsible for enforcement at
+ondrej@mirtes.cz.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
## Attribution
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
-available at [http://contributor-covenant.org/version/1/4][version]
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
-[homepage]: http://contributor-covenant.org
-[version]: http://contributor-covenant.org/version/1/4/
diff --git a/Makefile b/Makefile
index 0bfa9d747e..fa08492748 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,8 @@ lint:
--exclude tests/PHPStan/Rules/Operators/data/invalid-inc-dec.php \
--exclude tests/PHPStan/Rules/Arrays/data/offset-access-without-dim-for-reading.php \
--exclude tests/PHPStan/Rules/Classes/data/duplicate-declarations.php \
+ --exclude tests/PHPStan/Rules/Classes/data/duplicate-enum-cases.php \
+ --exclude tests/PHPStan/Rules/Classes/data/enum-sanity.php \
--exclude tests/PHPStan/Rules/Classes/data/extends-error.php \
--exclude tests/PHPStan/Rules/Classes/data/implements-error.php \
--exclude tests/PHPStan/Rules/Classes/data/interface-extends-error.php \
@@ -41,7 +43,13 @@ lint:
--exclude tests/PHPStan/Rules/Functions/data/arrow-function-nullsafe-by-ref.php \
--exclude tests/PHPStan/Levels/data/namedArguments.php \
--exclude tests/PHPStan/Rules/Keywords/data/continue-break.php \
- src tests compiler/src
+ --exclude tests/PHPStan/Rules/Properties/data/read-only-property.php \
+ --exclude tests/PHPStan/Rules/Properties/data/overriding-property.php \
+ --exclude tests/PHPStan/Rules/Constants/data/overriding-final-constant.php \
+ --exclude tests/PHPStan/Rules/Properties/data/intersection-types.php \
+ --exclude tests/PHPStan/Rules/Classes/data/first-class-instantiation-callable.php \
+ --exclude tests/PHPStan/Rules/Classes/data/instantiation-callable.php \
+ src tests
cs:
composer install --working-dir build-cs && php build-cs/vendor/bin/phpcs
diff --git a/README.md b/README.md
index 6692591ca7..b64d557a42 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
# PHPStan - PHP Static Analysis Tool
-[](https://github.com/phpstan/phpstan-src/actions)
-[](https://coveralls.io/github/phpstan/phpstan-src)
+[](https://github.com/phpstan/phpstan-src/actions)
[](https://github.com/phpstan/phpstan)
---
@@ -14,9 +13,9 @@ Any contributions are welcome.
### Building
-PHPStan's source code is developed on PHP 7.4. For distribution in `phpstan/phpstan` package and as a PHAR file, the source code is transformed to run on PHP 7.1 and higher.
+PHPStan's source code is developed on PHP 8.0. For distribution in `phpstan/phpstan` package and as a PHAR file, the source code is transformed to run on PHP 7.1 and higher.
-Initially you need to run `composer install`, or `composer update` in case you aren't working in a directory which was built before.
+Initially you need to run `composer install` in case you aren't working in a directory which was built before.
Afterwards you can either run the whole build including linting and coding standards using
@@ -40,8 +39,6 @@ To detect code style issues, run:
make cs
```
-This requires PHP 7.4. On older versions the build target will be skipped and succeed silently.
-
And then to fix code style, run:
```bash
diff --git a/bin/functionMetadata_original.php b/bin/functionMetadata_original.php
index b7505a6f6f..8004c6dcda 100644
--- a/bin/functionMetadata_original.php
+++ b/bin/functionMetadata_original.php
@@ -61,8 +61,46 @@
'bcmod' => ['hasSideEffects' => false],
'bcmul' => ['hasSideEffects' => false],
// continue functionMap.php, line 424
+ 'chgrp' => ['hasSideEffects' => true],
+ 'chmod' => ['hasSideEffects' => true],
+ 'chown' => ['hasSideEffects' => true],
+ 'copy' => ['hasSideEffects' => true],
'count' => ['hasSideEffects' => false],
+ 'fclose' => ['hasSideEffects' => true],
+ 'fflush' => ['hasSideEffects' => true],
+ 'fgetc' => ['hasSideEffects' => true],
+ 'fgetcsv' => ['hasSideEffects' => true],
+ 'fgets' => ['hasSideEffects' => true],
+ 'fgetss' => ['hasSideEffects' => true],
+ 'file_put_contents' => ['hasSideEffects' => true],
+ 'flock' => ['hasSideEffects' => true],
+ 'fopen' => ['hasSideEffects' => true],
+ 'fpassthru' => ['hasSideEffects' => true],
+ 'fputcsv' => ['hasSideEffects' => true],
+ 'fputs' => ['hasSideEffects' => true],
+ 'fread' => ['hasSideEffects' => true],
+ 'fscanf' => ['hasSideEffects' => true],
+ 'fseek' => ['hasSideEffects' => true],
+ 'ftruncate' => ['hasSideEffects' => true],
+ 'fwrite' => ['hasSideEffects' => true],
+ 'lchgrp' => ['hasSideEffects' => true],
+ 'lchown' => ['hasSideEffects' => true],
+ 'link' => ['hasSideEffects' => true],
+ 'mkdir' => ['hasSideEffects' => true],
+ 'move_uploaded_file' => ['hasSideEffects' => true],
+ 'pclose' => ['hasSideEffects' => true],
+ 'popen' => ['hasSideEffects' => true],
+ 'readfile' => ['hasSideEffects' => true],
+ 'rename' => ['hasSideEffects' => true],
+ 'rewind' => ['hasSideEffects' => true],
+ 'rmdir' => ['hasSideEffects' => true],
'sprintf' => ['hasSideEffects' => false],
+ 'symlink' => ['hasSideEffects' => true],
+ 'tempnam' => ['hasSideEffects' => true],
+ 'tmpfile' => ['hasSideEffects' => true],
+ 'touch' => ['hasSideEffects' => true],
+ 'umask' => ['hasSideEffects' => true],
+ 'unlink' => ['hasSideEffects' => true],
// random functions, do not have side effects but are not deterministic
'mt_rand' => ['hasSideEffects' => true],
diff --git a/bin/generate-function-metadata.php b/bin/generate-function-metadata.php
index a7e4ebd35a..a3779dda53 100755
--- a/bin/generate-function-metadata.php
+++ b/bin/generate-function-metadata.php
@@ -29,7 +29,7 @@ public function enterNode(Node $node)
foreach ($attrGroup->attrs as $attr) {
if ($attr->name->toString() === \JetBrains\PhpStorm\Pure::class) {
$this->functions[] = $node->namespacedName->toLowerString();
- break;
+ break 2;
}
}
}
@@ -45,7 +45,7 @@ public function enterNode(Node $node)
foreach ($attrGroup->attrs as $attr) {
if ($attr->name->toString() === \JetBrains\PhpStorm\Pure::class) {
$this->methods[] = sprintf('%s::%s', $className, $node->name->toString());
- break;
+ break 2;
}
}
}
@@ -71,6 +71,14 @@ public function enterNode(Node $node)
foreach ($visitor->functions as $functionName) {
if (array_key_exists($functionName, $metadata)) {
if ($metadata[$functionName]['hasSideEffects']) {
+ if (in_array($functionName, [
+ 'mt_rand',
+ 'rand',
+ 'random_bytes',
+ 'random_int',
+ ], true)) {
+ continue;
+ }
throw new \PHPStan\ShouldNotHappenException($functionName);
}
}
diff --git a/bin/phpstan b/bin/phpstan
index de25ec2c0a..2c06a78841 100755
--- a/bin/phpstan
+++ b/bin/phpstan
@@ -4,7 +4,6 @@
use PHPStan\Command\AnalyseCommand;
use PHPStan\Command\ClearResultCacheCommand;
use PHPStan\Command\FixerWorkerCommand;
-use PHPStan\Command\DumpDependenciesCommand;
use PHPStan\Command\WorkerCommand;
use Symfony\Component\Console\Helper\ProgressBar;
@@ -28,6 +27,13 @@ use Symfony\Component\Console\Helper\ProgressBar;
if (
!array_key_exists('e88992873b7765f9b5710cab95ba5dd7', $composerAutoloadFiles)
|| !array_key_exists('3e76f7f02b41af8cea96018933f6b7e3', $composerAutoloadFiles)
+ || !array_key_exists('a4a119a56e50fbb293281d9a48007e0e', $composerAutoloadFiles)
+ || !array_key_exists('0e6d7bf4a5811bfa5cf40c5ccd6fae6a', $composerAutoloadFiles)
+ || !array_key_exists('e69f7f6ee287b969198c3c9d6777bd38', $composerAutoloadFiles)
+ || !array_key_exists('0d59ee240a4cd96ddbb4ff164fccea4d', $composerAutoloadFiles)
+ || !array_key_exists('b686b8e46447868025a15ce5d0cb2634', $composerAutoloadFiles)
+ || !array_key_exists('25072dd6e2470089de65ae7bf11d3109', $composerAutoloadFiles)
+ || !array_key_exists('8825ede83f2f289127722d4e842cf7e8', $composerAutoloadFiles)
) {
echo "Composer autoloader changed\n";
exit(1);
@@ -38,6 +44,27 @@ use Symfony\Component\Console\Helper\ProgressBar;
// fix unprefixed Hoa namespace - files already loaded
'e88992873b7765f9b5710cab95ba5dd7' => true,
'3e76f7f02b41af8cea96018933f6b7e3' => true,
+
+ // vendor/symfony/polyfill-php80/bootstrap.php
+ 'a4a119a56e50fbb293281d9a48007e0e' => true,
+
+ // vendor/symfony/polyfill-mbstring/bootstrap.php
+ '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => true,
+
+ // vendor/symfony/polyfill-intl-normalizer/bootstrap.php
+ 'e69f7f6ee287b969198c3c9d6777bd38' => true,
+
+ // vendor/symfony/polyfill-php73/bootstrap.php
+ '0d59ee240a4cd96ddbb4ff164fccea4d' => true,
+
+ // vendor/symfony/polyfill-php74/bootstrap.php
+ 'b686b8e46447868025a15ce5d0cb2634' => true,
+
+ // vendor/symfony/polyfill-php72/bootstrap.php
+ '25072dd6e2470089de65ae7bf11d3109' => true,
+
+ // vendor/symfony/polyfill-intl-grapheme/bootstrap.php
+ '8825ede83f2f289127722d4e842cf7e8' => true,
];
$autoloaderInWorkingDirectory = getcwd() . '/vendor/autoload.php';
@@ -81,7 +108,7 @@ use Symfony\Component\Console\Helper\ProgressBar;
$version = 'Version unknown';
try {
- $version = \Jean85\PrettyVersions::getVersion('phpstan/phpstan')->getPrettyVersion();
+ $version = \Jean85\PrettyVersions::getVersion('phpstan/phpstan')->getPrettyVersion() ?: $version;
} catch (\OutOfBoundsException $e) {
}
@@ -95,7 +122,6 @@ use Symfony\Component\Console\Helper\ProgressBar;
$reversedComposerAutoloaderProjectPaths = array_reverse($composerAutoloaderProjectPaths);
$application->add(new AnalyseCommand($reversedComposerAutoloaderProjectPaths));
- $application->add(new DumpDependenciesCommand($reversedComposerAutoloaderProjectPaths));
$application->add(new WorkerCommand($reversedComposerAutoloaderProjectPaths));
$application->add(new ClearResultCacheCommand($reversedComposerAutoloaderProjectPaths));
$application->add(new FixerWorkerCommand($reversedComposerAutoloaderProjectPaths));
diff --git a/bin/transform-source.php b/bin/transform-source.php
deleted file mode 100755
index 8ecbefb023..0000000000
--- a/bin/transform-source.php
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/env php
-type === null) {
- return null;
- }
- $docComment = $node->getDocComment();
- if ($docComment !== null) {
- $node->type = null;
- return $node;
- }
-
- $node->setDocComment(new \PhpParser\Comment\Doc(sprintf('/** @var %s */', $this->printType($node->type))));
- $node->type = null;
-
- return $node;
- }
-
- /**
- * @param Identifier|Name|NullableType|UnionType $type
- * @return string
- */
- private function printType($type): string
- {
- if ($type instanceof NullableType) {
- return $this->printType($type->type) . '|null';
- }
-
- if ($type instanceof UnionType) {
- throw new \Exception('UnionType not yet supported');
- }
-
- if ($type instanceof Name) {
- $name = $type->toString();
- if ($type->isFullyQualified()) {
- return '\\' . $name;
- }
-
- return $name;
- }
-
- if ($type instanceof Identifier) {
- return $type->name;
- }
-
- throw new \Exception('Unsupported type class');
- }
-
-}
-
-(function () {
- $dir = __DIR__ . '/../src';
-
- $lexer = new Lexer\Emulative([
- 'usedAttributes' => [
- 'comments',
- 'startLine', 'endLine',
- 'startTokenPos', 'endTokenPos',
- ],
- ]);
- $parser = new Parser\Php7($lexer, [
- 'useIdentifierNodes' => true,
- 'useConsistentVariableNodes' => true,
- 'useExpressionStatements' => true,
- 'useNopStatements' => false,
- ]);
- $nameResolver = new NodeVisitor\NameResolver(null, [
- 'replaceNodes' => false
- ]);
-
- $printer = new PrettyPrinter\Standard();
-
- $traverser = new NodeTraverser();
- $traverser->addVisitor(new NodeVisitor\CloningVisitor());
- $traverser->addVisitor($nameResolver);
- $traverser->addVisitor(new PhpPatcher($printer));
-
- $it = new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator($dir),
- RecursiveIteratorIterator::LEAVES_ONLY
- );
- foreach ($it as $file) {
- $fileName = $file->getPathname();
- if (!preg_match('/\.php$/', $fileName)) {
- continue;
- }
-
- $code = \PHPStan\File\FileReader::read($fileName);
- $origStmts = $parser->parse($code);
- $newCode = $printer->printFormatPreserving(
- $traverser->traverse($origStmts),
- $origStmts,
- $lexer->getTokens()
- );
-
- \PHPStan\File\FileWriter::write($fileName, $newCode);
- }
-})();
diff --git a/build-cs/composer.json b/build-cs/composer.json
index 562c29c0f4..7912119afa 100644
--- a/build-cs/composer.json
+++ b/build-cs/composer.json
@@ -2,7 +2,12 @@
"require-dev": {
"consistence-community/coding-standard": "^3.11.0",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
- "slevomat/coding-standard": "^6.3.0",
+ "slevomat/coding-standard": "^7.0.18",
"squizlabs/php_codesniffer": "^3.5.3"
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
}
}
diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 19ea198bf0..475b81fba0 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -4,35 +4,35 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "2bd0f4453d373af44aff8ec8868f82e9",
+ "content-hash": "98d65cadfe6f694505bce09b80004cae",
"packages": [],
"packages-dev": [
{
"name": "consistence-community/coding-standard",
- "version": "3.11.0",
+ "version": "3.11.1",
"source": {
"type": "git",
"url": "https://github.com/consistence-community/coding-standard.git",
- "reference": "20f5c3673013be606a62ba0b6624f5c0e43bb64e"
+ "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/20f5c3673013be606a62ba0b6624f5c0e43bb64e",
- "reference": "20f5c3673013be606a62ba0b6624f5c0e43bb64e",
+ "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/4632fead8c9ee8f50044fcbce9f66c797b34c0df",
+ "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df",
"shasum": ""
},
"require": {
"php": ">=7.4",
- "slevomat/coding-standard": "~6.4",
- "squizlabs/php_codesniffer": "~3.5.8"
+ "slevomat/coding-standard": "~7.0",
+ "squizlabs/php_codesniffer": "~3.6.0"
},
"replace": {
"consistence/coding-standard": "3.10.*"
},
"require-dev": {
"phing/phing": "2.16.4",
- "php-parallel-lint/php-parallel-lint": "1.2.0",
- "phpunit/phpunit": "9.5.2"
+ "php-parallel-lint/php-parallel-lint": "1.3.0",
+ "phpunit/phpunit": "9.5.4"
},
"type": "library",
"autoload": {
@@ -69,33 +69,34 @@
"standard"
],
"support": {
- "source": "https://github.com/consistence-community/coding-standard/tree/3.11.0"
+ "issues": "https://github.com/consistence-community/coding-standard/issues",
+ "source": "https://github.com/consistence-community/coding-standard/tree/3.11.1"
},
- "time": "2021-02-28T08:38:12+00:00"
+ "time": "2021-05-03T18:13:22+00:00"
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
- "version": "v0.7.1",
+ "version": "v0.7.2",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
- "reference": "fe390591e0241955f22eb9ba327d137e501c771c"
+ "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/fe390591e0241955f22eb9ba327d137e501c771c",
- "reference": "fe390591e0241955f22eb9ba327d137e501c771c",
+ "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
+ "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.3",
- "squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0"
+ "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
- "phpcompatibility/php-compatibility": "^9.0",
- "sensiolabs/security-checker": "^4.1.0"
+ "php-parallel-lint/php-parallel-lint": "^1.3.1",
+ "phpcompatibility/php-compatibility": "^9.0"
},
"type": "composer-plugin",
"extra": {
@@ -116,6 +117,10 @@
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
@@ -127,6 +132,7 @@
"codesniffer",
"composer",
"installer",
+ "phpcbf",
"phpcs",
"plugin",
"qa",
@@ -141,41 +147,37 @@
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
},
- "time": "2020-12-07T18:04:37+00:00"
+ "time": "2022-02-04T12:51:07+00:00"
},
{
"name": "phpstan/phpdoc-parser",
- "version": "0.4.9",
+ "version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "98a088b17966bdf6ee25c8a4b634df313d8aa531"
+ "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/98a088b17966bdf6ee25c8a4b634df313d8aa531",
- "reference": "98a088b17966bdf6ee25c8a4b634df313d8aa531",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/dbc093d7af60eff5cd575d2ed761b15ed40bd08e",
+ "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
- "consistence/coding-standard": "^3.5",
- "ergebnis/composer-normalize": "^2.0.2",
- "jakub-onderka/php-parallel-lint": "^0.9.2",
- "phing/phing": "^2.16.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12.26",
- "phpstan/phpstan-strict-rules": "^0.12",
- "phpunit/phpunit": "^6.3",
- "slevomat/coding-standard": "^4.7.2",
- "symfony/process": "^4.0"
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.0",
+ "phpunit/phpunit": "^9.5",
+ "symfony/process": "^5.2"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "0.4-dev"
+ "dev-master": "1.0-dev"
}
},
"autoload": {
@@ -192,43 +194,43 @@
"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/master"
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/1.2.0"
},
- "time": "2020-08-03T20:32:43+00:00"
+ "time": "2021-09-16T20:46:02+00:00"
},
{
"name": "slevomat/coding-standard",
- "version": "6.4.1",
+ "version": "7.0.19",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
- "reference": "696dcca217d0c9da2c40d02731526c1e25b65346"
+ "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/696dcca217d0c9da2c40d02731526c1e25b65346",
- "reference": "696dcca217d0c9da2c40d02731526c1e25b65346",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37",
+ "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
"php": "^7.1 || ^8.0",
- "phpstan/phpdoc-parser": "0.4.5 - 0.4.9",
- "squizlabs/php_codesniffer": "^3.5.6"
+ "phpstan/phpdoc-parser": "^1.0.0",
+ "squizlabs/php_codesniffer": "^3.6.2"
},
"require-dev": {
- "phing/phing": "2.16.3",
- "php-parallel-lint/php-parallel-lint": "1.2.0",
- "phpstan/phpstan": "0.12.48",
- "phpstan/phpstan-deprecation-rules": "0.12.5",
- "phpstan/phpstan-phpunit": "0.12.16",
- "phpstan/phpstan-strict-rules": "0.12.5",
- "phpunit/phpunit": "7.5.20|8.5.5|9.4.0"
+ "phing/phing": "2.17.2",
+ "php-parallel-lint/php-parallel-lint": "1.3.2",
+ "phpstan/phpstan": "1.4.6",
+ "phpstan/phpstan-deprecation-rules": "1.0.0",
+ "phpstan/phpstan-phpunit": "1.0.0",
+ "phpstan/phpstan-strict-rules": "1.1.0",
+ "phpunit/phpunit": "7.5.20|8.5.21|9.5.16"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
- "dev-master": "6.x-dev"
+ "dev-master": "7.x-dev"
}
},
"autoload": {
@@ -243,7 +245,7 @@
"description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
"support": {
"issues": "https://github.com/slevomat/coding-standard/issues",
- "source": "https://github.com/slevomat/coding-standard/tree/6.4.1"
+ "source": "https://github.com/slevomat/coding-standard/tree/7.0.19"
},
"funding": [
{
@@ -255,20 +257,20 @@
"type": "tidelift"
}
],
- "time": "2020-10-05T12:39:37+00:00"
+ "time": "2022-03-01T18:01:41+00:00"
},
{
"name": "squizlabs/php_codesniffer",
- "version": "3.5.8",
+ "version": "3.6.2",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
- "reference": "9d583721a7157ee997f235f327de038e7ea6dac4"
+ "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4",
- "reference": "9d583721a7157ee997f235f327de038e7ea6dac4",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a",
+ "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a",
"shasum": ""
},
"require": {
@@ -311,7 +313,7 @@
"source": "https://github.com/squizlabs/PHP_CodeSniffer",
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
},
- "time": "2020-10-23T02:01:07+00:00"
+ "time": "2021-12-12T21:44:58+00:00"
}
],
"aliases": [],
@@ -321,5 +323,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
- "plugin-api-version": "2.0.0"
+ "plugin-api-version": "2.2.0"
}
diff --git a/build/PHPStan/Build/ServiceLocatorDynamicReturnTypeExtension.php b/build/PHPStan/Build/ServiceLocatorDynamicReturnTypeExtension.php
index 8ad0e108c1..da67c961e6 100644
--- a/build/PHPStan/Build/ServiceLocatorDynamicReturnTypeExtension.php
+++ b/build/PHPStan/Build/ServiceLocatorDynamicReturnTypeExtension.php
@@ -7,8 +7,6 @@
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\Constant\ConstantBooleanType;
-use PHPStan\Type\Constant\ConstantStringType;
-use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
@@ -30,23 +28,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
- if (count($methodCall->args) === 0) {
- return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
- }
- $argType = $scope->getType($methodCall->args[0]->value);
- if (!$argType instanceof ConstantStringType) {
- return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+ if (count($methodCall->getArgs()) === 0) {
+ return ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
}
- $type = new ObjectType($argType->getValue());
- if ($methodReflection->getName() === 'getByType' && count($methodCall->args) >= 2) {
- $argType = $scope->getType($methodCall->args[1]->value);
+ $returnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
+
+ if ($methodReflection->getName() === 'getByType' && count($methodCall->getArgs()) >= 2) {
+ $argType = $scope->getType($methodCall->getArgs()[1]->value);
if ($argType instanceof ConstantBooleanType && $argType->getValue()) {
- $type = TypeCombinator::addNull($type);
+ $returnType = TypeCombinator::addNull($returnType);
}
}
- return $type;
+ return $returnType;
}
}
diff --git a/build/baseline-32bit.neon b/build/baseline-32bit.neon
new file mode 100644
index 0000000000..82adbae209
--- /dev/null
+++ b/build/baseline-32bit.neon
@@ -0,0 +1,6 @@
+parameters:
+ ignoreErrors:
+ -
+ message: "#^Parameter \\#1 \\$value of class PHPStan\\\\Type\\\\Constant\\\\ConstantIntegerType constructor expects int, float given\\.$#"
+ count: 2
+ path: ../src/Analyser/MutatingScope.php
diff --git a/build/baseline-7.4.neon b/build/baseline-7.4.neon
index 0d3f438771..f1efeccdf6 100644
--- a/build/baseline-7.4.neon
+++ b/build/baseline-7.4.neon
@@ -8,20 +8,6 @@ parameters:
message: "#^Class PHPStan\\\\DependencyInjection\\\\Reflection\\\\DirectClassReflectionExtensionRegistryProvider has an uninitialized property \\$broker\\. Give it default value or assign it in the constructor\\.$#"
count: 1
path: ../src/DependencyInjection/Reflection/DirectClassReflectionExtensionRegistryProvider.php
- -
- message: "#^Class PHPStan\\\\DependencyInjection\\\\Type\\\\DirectDynamicReturnTypeExtensionRegistryProvider has an uninitialized property \\$broker\\. Give it default value or assign it in the constructor\\.$#"
- count: 1
- path: ../src/DependencyInjection/Type/DirectDynamicReturnTypeExtensionRegistryProvider.php
-
- -
- message: "#^Class PHPStan\\\\DependencyInjection\\\\Type\\\\DirectDynamicReturnTypeExtensionRegistryProvider has an uninitialized property \\$reflectionProvider\\. Give it default value or assign it in the constructor\\.$#"
- count: 1
- path: ../src/DependencyInjection/Type/DirectDynamicReturnTypeExtensionRegistryProvider.php
-
- -
- message: "#^Class PHPStan\\\\DependencyInjection\\\\Type\\\\DirectOperatorTypeSpecifyingExtensionRegistryProvider has an uninitialized property \\$broker\\. Give it default value or assign it in the constructor\\.$#"
- count: 1
- path: ../src/DependencyInjection/Type/DirectOperatorTypeSpecifyingExtensionRegistryProvider.php
-
message: "#^Class PHPStan\\\\Parallel\\\\ParallelAnalyser has an uninitialized property \\$processPool\\. Give it default value or assign it in the constructor\\.$#"
@@ -74,6 +60,11 @@ parameters:
count: 1
path: ../src/Reflection/BetterReflection/SourceLocator/CachingVisitor.php
+ -
+ message: "#^Class PHPStan\\\\Reflection\\\\BetterReflection\\\\SourceLocator\\\\CachingVisitor has an uninitialized property \\$contents\\. Give it default value or assign it in the constructor\\.$#"
+ count: 1
+ path: ../src/Reflection/BetterReflection/SourceLocator/CachingVisitor.php
+
-
message: "#^Class PHPStan\\\\Reflection\\\\BetterReflection\\\\SourceLocator\\\\CachingVisitor has an uninitialized property \\$classNodes\\. Give it default value or assign it in the constructor\\.$#"
count: 1
diff --git a/build/baseline-8.0.neon b/build/baseline-8.0.neon
index 17ff4b0ca8..7d6f91d1c8 100644
--- a/build/baseline-8.0.neon
+++ b/build/baseline-8.0.neon
@@ -11,7 +11,7 @@ parameters:
path: ../src/Type/Php/MbFunctionsReturnTypeExtension.php
-
- message: "#^Call to function is_array\\(\\) with array\\ will always evaluate to true\\.$#"
+ message: "#^Strict comparison using \\=\\=\\= between non-empty-array and false will always evaluate to false\\.$#"
count: 1
path: ../src/Type/Php/StrSplitFunctionReturnTypeExtension.php
diff --git a/build/baseline-8.1.neon b/build/baseline-8.1.neon
new file mode 100644
index 0000000000..fdd42f9d3d
--- /dev/null
+++ b/build/baseline-8.1.neon
@@ -0,0 +1,22 @@
+parameters:
+ ignoreErrors:
+ -
+ message: "#^Call to function method_exists\\(\\) with ReflectionClassConstant and 'isFinal' will always evaluate to true\\.$#"
+ count: 1
+ path: ../src/Reflection/ClassConstantReflection.php
+
+ -
+ message: "#^Call to function method_exists\\(\\) with ReflectionClass and 'isEnum' will always evaluate to true\\.$#"
+ count: 1
+ path: ../src/Reflection/ClassReflection.php
+
+ -
+ message: "#^Call to function method_exists\\(\\) with ReflectionMethod and 'getTentativeReturnT…' will always evaluate to true\\.$#"
+ count: 1
+ path: ../src/Reflection/Php/NativeBuiltinMethodReflection.php
+
+ -
+ message: "#^Call to function method_exists\\(\\) with ReflectionProperty and 'isReadOnly' will always evaluate to true\\.$#"
+ count: 1
+ path: ../src/Reflection/Php/PhpPropertyReflection.php
+
diff --git a/build/composer-require-checker.json b/build/composer-require-checker.json
index 45fb391f8d..75cb6d099e 100644
--- a/build/composer-require-checker.json
+++ b/build/composer-require-checker.json
@@ -3,13 +3,13 @@
"null", "true", "false",
"static", "self", "parent",
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object",
- "PHPUnit\\Framework\\TestCase", "PHPUnit\\Framework\\AssertionFailedError", "Composer\\Autoload\\ClassLoader",
+ "PHPUnit\\Framework\\TestCase", "PHPUnit\\Framework\\AssertionFailedError", "Composer\\Autoload\\ClassLoader", "PHPUnit\\Framework\\ExpectationFailedException",
"JSON_THROW_ON_ERROR", "JSON_INVALID_UTF8_IGNORE", "JsonSerializable", "SimpleXMLElement", "PHPStan\\ExtensionInstaller\\GeneratedConfig", "Nette\\DI\\InvalidConfigurationException",
"FILTER_SANITIZE_EMAIL", "FILTER_SANITIZE_EMAIL", "FILTER_SANITIZE_ENCODED", "FILTER_SANITIZE_MAGIC_QUOTES", "FILTER_SANITIZE_NUMBER_FLOAT",
"FILTER_SANITIZE_NUMBER_INT", "FILTER_SANITIZE_SPECIAL_CHARS", "FILTER_SANITIZE_STRING", "FILTER_SANITIZE_URL", "FILTER_VALIDATE_BOOLEAN",
"FILTER_VALIDATE_EMAIL", "FILTER_VALIDATE_FLOAT", "FILTER_VALIDATE_INT", "FILTER_VALIDATE_IP", "FILTER_VALIDATE_MAC", "FILTER_VALIDATE_REGEXP",
- "FILTER_VALIDATE_URL", "FILTER_NULL_ON_FAILURE", "FILTER_FORCE_ARRAY", "FILTER_SANITIZE_ADD_SLASHES", "FILTER_DEFAULT", "FILTER_UNSAFE_RAW", "opcache_invalidate", "ValueError", "ReflectionUnionType", "Attribute",
- "Clue\\React\\Block\\await"
+ "FILTER_VALIDATE_URL", "FILTER_NULL_ON_FAILURE", "FILTER_FORCE_ARRAY", "FILTER_SANITIZE_ADD_SLASHES", "FILTER_DEFAULT", "FILTER_UNSAFE_RAW", "opcache_invalidate", "ValueError", "ReflectionUnionType", "ReflectionIntersectionType", "Attribute", "ReflectionEnum", "ReflectionEnumBackedCase", "enum_exists",
+ "Clue\\React\\Block\\await", "Hoa\\File\\Read"
],
"php-core-extensions" : [
"Core",
diff --git a/build/enum-adapter-errors.neon b/build/enum-adapter-errors.neon
new file mode 100644
index 0000000000..7516959bee
--- /dev/null
+++ b/build/enum-adapter-errors.neon
@@ -0,0 +1,42 @@
+parameters:
+ ignoreErrors:
+ -
+ message: "#^Call to method getBackingValue\\(\\) on an unknown class ReflectionEnumBackedCase\\.$#"
+ count: 2
+ path: ../src/Reflection/ClassReflection.php
+
+ -
+ message: "#^Call to method getCase\\(\\) on an unknown class ReflectionEnum\\.$#"
+ count: 1
+ path: ../src/Reflection/ClassReflection.php
+
+ -
+ message: "#^Call to method getCases\\(\\) on an unknown class ReflectionEnum\\.$#"
+ count: 1
+ path: ../src/Reflection/ClassReflection.php
+
+ -
+ message: "#^Call to method isBacked\\(\\) on an unknown class ReflectionEnum\\.$#"
+ count: 2
+ path: ../src/Reflection/ClassReflection.php
+
+ -
+ message: "#^Call to method getBackingType\\(\\) on an unknown class ReflectionEnum\\.$#"
+ count: 1
+ path: ../src/Reflection/ClassReflection.php
+
+ -
+ message: "#^Class ReflectionEnum not found\\.$#"
+ count: 4
+ path: ../src/Reflection/ClassReflection.php
+
+ -
+ message: "#^Class ReflectionEnumBackedCase not found\\.$#"
+ count: 2
+ path: ../src/Reflection/ClassReflection.php
+
+ -
+ message: "#^Class ReflectionEnum not found\\.$#"
+ count: 1
+ path: ../tests/PHPStan/Reflection/ClassReflectionTest.php
+
diff --git a/build/enums.neon b/build/enums.neon
new file mode 100644
index 0000000000..6c61194dd9
--- /dev/null
+++ b/build/enums.neon
@@ -0,0 +1,4 @@
+parameters:
+ excludePaths:
+ - ../tests/PHPStan/Fixture/TestEnum.php
+ - ../tests/PHPStan/Fixture/AnotherTestEnum.php
diff --git a/build/ignore-by-architecture.neon.php b/build/ignore-by-architecture.neon.php
new file mode 100644
index 0000000000..a6c86b46cf
--- /dev/null
+++ b/build/ignore-by-architecture.neon.php
@@ -0,0 +1,11 @@
+load(__DIR__ . '/baseline-32bit.neon');
+}
+
+return [];
diff --git a/build/ignore-by-php-version.neon.php b/build/ignore-by-php-version.neon.php
index 44c106ee82..518cc5a40a 100644
--- a/build/ignore-by-php-version.neon.php
+++ b/build/ignore-by-php-version.neon.php
@@ -1,21 +1,30 @@
load(__DIR__ . '/baseline-lt-7.3.neon'));
+ $includes[] = __DIR__ . '/baseline-lt-7.3.neon';
} else {
- $config = array_merge_recursive($config, $adapter->load(__DIR__ . '/baseline-7.3.neon'));
+ $includes[] = __DIR__ . '/baseline-7.3.neon';
}
if (PHP_VERSION_ID >= 80000) {
- $config = array_merge_recursive($config, $adapter->load(__DIR__ . '/baseline-8.0.neon'));
+ $includes[] = __DIR__ . '/baseline-8.0.neon';
+}
+if (PHP_VERSION_ID >= 80100) {
+ $includes[] = __DIR__ . '/baseline-8.1.neon';
+} else {
+ $includes[] = __DIR__ . '/enums.neon';
}
if (PHP_VERSION_ID >= 70400) {
- $config = array_merge_recursive($config, $adapter->load(__DIR__ . '/ignore-gte-php7.4-errors.neon'));
+ $includes[] = __DIR__ . '/ignore-gte-php7.4-errors.neon';
}
+if (PHP_VERSION_ID < 80000) {
+ $includes[] = __DIR__ . '/enum-adapter-errors.neon';
+}
+
+$config = [];
+$config['includes'] = $includes;
+$config['parameters']['phpVersion'] = PHP_VERSION_ID;
+
return $config;
diff --git a/build/ignore-gte-php7.4-errors.neon b/build/ignore-gte-php7.4-errors.neon
index cae6484551..54b5cb905d 100644
--- a/build/ignore-gte-php7.4-errors.neon
+++ b/build/ignore-gte-php7.4-errors.neon
@@ -8,3 +8,6 @@ parameters:
path: ../src/Reflection/Php/PhpClassReflectionExtension.php
- '#^Class PHPStan\\Rules\\RuleErrors\\RuleError(?:\d+) has an uninitialized property (?:\$message|\$line|\$identifier|\$tip|\$file|\$metadata)#'
- '#Extension has an uninitialized property (?:\$typeSpecifier|\$broker)#'
+ -
+ message: '#has an uninitialized property#'
+ path: ../tests
diff --git a/build/phpstan.neon b/build/phpstan.neon
index 2ae97ea454..aea35d06cc 100644
--- a/build/phpstan.neon
+++ b/build/phpstan.neon
@@ -8,6 +8,7 @@ includes:
- ../conf/bleedingEdge.neon
- ../phpstan-baseline.neon
- ignore-by-php-version.neon.php
+ - ignore-by-architecture.neon.php
parameters:
level: 8
paths:
@@ -18,7 +19,7 @@ parameters:
- ../tests/phpstan-bootstrap.php
checkUninitializedProperties: true
checkMissingCallableSignature: true
- excludes_analyse:
+ excludePaths:
- ../src/Reflection/SignatureMap/functionMap.php
- ../src/Reflection/SignatureMap/functionMetadata.php
- ../tests/*/data/*
@@ -65,9 +66,6 @@ parameters:
check:
missingCheckedExceptionInThrows: true
tooWideThrowType: true
- featureToggles:
- readComposerPhpVersion: false
- apiRules: false
ignoreErrors:
- '#^Dynamic call to static method PHPUnit\\Framework\\\S+\(\)\.$#'
- '#should be contravariant with parameter \$node \(PhpParser\\Node\) of method PHPStan\\Rules\\Rule::processNode\(\)$#'
@@ -91,6 +89,7 @@ parameters:
stubFiles:
- stubs/ReactChildProcess.stub
- stubs/ReactStreams.stub
+ - stubs/NetteDIContainer.stub
services:
-
class: PHPStan\Build\ServiceLocatorDynamicReturnTypeExtension
diff --git a/build/rector-downgrade-vendor.php b/build/rector-downgrade-vendor.php
new file mode 100644
index 0000000000..ac77234538
--- /dev/null
+++ b/build/rector-downgrade-vendor.php
@@ -0,0 +1,28 @@
+parameters();
+ $parameters->set(Option::PHP_VERSION_FEATURES, $targetPhpVersionId);
+
+ $services = $containerConfigurator->services();
+
+ if ($targetPhpVersionId < 70200) {
+ $services->set(DowngradeObjectTypeDeclarationRector::class);
+ $services->set(DowngradePregUnmatchedAsNullConstantRector::class);
+ $services->set(DowngradeStreamIsattyRector::class);
+ }
+};
diff --git a/build/rector-downgrade.php b/build/rector-downgrade.php
new file mode 100644
index 0000000000..afdb07beef
--- /dev/null
+++ b/build/rector-downgrade.php
@@ -0,0 +1,63 @@
+parameters();
+
+ $parameters->set(Option::PHP_VERSION_FEATURES, $targetPhpVersionId);
+ $parameters->set(Option::SKIP, [
+ 'tests/*/data/*',
+ 'tests/*/Fixture/*',
+ 'tests/PHPStan/Analyser/traits/*',
+ 'tests/PHPStan/Generics/functions.php',
+ 'tests/e2e/resultCache_1.php',
+ 'tests/e2e/resultCache_2.php',
+ 'tests/e2e/resultCache_3.php',
+ ]);
+
+ $services = $containerConfigurator->services();
+
+ if ($targetPhpVersionId < 80000) {
+ $services->set(DowngradeTrailingCommasInParamUseRector::class);
+ $services->set(DowngradeNonCapturingCatchesRector::class);
+ $services->set(DowngradeUnionTypeTypedPropertyRector::class);
+ $services->set(DowngradePropertyPromotionRector::class);
+ $services->set(DowngradeUnionTypeDeclarationRector::class);
+ }
+
+ if ($targetPhpVersionId < 70400) {
+ $services->set(DowngradeTypedPropertyRector::class);
+ $services->set(DowngradeNullCoalescingOperatorRector::class);
+ $services->set(ArrowFunctionToAnonymousFunctionRector::class);
+ }
+
+ if ($targetPhpVersionId < 70300) {
+ $services->set(DowngradeTrailingCommasInFunctionCallsRector::class);
+ }
+
+ if ($targetPhpVersionId < 70200) {
+ $services->set(DowngradeObjectTypeDeclarationRector::class);
+ }
+};
diff --git a/build/stubs/NetteDIContainer.stub b/build/stubs/NetteDIContainer.stub
new file mode 100644
index 0000000000..455eb43455
--- /dev/null
+++ b/build/stubs/NetteDIContainer.stub
@@ -0,0 +1,15 @@
+ $type
+ * @return T
+ */
+ public function getByType(string $type);
+
+}
diff --git a/build/transform-source b/build/transform-source
new file mode 100755
index 0000000000..71773edfbe
--- /dev/null
+++ b/build/transform-source
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o pipefail
+set -o nounset
+
+export TARGET_PHP_VERSION=$1
+
+vendor/bin/rector process src tests/PHPStan tests/e2e -c build/rector-downgrade.php --no-diffs
+
+vendor/bin/rector process vendor/symfony vendor/nette -c build/rector-downgrade-vendor.php --no-diffs || true
diff --git a/compiler/build/box.phar b/compiler/build/box.phar
index 76da031be9..d0476cfc4c 100644
Binary files a/compiler/build/box.phar and b/compiler/build/box.phar differ
diff --git a/compiler/build/scoper.inc.php b/compiler/build/scoper.inc.php
index f2e2ae8151..c2b758dab3 100644
--- a/compiler/build/scoper.inc.php
+++ b/compiler/build/scoper.inc.php
@@ -16,6 +16,13 @@
'../../stubs',
'../../vendor/jetbrains/phpstorm-stubs',
'../../vendor/phpstan/php-8-stubs/stubs',
+ '../../vendor/symfony/polyfill-php80',
+ '../../vendor/symfony/polyfill-mbstring',
+ '../../vendor/symfony/polyfill-intl-normalizer',
+ '../../vendor/symfony/polyfill-php73',
+ '../../vendor/symfony/polyfill-php74',
+ '../../vendor/symfony/polyfill-php72',
+ '../../vendor/symfony/polyfill-intl-grapheme',
]) as $file) {
if ($file->getPathName() === '../../vendor/jetbrains/phpstorm-stubs/PhpStormStubsMap.php') {
continue;
@@ -23,8 +30,13 @@
$stubs[] = $file->getPathName();
}
+exec('git rev-parse --short HEAD', $gitCommitOutputLines, $gitExitCode);
+if ($gitExitCode !== 0) {
+ die('Could not get Git commit');
+}
+
return [
- 'prefix' => null,
+ 'prefix' => sprintf('_PHPStan_%s', $gitCommitOutputLines[0]),
'finders' => [],
'files-whitelist' => $stubs,
'patchers' => [
@@ -75,7 +87,7 @@ function (string $filePath, string $prefix, string $content): string {
return $content;
},
function (string $filePath, string $prefix, string $content): string {
- if ($filePath !== 'src/Testing/TestCase.php') {
+ if ($filePath !== 'src/Testing/PHPStanTestCase.php') {
return $content;
}
return str_replace(sprintf('\\%s\\PHPUnit\\Framework\\TestCase', $prefix), '\\PHPUnit\\Framework\\TestCase', $content);
@@ -155,7 +167,8 @@ function (string $filePath, string $prefix, string $content): string {
function (string $filePath, string $prefix, string $content): string {
if (!in_array($filePath, [
'src/Testing/TestCaseSourceLocatorFactory.php',
- 'src/Testing/TestCase.php',
+ 'src/Testing/PHPStanTestCase.php',
+ 'vendor/ondrejmirtes/better-reflection/src/SourceLocator/Type/ComposerSourceLocator.php',
], true)) {
return $content;
}
@@ -190,18 +203,72 @@ function (string $filePath, string $prefix, string $content): string {
return str_replace(sprintf('%s\\ReflectionUnionType', $prefix), 'ReflectionUnionType', $content);
},
+ function (string $filePath, string $prefix, string $content): string {
+ if (!in_array($filePath, [
+ 'vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionClass.php',
+ 'vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionClassConstant.php',
+ 'vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionFunction.php',
+ 'vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionMethod.php',
+ 'vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionObject.php',
+ 'vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionParameter.php',
+ 'vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionProperty.php',
+ ], true)) {
+ return $content;
+ }
+
+ return str_replace(sprintf('%s\\ReturnTypeWillChange', $prefix), 'ReturnTypeWillChange', $content);
+ },
+ function (string $filePath, string $prefix, string $content): string {
+ if (!in_array($filePath, [
+ 'src/Type/TypehintHelper.php',
+ 'vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionIntersectionType.php',
+ 'vendor/ondrejmirtes/better-reflection/src/SourceLocator/SourceStubber/ReflectionSourceStubber.php',
+ ], true)) {
+ return $content;
+ }
+
+ return str_replace(sprintf('%s\\ReflectionIntersectionType', $prefix), 'ReflectionIntersectionType', $content);
+ },
function (string $filePath, string $prefix, string $content): string {
if (strpos($filePath, 'src/') !== 0) {
return $content;
}
return str_replace(sprintf('%s\\Attribute', $prefix), 'Attribute', $content);
+ },
+ function (string $filePath, string $prefix, string $content): string {
+ if (strpos($filePath, 'src/') !== 0) {
+ return $content;
+ }
+
+ return str_replace(sprintf('%s\\ReturnTypeWillChange', $prefix), 'ReturnTypeWillChange', $content);
+ },
+ function (string $filePath, string $prefix, string $content): string {
+ if ($filePath !== 'vendor/ondrejmirtes/better-reflection/src/SourceLocator/SourceStubber/PhpStormStubsSourceStubber.php') {
+ return $content;
+ }
+
+ return str_replace('Core/Core_d.php', 'Core/Core_d.stub', $content);
+ },
+ function (string $filePath, string $prefix, string $content): string {
+ if ($filePath !== 'vendor/ondrejmirtes/better-reflection/src/SourceLocator/SourceStubber/PhpStormStubsSourceStubber.php') {
+ return $content;
+ }
+
+ return str_replace(sprintf('\'%s\\\\JetBrains\\\\', $prefix), '\'JetBrains\\\\', $content);
}
],
'whitelist' => [
'PHPStan\*',
'PhpParser\*',
'Hoa\*',
+ 'Symfony\Polyfill\Php80\*',
+ 'Symfony\Polyfill\Mbstring\*',
+ 'Symfony\Polyfill\Intl\Normalizer\*',
+ 'Symfony\Polyfill\Php73\*',
+ 'Symfony\Polyfill\Php74\*',
+ 'Symfony\Polyfill\Php72\*',
+ 'Symfony\Polyfill\Intl\Grapheme\*',
],
'whitelist-global-functions' => false,
'whitelist-global-classes' => false,
diff --git a/compiler/composer.json b/compiler/composer.json
index fb3b8e47c5..89b5e05bf1 100644
--- a/compiler/composer.json
+++ b/compiler/composer.json
@@ -4,12 +4,12 @@
"description": "PHAR Compiler for PHPStan",
"license": ["MIT"],
"require": {
- "php": "^7.3",
+ "php": "^8.0",
"nette/neon": "^3.0.0",
- "symfony/console": "^5.2.2",
- "symfony/process": "^5.2.2",
- "symfony/filesystem": "^5.2.2",
- "symfony/finder": "^5.2.2"
+ "symfony/console": "^6.0.0",
+ "symfony/process": "^6.0.0",
+ "symfony/filesystem": "^6.0.0",
+ "symfony/finder": "^6.0.0"
},
"autoload": {
"psr-4": {
@@ -23,13 +23,15 @@
},
"require-dev": {
"phpunit/phpunit": "^9.5.1",
- "phpstan/phpstan-phpunit": "^0.12.8"
+ "phpstan/phpstan-phpunit": "^1.0"
},
"config": {
"platform": {
- "php": "7.3.24"
+ "php": "8.0.99"
},
"platform-check": false,
"sort-packages": true
- }
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true
}
diff --git a/compiler/composer.lock b/compiler/composer.lock
index 05d5c0ecac..30074d1e1e 100644
--- a/compiler/composer.lock
+++ b/compiler/composer.lock
@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "3d8d846053f4a058c0f88025d0edead6",
+ "content-hash": "fed40217d551b1c8bdc627086b633e9b",
"packages": [
{
"name": "nette/neon",
- "version": "v3.2.2",
+ "version": "v3.3.3",
"source": {
"type": "git",
"url": "https://github.com/nette/neon.git",
- "reference": "e4ca6f4669121ca6876b1d048c612480e39a28d5"
+ "reference": "22e384da162fab42961d48eb06c06d3ad0c11b95"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/neon/zipball/e4ca6f4669121ca6876b1d048c612480e39a28d5",
- "reference": "e4ca6f4669121ca6876b1d048c612480e39a28d5",
+ "url": "https://api.github.com/repos/nette/neon/zipball/22e384da162fab42961d48eb06c06d3ad0c11b95",
+ "reference": "22e384da162fab42961d48eb06c06d3ad0c11b95",
"shasum": ""
},
"require": {
@@ -27,12 +27,15 @@
"require-dev": {
"nette/tester": "^2.0",
"phpstan/phpstan": "^0.12",
- "tracy/tracy": "^2.3"
+ "tracy/tracy": "^2.7"
},
+ "bin": [
+ "bin/neon-lint"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.2-dev"
+ "dev-master": "3.3-dev"
}
},
"autoload": {
@@ -67,28 +70,33 @@
],
"support": {
"issues": "https://github.com/nette/neon/issues",
- "source": "https://github.com/nette/neon/tree/v3.2.2"
+ "source": "https://github.com/nette/neon/tree/v3.3.3"
},
- "time": "2021-02-28T12:30:32+00:00"
+ "time": "2022-03-10T02:04:26+00:00"
},
{
"name": "psr/container",
- "version": "1.1.1",
+ "version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
- "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
- "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"shasum": ""
},
"require": {
- "php": ">=7.2.0"
+ "php": ">=7.4.0"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
@@ -115,51 +123,48 @@
],
"support": {
"issues": "https://github.com/php-fig/container/issues",
- "source": "https://github.com/php-fig/container/tree/1.1.1"
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
},
- "time": "2021-03-05T17:36:06+00:00"
+ "time": "2021-11-05T16:47:00+00:00"
},
{
"name": "symfony/console",
- "version": "v5.3.2",
+ "version": "v6.0.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1"
+ "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/649730483885ff2ca99ca0560ef0e5f6b03f2ac1",
- "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1",
+ "url": "https://api.github.com/repos/symfony/console/zipball/3bebf4108b9e07492a2a4057d207aa5a77d146b1",
+ "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/deprecation-contracts": "^2.1",
+ "php": ">=8.0.2",
"symfony/polyfill-mbstring": "~1.0",
- "symfony/polyfill-php73": "^1.8",
- "symfony/polyfill-php80": "^1.15",
- "symfony/service-contracts": "^1.1|^2",
- "symfony/string": "^5.1"
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/string": "^5.4|^6.0"
},
"conflict": {
- "symfony/dependency-injection": "<4.4",
- "symfony/dotenv": "<5.1",
- "symfony/event-dispatcher": "<4.4",
- "symfony/lock": "<4.4",
- "symfony/process": "<4.4"
+ "symfony/dependency-injection": "<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"
+ "psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "^4.4|^5.0",
- "symfony/dependency-injection": "^4.4|^5.0",
- "symfony/event-dispatcher": "^4.4|^5.0",
- "symfony/lock": "^4.4|^5.0",
- "symfony/process": "^4.4|^5.0",
- "symfony/var-dumper": "^4.4|^5.0"
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/lock": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0",
+ "symfony/var-dumper": "^5.4|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
@@ -199,7 +204,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v5.3.2"
+ "source": "https://github.com/symfony/console/tree/v6.0.5"
},
"funding": [
{
@@ -215,92 +220,26 @@
"type": "tidelift"
}
],
- "time": "2021-06-12T09:42:48+00:00"
- },
- {
- "name": "symfony/deprecation-contracts",
- "version": "v2.4.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627",
- "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "2.4-dev"
- },
- "thanks": {
- "name": "symfony/contracts",
- "url": "https://github.com/symfony/contracts"
- }
- },
- "autoload": {
- "files": [
- "function.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "A generic function and convention to trigger deprecation notices",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.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": "2021-03-23T23:28:01+00:00"
+ "time": "2022-02-25T10:48:52+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v5.3.0",
+ "version": "v6.0.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "348116319d7fb7d1faa781d26a48922428013eb2"
+ "reference": "52b888523545b0b4049ab9ce48766802484d7046"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/348116319d7fb7d1faa781d26a48922428013eb2",
- "reference": "348116319d7fb7d1faa781d26a48922428013eb2",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/52b888523545b0b4049ab9ce48766802484d7046",
+ "reference": "52b888523545b0b4049ab9ce48766802484d7046",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/polyfill-ctype": "~1.8"
+ "php": ">=8.0.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
},
"type": "library",
"autoload": {
@@ -328,7 +267,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/filesystem/tree/v5.3.0"
+ "source": "https://github.com/symfony/filesystem/tree/v6.0.6"
},
"funding": [
{
@@ -344,24 +283,24 @@
"type": "tidelift"
}
],
- "time": "2021-05-26T17:43:10+00:00"
+ "time": "2022-03-02T12:58:14+00:00"
},
{
"name": "symfony/finder",
- "version": "v5.3.0",
+ "version": "v6.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6"
+ "reference": "8661b74dbabc23223f38c9b99d3f8ade71170430"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6",
- "reference": "0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/8661b74dbabc23223f38c9b99d3f8ade71170430",
+ "reference": "8661b74dbabc23223f38c9b99d3f8ade71170430",
"shasum": ""
},
"require": {
- "php": ">=7.2.5"
+ "php": ">=8.0.2"
},
"type": "library",
"autoload": {
@@ -389,7 +328,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v5.3.0"
+ "source": "https://github.com/symfony/finder/tree/v6.0.3"
},
"funding": [
{
@@ -405,25 +344,28 @@
"type": "tidelift"
}
],
- "time": "2021-05-26T12:52:38+00:00"
+ "time": "2022-01-26T17:23:29+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.23.0",
+ "version": "v1.25.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
+ "reference": "30885182c981ab175d4d034db0f6f469898070ab"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
- "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
+ "reference": "30885182c981ab175d4d034db0f6f469898070ab",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
+ "provide": {
+ "ext-ctype": "*"
+ },
"suggest": {
"ext-ctype": "For best performance"
},
@@ -438,12 +380,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Ctype\\": ""
- },
"files": [
"bootstrap.php"
- ]
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -468,7 +410,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
},
"funding": [
{
@@ -484,20 +426,20 @@
"type": "tidelift"
}
],
- "time": "2021-02-19T12:13:01+00:00"
+ "time": "2021-10-20T20:35:02+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
- "version": "v1.23.0",
+ "version": "v1.25.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab"
+ "reference": "81b86b50cf841a64252b439e738e97f4a34e2783"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/24b72c6baa32c746a4d0840147c9715e42bb68ab",
- "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783",
+ "reference": "81b86b50cf841a64252b439e738e97f4a34e2783",
"shasum": ""
},
"require": {
@@ -517,12 +459,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
- },
"files": [
"bootstrap.php"
- ]
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -549,7 +491,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.0"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0"
},
"funding": [
{
@@ -565,11 +507,11 @@
"type": "tidelift"
}
],
- "time": "2021-05-27T09:17:38+00:00"
+ "time": "2021-11-23T21:10:46+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.23.0",
+ "version": "v1.25.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
@@ -598,12 +540,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
- },
"files": [
"bootstrap.php"
],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
"classmap": [
"Resources/stubs"
]
@@ -633,7 +575,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0"
},
"funding": [
{
@@ -653,21 +595,24 @@
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.23.0",
+ "version": "v1.25.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1"
+ "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
- "reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
+ "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
+ "provide": {
+ "ext-mbstring": "*"
+ },
"suggest": {
"ext-mbstring": "For best performance"
},
@@ -682,181 +627,18 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Mbstring\\": ""
- },
- "files": [
- "bootstrap.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": "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.23.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": "2021-05-27T09:27:20+00:00"
- },
- {
- "name": "symfony/polyfill-php73",
- "version": "v1.23.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-php73.git",
- "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
- "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "1.23-dev"
- },
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php73\\": ""
- },
"files": [
"bootstrap.php"
],
- "classmap": [
- "Resources/stubs"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.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": "2021-02-19T12:13:01+00:00"
- },
- {
- "name": "symfony/polyfill-php80",
- "version": "v1.23.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0",
- "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "1.23-dev"
- },
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
- "autoload": {
"psr-4": {
- "Symfony\\Polyfill\\Php80\\": ""
- },
- "files": [
- "bootstrap.php"
- ],
- "classmap": [
- "Resources/stubs"
- ]
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
- {
- "name": "Ion Bazan",
- "email": "ion.bazan@gmail.com"
- },
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
@@ -866,16 +648,17 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
+ "mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0"
},
"funding": [
{
@@ -891,25 +674,24 @@
"type": "tidelift"
}
],
- "time": "2021-02-19T12:13:01+00:00"
+ "time": "2021-11-30T18:21:41+00:00"
},
{
"name": "symfony/process",
- "version": "v5.3.2",
+ "version": "v6.0.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "714b47f9196de61a196d86c4bad5f09201b307df"
+ "reference": "1ccceccc6497e96f4f646218f04b97ae7d9fa7a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/714b47f9196de61a196d86c4bad5f09201b307df",
- "reference": "714b47f9196de61a196d86c4bad5f09201b307df",
+ "url": "https://api.github.com/repos/symfony/process/zipball/1ccceccc6497e96f4f646218f04b97ae7d9fa7a1",
+ "reference": "1ccceccc6497e96f4f646218f04b97ae7d9fa7a1",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/polyfill-php80": "^1.15"
+ "php": ">=8.0.2"
},
"type": "library",
"autoload": {
@@ -937,7 +719,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/process/tree/v5.3.2"
+ "source": "https://github.com/symfony/process/tree/v6.0.5"
},
"funding": [
{
@@ -953,25 +735,28 @@
"type": "tidelift"
}
],
- "time": "2021-06-12T10:15:01+00:00"
+ "time": "2022-01-30T18:19:12+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v2.4.0",
+ "version": "v3.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb"
+ "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
- "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/36715ebf9fb9db73db0cb24263c79077c6fe8603",
+ "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "psr/container": "^1.1"
+ "php": ">=8.0.2",
+ "psr/container": "^2.0"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
@@ -979,7 +764,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.4-dev"
+ "dev-main": "3.0-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -1016,7 +801,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v2.4.0"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.0.0"
},
"funding": [
{
@@ -1032,44 +817,46 @@
"type": "tidelift"
}
],
- "time": "2021-04-01T10:43:52+00:00"
+ "time": "2021-11-04T17:53:12+00:00"
},
{
"name": "symfony/string",
- "version": "v5.3.2",
+ "version": "v6.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "0732e97e41c0a590f77e231afc16a327375d50b0"
+ "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/0732e97e41c0a590f77e231afc16a327375d50b0",
- "reference": "0732e97e41c0a590f77e231afc16a327375d50b0",
+ "url": "https://api.github.com/repos/symfony/string/zipball/522144f0c4c004c80d56fa47e40e17028e2eefc2",
+ "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
+ "php": ">=8.0.2",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-intl-grapheme": "~1.0",
"symfony/polyfill-intl-normalizer": "~1.0",
- "symfony/polyfill-mbstring": "~1.0",
- "symfony/polyfill-php80": "~1.15"
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.0"
},
"require-dev": {
- "symfony/error-handler": "^4.4|^5.0",
- "symfony/http-client": "^4.4|^5.0",
- "symfony/translation-contracts": "^1.1|^2",
- "symfony/var-exporter": "^4.4|^5.0"
+ "symfony/error-handler": "^5.4|^6.0",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/translation-contracts": "^2.0|^3.0",
+ "symfony/var-exporter": "^5.4|^6.0"
},
"type": "library",
"autoload": {
- "psr-4": {
- "Symfony\\Component\\String\\": ""
- },
"files": [
"Resources/functions.php"
],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
"exclude-from-classmap": [
"/Tests/"
]
@@ -1099,7 +886,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v5.3.2"
+ "source": "https://github.com/symfony/string/tree/v6.0.3"
},
"funding": [
{
@@ -1115,35 +902,36 @@
"type": "tidelift"
}
],
- "time": "2021-06-06T09:51:56+00:00"
+ "time": "2022-01-02T09:55:41+00:00"
}
],
"packages-dev": [
{
"name": "doctrine/instantiator",
- "version": "1.4.0",
+ "version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
- "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
- "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
- "doctrine/coding-standard": "^8.0",
+ "doctrine/coding-standard": "^9",
"ext-pdo": "*",
"ext-phar": "*",
- "phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
- "phpstan/phpstan": "^0.12",
- "phpstan/phpstan-phpunit": "^0.12",
- "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
+ "phpbench/phpbench": "^0.16 || ^1",
+ "phpstan/phpstan": "^1.4",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "vimeo/psalm": "^4.22"
},
"type": "library",
"autoload": {
@@ -1170,7 +958,7 @@
],
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
- "source": "https://github.com/doctrine/instantiator/tree/1.4.0"
+ "source": "https://github.com/doctrine/instantiator/tree/1.4.1"
},
"funding": [
{
@@ -1186,41 +974,42 @@
"type": "tidelift"
}
],
- "time": "2020-11-10T18:47:58+00:00"
+ "time": "2022-03-03T08:28:38+00:00"
},
{
"name": "myclabs/deep-copy",
- "version": "1.10.2",
+ "version": "1.11.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
- "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
- "replace": {
- "myclabs/deep-copy": "self.version"
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3,<3.2.2"
},
"require-dev": {
- "doctrine/collections": "^1.0",
- "doctrine/common": "^2.6",
- "phpunit/phpunit": "^7.1"
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
},
"type": "library",
"autoload": {
- "psr-4": {
- "DeepCopy\\": "src/DeepCopy/"
- },
"files": [
"src/DeepCopy/deep_copy.php"
- ]
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1236,7 +1025,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
},
"funding": [
{
@@ -1244,20 +1033,20 @@
"type": "tidelift"
}
],
- "time": "2020-11-13T09:40:50+00:00"
+ "time": "2022-03-03T13:19:32+00:00"
},
{
"name": "nikic/php-parser",
- "version": "v4.10.5",
+ "version": "v4.13.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f"
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4432ba399e47c66624bc73c8c0f811e5c109576f",
- "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077",
"shasum": ""
},
"require": {
@@ -1298,22 +1087,22 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.5"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
},
- "time": "2021-05-03T19:11:20+00:00"
+ "time": "2021-11-30T19:35:32+00:00"
},
{
"name": "phar-io/manifest",
- "version": "2.0.1",
+ "version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/phar-io/manifest.git",
- "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133"
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
- "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
"shasum": ""
},
"require": {
@@ -1358,22 +1147,22 @@
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
"support": {
"issues": "https://github.com/phar-io/manifest/issues",
- "source": "https://github.com/phar-io/manifest/tree/master"
+ "source": "https://github.com/phar-io/manifest/tree/2.0.3"
},
- "time": "2020-06-27T14:33:11+00:00"
+ "time": "2021-07-20T11:28:43+00:00"
},
{
"name": "phar-io/version",
- "version": "3.1.0",
+ "version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/phar-io/version.git",
- "reference": "bae7c545bef187884426f042434e561ab1ddb182"
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182",
- "reference": "bae7c545bef187884426f042434e561ab1ddb182",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
"shasum": ""
},
"require": {
@@ -1409,9 +1198,9 @@
"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.1.0"
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
},
- "time": "2021-02-23T14:00:09+00:00"
+ "time": "2022-02-21T01:04:05+00:00"
},
{
"name": "phpdocumentor/reflection-common",
@@ -1468,16 +1257,16 @@
},
{
"name": "phpdocumentor/reflection-docblock",
- "version": "5.2.2",
+ "version": "5.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
- "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
"shasum": ""
},
"require": {
@@ -1488,7 +1277,8 @@
"webmozart/assert": "^1.9.1"
},
"require-dev": {
- "mockery/mockery": "~1.3.2"
+ "mockery/mockery": "~1.3.2",
+ "psalm/phar": "^4.8"
},
"type": "library",
"extra": {
@@ -1518,22 +1308,22 @@
"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/master"
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
},
- "time": "2020-09-03T19:13:55+00:00"
+ "time": "2021-10-19T17:43:47+00:00"
},
{
"name": "phpdocumentor/type-resolver",
- "version": "1.4.0",
+ "version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
- "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
+ "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
- "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706",
+ "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706",
"shasum": ""
},
"require": {
@@ -1541,7 +1331,8 @@
"phpdocumentor/reflection-common": "^2.0"
},
"require-dev": {
- "ext-tokenizer": "*"
+ "ext-tokenizer": "*",
+ "psalm/phar": "^4.8"
},
"type": "library",
"extra": {
@@ -1567,39 +1358,39 @@
"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.4.0"
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0"
},
- "time": "2020-09-17T18:55:26+00:00"
+ "time": "2022-01-04T19:58:01+00:00"
},
{
"name": "phpspec/prophecy",
- "version": "1.13.0",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
- "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea"
+ "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea",
- "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
+ "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.2",
- "php": "^7.2 || ~8.0, <8.1",
+ "php": "^7.2 || ~8.0, <8.2",
"phpdocumentor/reflection-docblock": "^5.2",
"sebastian/comparator": "^3.0 || ^4.0",
"sebastian/recursion-context": "^3.0 || ^4.0"
},
"require-dev": {
- "phpspec/phpspec": "^6.0",
+ "phpspec/phpspec": "^6.0 || ^7.0",
"phpunit/phpunit": "^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.11.x-dev"
+ "dev-master": "1.x-dev"
}
},
"autoload": {
@@ -1634,22 +1425,22 @@
],
"support": {
"issues": "https://github.com/phpspec/prophecy/issues",
- "source": "https://github.com/phpspec/prophecy/tree/1.13.0"
+ "source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
},
- "time": "2021-03-17T13:42:18+00:00"
+ "time": "2021-12-08T12:19:24+00:00"
},
{
"name": "phpstan/phpstan",
- "version": "0.12.90",
+ "version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "f0e4b56630fc3d4eb5be86606d07212ac212ede4"
+ "reference": "cbe085f9fdead5b6d62e4c022ca52dc9427a10ee"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f0e4b56630fc3d4eb5be86606d07212ac212ede4",
- "reference": "f0e4b56630fc3d4eb5be86606d07212ac212ede4",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cbe085f9fdead5b6d62e4c022ca52dc9427a10ee",
+ "reference": "cbe085f9fdead5b6d62e4c022ca52dc9427a10ee",
"shasum": ""
},
"require": {
@@ -1665,7 +1456,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "0.12-dev"
+ "dev-master": "1.2-dev"
}
},
"autoload": {
@@ -1680,7 +1471,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
- "source": "https://github.com/phpstan/phpstan/tree/0.12.90"
+ "source": "https://github.com/phpstan/phpstan/tree/1.2.0"
},
"funding": [
{
@@ -1700,38 +1491,39 @@
"type": "tidelift"
}
],
- "time": "2021-06-18T07:15:38+00:00"
+ "time": "2021-11-18T14:09:01+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
- "version": "0.12.20",
+ "version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
- "reference": "efc009981af383eb3303f0ca9868c29acad7ce74"
+ "reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/efc009981af383eb3303f0ca9868c29acad7ce74",
- "reference": "efc009981af383eb3303f0ca9868c29acad7ce74",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
+ "reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
- "phpstan/phpstan": "^0.12.86"
+ "phpstan/phpstan": "^1.0"
},
"conflict": {
"phpunit/phpunit": "<7.0"
},
"require-dev": {
+ "nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-strict-rules": "^0.12.6",
+ "phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
- "dev-master": "0.12-dev"
+ "dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
@@ -1752,29 +1544,29 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
- "source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.20"
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.0.0"
},
- "time": "2021-06-17T08:28:30+00:00"
+ "time": "2021-10-14T08:03:54+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "9.2.6",
+ "version": "9.2.15",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "f6293e1b30a2354e8428e004689671b83871edde"
+ "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
- "reference": "f6293e1b30a2354e8428e004689671b83871edde",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
+ "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
- "nikic/php-parser": "^4.10.2",
+ "nikic/php-parser": "^4.13.0",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
@@ -1823,7 +1615,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
},
"funding": [
{
@@ -1831,20 +1623,20 @@
"type": "github"
}
],
- "time": "2021-03-28T07:26:59+00:00"
+ "time": "2022-03-07T09:28:20+00:00"
},
{
"name": "phpunit/php-file-iterator",
- "version": "3.0.5",
+ "version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8"
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8",
- "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"shasum": ""
},
"require": {
@@ -1883,7 +1675,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
- "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5"
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
},
"funding": [
{
@@ -1891,7 +1683,7 @@
"type": "github"
}
],
- "time": "2020-09-28T05:57:25+00:00"
+ "time": "2021-12-02T12:48:52+00:00"
},
{
"name": "phpunit/php-invoker",
@@ -2076,16 +1868,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.5.6",
+ "version": "9.5.18",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb"
+ "reference": "1b5856028273bfd855e60a887278857d872ec67a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb",
- "reference": "fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b5856028273bfd855e60a887278857d872ec67a",
+ "reference": "1b5856028273bfd855e60a887278857d872ec67a",
"shasum": ""
},
"require": {
@@ -2097,11 +1889,11 @@
"ext-xml": "*",
"ext-xmlwriter": "*",
"myclabs/deep-copy": "^1.10.1",
- "phar-io/manifest": "^2.0.1",
+ "phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
"php": ">=7.3",
"phpspec/prophecy": "^1.12.1",
- "phpunit/php-code-coverage": "^9.2.3",
+ "phpunit/php-code-coverage": "^9.2.13",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-invoker": "^3.1.1",
"phpunit/php-text-template": "^2.0.3",
@@ -2136,11 +1928,11 @@
}
},
"autoload": {
- "classmap": [
- "src/"
- ],
"files": [
"src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -2163,11 +1955,11 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.6"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.18"
},
"funding": [
{
- "url": "https://phpunit.de/donate.html",
+ "url": "https://phpunit.de/sponsors.html",
"type": "custom"
},
{
@@ -2175,7 +1967,7 @@
"type": "github"
}
],
- "time": "2021-06-23T05:14:38+00:00"
+ "time": "2022-03-08T06:52:28+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -2606,16 +2398,16 @@
},
{
"name": "sebastian/exporter",
- "version": "4.0.3",
+ "version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65"
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65",
- "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9",
"shasum": ""
},
"require": {
@@ -2664,14 +2456,14 @@
}
],
"description": "Provides the functionality to export PHP variables for visualization",
- "homepage": "http://www.github.com/sebastianbergmann/exporter",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
- "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3"
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4"
},
"funding": [
{
@@ -2679,20 +2471,20 @@
"type": "github"
}
],
- "time": "2020-09-28T05:24:23+00:00"
+ "time": "2021-11-11T14:18:36+00:00"
},
{
"name": "sebastian/global-state",
- "version": "5.0.3",
+ "version": "5.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49"
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49",
- "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"shasum": ""
},
"require": {
@@ -2735,7 +2527,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
- "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3"
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
},
"funding": [
{
@@ -2743,7 +2535,7 @@
"type": "github"
}
],
- "time": "2021-06-11T13:31:12+00:00"
+ "time": "2022-02-14T08:28:10+00:00"
},
{
"name": "sebastian/lines-of-code",
@@ -3143,16 +2935,16 @@
},
{
"name": "theseer/tokenizer",
- "version": "1.2.0",
+ "version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
- "reference": "75a63c33a8577608444246075ea0af0d052e452a"
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a",
- "reference": "75a63c33a8577608444246075ea0af0d052e452a",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
"shasum": ""
},
"require": {
@@ -3181,7 +2973,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
- "source": "https://github.com/theseer/tokenizer/tree/master"
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
},
"funding": [
{
@@ -3189,7 +2981,7 @@
"type": "github"
}
],
- "time": "2020-07-12T23:59:07+00:00"
+ "time": "2021-07-28T10:34:58+00:00"
},
{
"name": "webmozart/assert",
@@ -3251,16 +3043,16 @@
}
],
"aliases": [],
- "minimum-stability": "stable",
+ "minimum-stability": "dev",
"stability-flags": [],
- "prefer-stable": false,
+ "prefer-stable": true,
"prefer-lowest": false,
"platform": {
- "php": "^7.3"
+ "php": "^8.0"
},
"platform-dev": [],
"platform-overrides": {
- "php": "7.3.24"
+ "php": "8.0.99"
},
- "plugin-api-version": "2.1.0"
+ "plugin-api-version": "2.2.0"
}
diff --git a/compiler/patches/stubs/PDO/PDO.stub.patch b/compiler/patches/stubs/PDO/PDO.stub.patch
deleted file mode 100644
index 0f65cc6010..0000000000
--- a/compiler/patches/stubs/PDO/PDO.stub.patch
+++ /dev/null
@@ -1,18 +0,0 @@
---- PDO/PDO.stub 2020-06-14 14:26:12.000000000 +0200
-+++ PDO/PDO2.stub 2020-06-14 14:26:12.000000000 +0200
-@@ -843,6 +843,15 @@
- */
- const SQLITE_ATTR_EXTENDED_RESULT_CODES = 2;
-
-+ const FB_ATTR_DATE_FORMAT = 1;
-+ const FB_ATTR_TIME_FORMAT = 2;
-+ const FB_ATTR_TIMESTAMP_FORMAT = 3;
-+
-+ const OCI_ATTR_ACTION = 1;
-+ const OCI_ATTR_CLIENT_INFO = 2;
-+ const OCI_ATTR_CLIENT_IDENTIFIER = 3;
-+ const OCI_ATTR_MODULE = 4;
-+
- /**
- * (PHP 5 >= 5.1.0, PHP 7, PECL pdo >= 0.1.0)
- * Creates a PDO instance representing a connection to a database
diff --git a/compiler/src/Console/CompileCommand.php b/compiler/src/Console/CompileCommand.php
index 194198f2f2..16834d69e2 100644
--- a/compiler/src/Console/CompileCommand.php
+++ b/compiler/src/Console/CompileCommand.php
@@ -2,40 +2,46 @@
namespace PHPStan\Compiler\Console;
+use Exception;
use PHPStan\Compiler\Filesystem\Filesystem;
use PHPStan\Compiler\Process\ProcessFactory;
+use PHPStan\ShouldNotHappenException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Finder\Finder;
+use function chdir;
+use function dirname;
use function escapeshellarg;
+use function exec;
+use function file_get_contents;
+use function file_put_contents;
+use function implode;
+use function is_dir;
+use function json_decode;
+use function json_encode;
+use function realpath;
+use function rename;
+use function sprintf;
+use function str_replace;
+use function strlen;
+use function substr;
+use function unlink;
+use function var_export;
+use const JSON_PRETTY_PRINT;
+use const JSON_UNESCAPED_SLASHES;
final class CompileCommand extends Command
{
- /** @var Filesystem */
- private $filesystem;
-
- /** @var ProcessFactory */
- private $processFactory;
-
- /** @var string */
- private $dataDir;
-
- /** @var string */
- private $buildDir;
-
public function __construct(
- Filesystem $filesystem,
- ProcessFactory $processFactory,
- string $dataDir,
- string $buildDir
+ private Filesystem $filesystem,
+ private ProcessFactory $processFactory,
+ private string $dataDir,
+ private string $buildDir,
)
{
parent::__construct();
- $this->filesystem = $filesystem;
- $this->processFactory = $processFactory;
- $this->dataDir = $dataDir;
- $this->buildDir = $buildDir;
}
protected function configure(): void
@@ -52,7 +58,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->deleteUnnecessaryVendorCode();
$this->fixComposerJson($this->buildDir);
$this->renamePhpStormStubs();
- $this->patchPhpStormStubs($output);
$this->renamePhp8Stubs();
$this->transformSource();
@@ -74,7 +79,7 @@ private function fixComposerJson(string $buildDir): void
$encodedJson = json_encode($json, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
if ($encodedJson === false) {
- throw new \Exception('json_encode() was not successful.');
+ throw new Exception('json_encode() was not successful.');
}
$this->filesystem->write($buildDir . '/composer.json', $encodedJson);
@@ -87,8 +92,11 @@ private function renamePhpStormStubs(): void
return;
}
- $stubFinder = \Symfony\Component\Finder\Finder::create();
- $stubsMapPath = $directory . '/PhpStormStubsMap.php';
+ $stubFinder = Finder::create();
+ $stubsMapPath = realpath($directory . '/PhpStormStubsMap.php');
+ if ($stubsMapPath === false) {
+ throw new Exception('realpath() failed');
+ }
foreach ($stubFinder->files()->name('*.php')->in($directory) as $stubFile) {
$path = $stubFile->getPathname();
if ($path === $stubsMapPath) {
@@ -97,23 +105,23 @@ private function renamePhpStormStubs(): void
$renameSuccess = rename(
$path,
- dirname($path) . '/' . $stubFile->getBasename('.php') . '.stub'
+ dirname($path) . '/' . $stubFile->getBasename('.php') . '.stub',
);
if ($renameSuccess === false) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Could not rename %s', $path));
+ throw new ShouldNotHappenException(sprintf('Could not rename %s', $path));
}
}
$stubsMapContents = file_get_contents($stubsMapPath);
if ($stubsMapContents === false) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Could not read %s', $stubsMapPath));
+ throw new ShouldNotHappenException(sprintf('Could not read %s', $stubsMapPath));
}
$stubsMapContents = str_replace('.php\',', '.stub\',', $stubsMapContents);
$putSuccess = file_put_contents($stubsMapPath, $stubsMapContents);
if ($putSuccess === false) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Could not write %s', $stubsMapPath));
+ throw new ShouldNotHappenException(sprintf('Could not write %s', $stubsMapPath));
}
}
@@ -124,7 +132,7 @@ private function renamePhp8Stubs(): void
return;
}
- $stubFinder = \Symfony\Component\Finder\Finder::create();
+ $stubFinder = Finder::create();
$stubsMapPath = $directory . '/../Php8StubsMap.php';
foreach ($stubFinder->files()->name('*.php')->in($directory) as $stubFile) {
$path = $stubFile->getPathname();
@@ -134,39 +142,23 @@ private function renamePhp8Stubs(): void
$renameSuccess = rename(
$path,
- dirname($path) . '/' . $stubFile->getBasename('.php') . '.stub'
+ dirname($path) . '/' . $stubFile->getBasename('.php') . '.stub',
);
if ($renameSuccess === false) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Could not rename %s', $path));
+ throw new ShouldNotHappenException(sprintf('Could not rename %s', $path));
}
}
$stubsMapContents = file_get_contents($stubsMapPath);
if ($stubsMapContents === false) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Could not read %s', $stubsMapPath));
+ throw new ShouldNotHappenException(sprintf('Could not read %s', $stubsMapPath));
}
$stubsMapContents = str_replace('.php\',', '.stub\',', $stubsMapContents);
$putSuccess = file_put_contents($stubsMapPath, $stubsMapContents);
if ($putSuccess === false) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Could not write %s', $stubsMapPath));
- }
- }
-
- private function patchPhpStormStubs(OutputInterface $output): void
- {
- $stubFinder = \Symfony\Component\Finder\Finder::create();
- $stubsDirectory = __DIR__ . '/../../../vendor/jetbrains/phpstorm-stubs';
- foreach ($stubFinder->files()->name('*.patch')->in(__DIR__ . '/../../patches/stubs') as $patchFile) {
- $absolutePatchPath = $patchFile->getPathname();
- $patchPath = $patchFile->getRelativePathname();
- $stubPath = realpath($stubsDirectory . '/' . dirname($patchPath) . '/' . basename($patchPath, '.patch'));
- if ($stubPath === false) {
- $output->writeln(sprintf('Stub %s not found.', $stubPath));
- continue;
- }
- $this->patchFile($output, $stubPath, $absolutePatchPath);
+ throw new ShouldNotHappenException(sprintf('Could not write %s', $stubsMapPath));
}
}
@@ -183,7 +175,7 @@ private function buildPreloadScript(): void
%s
php;
- $finder = \Symfony\Component\Finder\Finder::create();
+ $finder = Finder::create();
$root = realpath(__DIR__ . '/../../..');
if ($root === false) {
return;
@@ -218,29 +210,15 @@ private function deleteUnnecessaryVendorCode(): void
@unlink($vendorDir . '/nikic/php-parser/bin/php-parse');
}
- private function patchFile(OutputInterface $output, string $originalFile, string $patchFile): void
- {
- exec(sprintf(
- 'patch -d %s %s %s',
- escapeshellarg($this->buildDir),
- escapeshellarg($originalFile),
- escapeshellarg($patchFile)
- ), $outputLines, $exitCode);
- if ($exitCode === 0) {
- return;
- }
-
- $output->writeln(sprintf('Patching failed: %s', implode("\n", $outputLines)));
- }
-
private function transformSource(): void
{
- exec(escapeshellarg(__DIR__ . '/../../../bin/transform-source.php'), $outputLines, $exitCode);
+ chdir(__DIR__ . '/../../..');
+ exec(escapeshellarg(__DIR__ . '/../../../build/transform-source') . ' 7.1', $outputLines, $exitCode);
if ($exitCode === 0) {
return;
}
- throw new \PHPStan\ShouldNotHappenException(implode("\n", $outputLines));
+ throw new ShouldNotHappenException(implode("\n", $outputLines));
}
}
diff --git a/compiler/src/Filesystem/SymfonyFilesystem.php b/compiler/src/Filesystem/SymfonyFilesystem.php
index 7946e92626..e74d01fd74 100644
--- a/compiler/src/Filesystem/SymfonyFilesystem.php
+++ b/compiler/src/Filesystem/SymfonyFilesystem.php
@@ -2,15 +2,15 @@
namespace PHPStan\Compiler\Filesystem;
+use RuntimeException;
+use function file_get_contents;
+use function file_put_contents;
+
final class SymfonyFilesystem implements Filesystem
{
- /** @var \Symfony\Component\Filesystem\Filesystem */
- private $filesystem;
-
- public function __construct(\Symfony\Component\Filesystem\Filesystem $filesystem)
+ public function __construct(private \Symfony\Component\Filesystem\Filesystem $filesystem)
{
- $this->filesystem = $filesystem;
}
public function exists(string $dir): bool
@@ -32,7 +32,7 @@ public function read(string $file): string
{
$content = file_get_contents($file);
if ($content === false) {
- throw new \RuntimeException();
+ throw new RuntimeException();
}
return $content;
}
diff --git a/compiler/src/Process/DefaultProcessFactory.php b/compiler/src/Process/DefaultProcessFactory.php
index 3f4a09a6b1..3df3ce50da 100644
--- a/compiler/src/Process/DefaultProcessFactory.php
+++ b/compiler/src/Process/DefaultProcessFactory.php
@@ -8,8 +8,7 @@
final class DefaultProcessFactory implements ProcessFactory
{
- /** @var OutputInterface */
- private $output;
+ private OutputInterface $output;
public function __construct()
{
@@ -18,8 +17,6 @@ public function __construct()
/**
* @param string[] $command
- * @param string $cwd
- * @return \PHPStan\Compiler\Process\Process
*/
public function create(array $command, string $cwd): Process
{
diff --git a/compiler/src/Process/ProcessFactory.php b/compiler/src/Process/ProcessFactory.php
index f892601f73..b2225f5343 100644
--- a/compiler/src/Process/ProcessFactory.php
+++ b/compiler/src/Process/ProcessFactory.php
@@ -9,8 +9,6 @@ interface ProcessFactory
/**
* @param string[] $command
- * @param string $cwd
- * @return \PHPStan\Compiler\Process\Process
*/
public function create(array $command, string $cwd): Process;
diff --git a/compiler/src/Process/SymfonyProcess.php b/compiler/src/Process/SymfonyProcess.php
index 56e8b1f5ec..8fbfea879e 100644
--- a/compiler/src/Process/SymfonyProcess.php
+++ b/compiler/src/Process/SymfonyProcess.php
@@ -8,12 +8,10 @@ final class SymfonyProcess implements Process
{
/** @var \Symfony\Component\Process\Process */
- private $process;
+ private \Symfony\Component\Process\Process $process;
/**
* @param string[] $command
- * @param string $cwd
- * @param \Symfony\Component\Console\Output\OutputInterface $output
*/
public function __construct(array $command, string $cwd, OutputInterface $output)
{
diff --git a/compiler/tests/Console/CompileCommandTest.php b/compiler/tests/Console/CompileCommandTest.php
index 72e1a5d5dc..000b738533 100644
--- a/compiler/tests/Console/CompileCommandTest.php
+++ b/compiler/tests/Console/CompileCommandTest.php
@@ -30,8 +30,7 @@ public function testCommand(): void
}
}
}
-EOT
- );
+EOT);
$process = $this->createMock(Process::class);
diff --git a/compiler/tests/Filesystem/SymfonyFilesystemTest.php b/compiler/tests/Filesystem/SymfonyFilesystemTest.php
index 0d21aff557..5a0f486441 100644
--- a/compiler/tests/Filesystem/SymfonyFilesystemTest.php
+++ b/compiler/tests/Filesystem/SymfonyFilesystemTest.php
@@ -3,13 +3,15 @@
namespace PHPStan\Compiler\Filesystem;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Filesystem\Filesystem;
+use function unlink;
final class SymfonyFilesystemTest extends TestCase
{
public function testExists(): void
{
- $inner = $this->createMock(\Symfony\Component\Filesystem\Filesystem::class);
+ $inner = $this->createMock(Filesystem::class);
$inner->expects(self::once())->method('exists')->with('foo')->willReturn(true);
self::assertTrue((new SymfonyFilesystem($inner))->exists('foo'));
@@ -17,7 +19,7 @@ public function testExists(): void
public function testRemove(): void
{
- $inner = $this->createMock(\Symfony\Component\Filesystem\Filesystem::class);
+ $inner = $this->createMock(Filesystem::class);
$inner->expects(self::once())->method('remove')->with('foo')->willReturn(true);
(new SymfonyFilesystem($inner))->remove('foo');
@@ -25,7 +27,7 @@ public function testRemove(): void
public function testMkdir(): void
{
- $inner = $this->createMock(\Symfony\Component\Filesystem\Filesystem::class);
+ $inner = $this->createMock(Filesystem::class);
$inner->expects(self::once())->method('mkdir')->with('foo')->willReturn(true);
(new SymfonyFilesystem($inner))->mkdir('foo');
@@ -33,7 +35,7 @@ public function testMkdir(): void
public function testRead(): void
{
- $inner = $this->createMock(\Symfony\Component\Filesystem\Filesystem::class);
+ $inner = $this->createMock(Filesystem::class);
$content = (new SymfonyFilesystem($inner))->read(__DIR__ . '/data/composer.json');
self::assertSame("{}\n", $content);
@@ -41,7 +43,7 @@ public function testRead(): void
public function testWrite(): void
{
- $inner = $this->createMock(\Symfony\Component\Filesystem\Filesystem::class);
+ $inner = $this->createMock(Filesystem::class);
@unlink(__DIR__ . '/data/test.json');
(new SymfonyFilesystem($inner))->write(__DIR__ . '/data/test.json', "{}\n");
diff --git a/composer.json b/composer.json
index 22f869022e..45c70f5f28 100644
--- a/composer.json
+++ b/composer.json
@@ -5,7 +5,7 @@
"MIT"
],
"require": {
- "php": "^7.4 || ^8.0",
+ "php": "^8.0",
"clue/block-react": "^1.4",
"clue/ndjson-react": "^1.0",
"composer/ca-bundle": "^1.2",
@@ -14,27 +14,36 @@
"hoa/exception": "^1.0",
"hoa/regex": "1.17.01.13",
"jean85/pretty-package-versions": "^1.0.3",
- "jetbrains/phpstorm-stubs": "dev-master#2000119caf61d226e7facad68d7a260d8616a925",
+ "jetbrains/phpstorm-stubs": "dev-master#25c2ba4791d7c08e62aae0793336694ea4fd424a",
"nette/bootstrap": "^3.0",
- "nette/di": "^3.0.5",
+ "nette/di": "^3.0.11",
"nette/finder": "^2.5",
- "nette/neon": "^3.0",
- "nette/schema": "^1.0",
- "nette/utils": "^3.1.3",
- "nikic/php-parser": "4.10.5",
+ "nette/neon": "^3.3.1",
+ "nette/schema": "^1.2.2",
+ "nette/utils": "^3.2.5",
+ "nikic/php-parser": "^4.13.2",
"ondram/ci-detector": "^3.4.0",
- "ondrejmirtes/better-reflection": "4.3.60",
- "phpstan/php-8-stubs": "^0.1.21",
- "phpstan/phpdoc-parser": "^0.5.5",
- "react/child-process": "^0.6.1",
- "react/event-loop": "^1.1",
+ "ondrejmirtes/better-reflection": "5.0.7.2",
+ "phpstan/php-8-stubs": "0.1.49",
+ "phpstan/phpdoc-parser": "^1.2.0",
+ "react/child-process": "^0.6.4",
+ "react/event-loop": "^1.2",
"react/http": "^1.1",
"react/promise": "^2.8",
"react/socket": "^1.3",
"react/stream": "^1.1",
- "symfony/console": "^4.3",
- "symfony/finder": "^4.3",
- "symfony/service-contracts": "1.1.8"
+ "symfony/console": "^5.4.3",
+ "symfony/finder": "^5.4.3",
+ "symfony/polyfill-intl-grapheme": "^1.23",
+ "symfony/polyfill-intl-normalizer": "^1.23",
+ "symfony/polyfill-mbstring": "^1.23",
+ "symfony/polyfill-php72": "^1.23",
+ "symfony/polyfill-php73": "^1.23",
+ "symfony/polyfill-php74": "^1.23",
+ "symfony/polyfill-php80": "^1.23",
+ "symfony/process": "^5.4.3",
+ "symfony/service-contracts": "^2.5.0",
+ "symfony/string": "^5.4.3"
},
"replace": {
"phpstan/phpstan": "self.version"
@@ -43,25 +52,27 @@
"brianium/paratest": "^6.2.0",
"nategood/httpful": "^0.2.20",
"php-parallel-lint/php-parallel-lint": "^1.2.0",
- "phpstan/phpstan-deprecation-rules": "^0.12.3",
- "phpstan/phpstan-nette": "^0.12.18",
- "phpstan/phpstan-php-parser": "^0.12",
- "phpstan/phpstan-phpunit": "^0.12.19",
- "phpstan/phpstan-strict-rules": "^0.12",
+ "phpstan/phpstan-deprecation-rules": "^1.0",
+ "phpstan/phpstan-nette": "^1.0",
+ "phpstan/phpstan-php-parser": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5.4",
+ "rector/rector": "^0.12.15",
"vaimo/composer-patches": "^4.22"
},
"config": {
"platform": {
- "php": "7.4.6"
+ "php": "8.0.99"
},
"platform-check": false,
- "sort-packages": true
+ "sort-packages": true,
+ "allow-plugins": {
+ "composer/package-versions-deprecated": true,
+ "vaimo/composer-patches": true
+ }
},
"extra": {
- "branch-alias": {
- "dev-master": "0.12-dev"
- },
"patcher": {
"search": "patches"
}
diff --git a/composer.lock b/composer.lock
index d6f0ca8b96..1f89e62581 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,31 +4,31 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "232996096032e099e1ea42f579ec050d",
+ "content-hash": "317daa3b733dc326442f1c6bd70a8333",
"packages": [
{
"name": "clue/block-react",
- "version": "v1.4.0",
+ "version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/clue/reactphp-block.git",
- "reference": "c8e7583ae55127b89d6915480ce295bac81c4f88"
+ "reference": "718b0571a94aa693c6fffc72182e87257ac900f3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/clue/reactphp-block/zipball/c8e7583ae55127b89d6915480ce295bac81c4f88",
- "reference": "c8e7583ae55127b89d6915480ce295bac81c4f88",
+ "url": "https://api.github.com/repos/clue/reactphp-block/zipball/718b0571a94aa693c6fffc72182e87257ac900f3",
+ "reference": "718b0571a94aa693c6fffc72182e87257ac900f3",
"shasum": ""
},
"require": {
"php": ">=5.3",
- "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
- "react/promise": "^2.7 || ^1.2.1",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.0 || ^2.7 || ^1.2.1",
"react/promise-timer": "^1.5"
},
"require-dev": {
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
- "react/http": "^1.0"
+ "react/http": "^1.4"
},
"type": "library",
"autoload": {
@@ -60,7 +60,7 @@
],
"support": {
"issues": "https://github.com/clue/reactphp-block/issues",
- "source": "https://github.com/clue/reactphp-block/tree/v1.4.0"
+ "source": "https://github.com/clue/reactphp-block/tree/v1.5.0"
},
"funding": [
{
@@ -72,20 +72,20 @@
"type": "github"
}
],
- "time": "2020-08-21T14:09:44+00:00"
+ "time": "2021-10-20T14:07:33+00:00"
},
{
"name": "clue/ndjson-react",
- "version": "v1.1.0",
+ "version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/clue/reactphp-ndjson.git",
- "reference": "767ec9543945802b5766fab0da4520bf20626f66"
+ "reference": "708411c7e45ac85371a99d50f52284971494bede"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/767ec9543945802b5766fab0da4520bf20626f66",
- "reference": "767ec9543945802b5766fab0da4520bf20626f66",
+ "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/708411c7e45ac85371a99d50f52284971494bede",
+ "reference": "708411c7e45ac85371a99d50f52284971494bede",
"shasum": ""
},
"require": {
@@ -93,7 +93,7 @@
"react/stream": "^1.0 || ^0.7 || ^0.6"
},
"require-dev": {
- "phpunit/phpunit": "^7.0 || ^6.0 || ^5.7 || ^4.8.35",
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3"
},
"type": "library",
@@ -124,22 +124,32 @@
],
"support": {
"issues": "https://github.com/clue/reactphp-ndjson/issues",
- "source": "https://github.com/clue/reactphp-ndjson/tree/v1.1.0"
+ "source": "https://github.com/clue/reactphp-ndjson/tree/v1.2.0"
},
- "time": "2020-02-04T11:48:52+00:00"
+ "funding": [
+ {
+ "url": "https://clue.engineering/support",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2020-12-09T13:09:07+00:00"
},
{
"name": "composer/ca-bundle",
- "version": "1.2.8",
+ "version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
- "reference": "8a7ecad675253e4654ea05505233285377405215"
+ "reference": "4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8a7ecad675253e4654ea05505233285377405215",
- "reference": "8a7ecad675253e4654ea05505233285377405215",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b",
+ "reference": "4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b",
"shasum": ""
},
"require": {
@@ -148,14 +158,15 @@
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
+ "phpstan/phpstan": "^0.12.55",
"psr/log": "^1.0",
- "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
+ "symfony/phpunit-bridge": "^4.2 || ^5",
+ "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.x-dev"
+ "dev-main": "1.x-dev"
}
},
"autoload": {
@@ -185,7 +196,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues",
- "source": "https://github.com/composer/ca-bundle/tree/1.2.8"
+ "source": "https://github.com/composer/ca-bundle/tree/1.3.1"
},
"funding": [
{
@@ -201,20 +212,20 @@
"type": "tidelift"
}
],
- "time": "2020-08-23T12:54:47+00:00"
+ "time": "2021-10-28T20:44:15+00:00"
},
{
"name": "composer/package-versions-deprecated",
- "version": "1.11.99",
+ "version": "1.11.99.4",
"source": {
"type": "git",
"url": "https://github.com/composer/package-versions-deprecated.git",
- "reference": "c8c9aa8a14cc3d3bec86d0a8c3fa52ea79936855"
+ "reference": "b174585d1fe49ceed21928a945138948cb394600"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/c8c9aa8a14cc3d3bec86d0a8c3fa52ea79936855",
- "reference": "c8c9aa8a14cc3d3bec86d0a8c3fa52ea79936855",
+ "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600",
+ "reference": "b174585d1fe49ceed21928a945138948cb394600",
"shasum": ""
},
"require": {
@@ -258,7 +269,78 @@
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"support": {
"issues": "https://github.com/composer/package-versions-deprecated/issues",
- "source": "https://github.com/composer/package-versions-deprecated/tree/master"
+ "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-09-13T08:41:34+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "3d322d715c43a1ac36c7fe215fa59336265500f2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/3d322d715c43a1ac36c7fe215fa59336265500f2",
+ "reference": "3d322d715c43a1ac36c7fe215fa59336265500f2",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "symfony/phpunit-bridge": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.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/1.0.0"
},
"funding": [
{
@@ -274,29 +356,31 @@
"type": "tidelift"
}
],
- "time": "2020-08-25T05:50:16+00:00"
+ "time": "2021-12-06T15:17:27+00:00"
},
{
"name": "composer/xdebug-handler",
- "version": "2.0.1",
+ "version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
- "reference": "964adcdd3a28bf9ed5d9ac6450064e0d71ed7496"
+ "reference": "6555461e76962fd0379c444c46fd558a0fcfb65e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/964adcdd3a28bf9ed5d9ac6450064e0d71ed7496",
- "reference": "964adcdd3a28bf9ed5d9ac6450064e0d71ed7496",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6555461e76962fd0379c444c46fd558a0fcfb65e",
+ "reference": "6555461e76962fd0379c444c46fd558a0fcfb65e",
"shasum": ""
},
"require": {
+ "composer/pcre": "^1",
"php": "^5.3.2 || ^7.0 || ^8.0",
- "psr/log": "^1.0"
+ "psr/log": "^1 || ^2 || ^3"
},
"require-dev": {
- "phpstan/phpstan": "^0.12.55",
- "symfony/phpunit-bridge": "^4.2 || ^5"
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0"
},
"type": "library",
"autoload": {
@@ -322,7 +406,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/xdebug-handler/issues",
- "source": "https://github.com/composer/xdebug-handler/tree/2.0.1"
+ "source": "https://github.com/composer/xdebug-handler/tree/2.0.3"
},
"funding": [
{
@@ -338,7 +422,7 @@
"type": "tidelift"
}
],
- "time": "2021-05-05T19:37:51+00:00"
+ "time": "2021-12-08T13:07:32+00:00"
},
{
"name": "evenement/evenement",
@@ -475,6 +559,7 @@
"issues": "https://github.com/hoaproject/Compiler/issues",
"source": "https://central.hoa-project.net/Resource/Library/Compiler"
},
+ "abandoned": true,
"time": "2017-08-08T07:44:07+00:00"
},
{
@@ -546,6 +631,7 @@
"issues": "https://github.com/hoaproject/Consistency/issues",
"source": "https://central.hoa-project.net/Resource/Library/Consistency"
},
+ "abandoned": true,
"time": "2017-05-02T12:18:12+00:00"
},
{
@@ -610,6 +696,7 @@
"issues": "https://github.com/hoaproject/Event/issues",
"source": "https://central.hoa-project.net/Resource/Library/Event"
},
+ "abandoned": true,
"time": "2017-01-13T15:30:50+00:00"
},
{
@@ -672,6 +759,7 @@
"issues": "https://github.com/hoaproject/Exception/issues",
"source": "https://central.hoa-project.net/Resource/Library/Exception"
},
+ "abandoned": true,
"time": "2017-01-16T07:53:27+00:00"
},
{
@@ -742,6 +830,7 @@
"issues": "https://github.com/hoaproject/File/issues",
"source": "https://central.hoa-project.net/Resource/Library/File"
},
+ "abandoned": true,
"time": "2017-07-11T07:42:15+00:00"
},
{
@@ -804,6 +893,7 @@
"issues": "https://github.com/hoaproject/Iterator/issues",
"source": "https://central.hoa-project.net/Resource/Library/Iterator"
},
+ "abandoned": true,
"time": "2017-01-10T10:34:47+00:00"
},
{
@@ -877,6 +967,7 @@
"issues": "https://github.com/hoaproject/Math/issues",
"source": "https://central.hoa-project.net/Resource/Library/Math"
},
+ "abandoned": true,
"time": "2017-05-16T08:02:17+00:00"
},
{
@@ -945,6 +1036,7 @@
"issues": "https://github.com/hoaproject/Protocol/issues",
"source": "https://central.hoa-project.net/Resource/Library/Protocol"
},
+ "abandoned": true,
"time": "2017-01-14T12:26:10+00:00"
},
{
@@ -1009,6 +1101,7 @@
"issues": "https://github.com/hoaproject/Regex/issues",
"source": "https://central.hoa-project.net/Resource/Library/Regex"
},
+ "abandoned": true,
"time": "2017-01-13T16:10:24+00:00"
},
{
@@ -1081,6 +1174,7 @@
"issues": "https://github.com/hoaproject/Stream/issues",
"source": "https://central.hoa-project.net/Resource/Library/Stream"
},
+ "abandoned": true,
"time": "2017-02-21T16:01:06+00:00"
},
{
@@ -1149,6 +1243,7 @@
"issues": "https://github.com/hoaproject/Ustring/issues",
"source": "https://central.hoa-project.net/Resource/Library/Ustring"
},
+ "abandoned": true,
"time": "2017-01-16T07:08:25+00:00"
},
{
@@ -1212,6 +1307,7 @@
"issues": "https://github.com/hoaproject/Visitor/issues",
"source": "https://central.hoa-project.net/Resource/Library/Visitor"
},
+ "abandoned": true,
"time": "2017-01-16T07:02:03+00:00"
},
{
@@ -1272,6 +1368,7 @@
"issues": "https://github.com/hoaproject/Zformat/issues",
"source": "https://central.hoa-project.net/Resource/Library/Zformat"
},
+ "abandoned": true,
"time": "2017-01-10T10:39:54+00:00"
},
{
@@ -1335,12 +1432,12 @@
"source": {
"type": "git",
"url": "https://github.com/JetBrains/phpstorm-stubs.git",
- "reference": "2000119caf61d226e7facad68d7a260d8616a925"
+ "reference": "25c2ba4791d7c08e62aae0793336694ea4fd424a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/2000119caf61d226e7facad68d7a260d8616a925",
- "reference": "2000119caf61d226e7facad68d7a260d8616a925",
+ "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/25c2ba4791d7c08e62aae0793336694ea4fd424a",
+ "reference": "25c2ba4791d7c08e62aae0793336694ea4fd424a",
"shasum": ""
},
"require-dev": {
@@ -1376,33 +1473,33 @@
"support": {
"source": "https://github.com/JetBrains/phpstorm-stubs/tree/master"
},
- "time": "2021-05-18T14:05:15+00:00"
+ "time": "2022-03-15T14:13:43+00:00"
},
{
"name": "nette/bootstrap",
- "version": "v3.0.2",
+ "version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/nette/bootstrap.git",
- "reference": "67830a65b42abfb906f8e371512d336ebfb5da93"
+ "reference": "3ab4912a08af0c16d541c3709935c3478b5ee090"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/bootstrap/zipball/67830a65b42abfb906f8e371512d336ebfb5da93",
- "reference": "67830a65b42abfb906f8e371512d336ebfb5da93",
+ "url": "https://api.github.com/repos/nette/bootstrap/zipball/3ab4912a08af0c16d541c3709935c3478b5ee090",
+ "reference": "3ab4912a08af0c16d541c3709935c3478b5ee090",
"shasum": ""
},
"require": {
- "nette/di": "^3.0",
- "nette/utils": "^3.0",
- "php": ">=7.1"
+ "nette/di": "^3.0.5",
+ "nette/utils": "^3.2.1",
+ "php": ">=7.2 <8.2"
},
"conflict": {
"tracy/tracy": "<2.6"
},
"require-dev": {
- "latte/latte": "^2.2",
- "nette/application": "^3.0",
+ "latte/latte": "^2.8",
+ "nette/application": "^3.1",
"nette/caching": "^3.0",
"nette/database": "^3.0",
"nette/forms": "^3.0",
@@ -1422,7 +1519,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.0-dev"
+ "dev-master": "3.1-dev"
}
},
"autoload": {
@@ -1446,7 +1543,7 @@
"homepage": "https://nette.org/contributors"
}
],
- "description": "🅱 Nette Bootstrap: the simple way to configure and bootstrap your Nette application.",
+ "description": "🅱 Nette Bootstrap: the simple way to configure and bootstrap your Nette application.",
"homepage": "https://nette.org",
"keywords": [
"bootstrapping",
@@ -1455,32 +1552,32 @@
],
"support": {
"issues": "https://github.com/nette/bootstrap/issues",
- "source": "https://github.com/nette/bootstrap/tree/master"
+ "source": "https://github.com/nette/bootstrap/tree/v3.1.2"
},
- "time": "2020-05-26T08:46:23+00:00"
+ "time": "2021-11-24T16:51:46+00:00"
},
{
"name": "nette/di",
- "version": "v3.0.5",
+ "version": "v3.0.11",
"source": {
"type": "git",
"url": "https://github.com/nette/di.git",
- "reference": "766e8185196a97ded4f9128db6d79a3a124b7eb6"
+ "reference": "942e406f63b88b57cb4e095ae0fd95c103d12c5b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/di/zipball/766e8185196a97ded4f9128db6d79a3a124b7eb6",
- "reference": "766e8185196a97ded4f9128db6d79a3a124b7eb6",
+ "url": "https://api.github.com/repos/nette/di/zipball/942e406f63b88b57cb4e095ae0fd95c103d12c5b",
+ "reference": "942e406f63b88b57cb4e095ae0fd95c103d12c5b",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
- "nette/neon": "^3.0",
+ "nette/neon": "^3.3",
"nette/php-generator": "^3.3.3",
"nette/robot-loader": "^3.2",
- "nette/schema": "^1.0",
- "nette/utils": "^3.1",
- "php": ">=7.1"
+ "nette/schema": "^1.1",
+ "nette/utils": "^3.1.6",
+ "php": ">=7.1 <8.2"
},
"conflict": {
"nette/bootstrap": "<3.0"
@@ -1517,7 +1614,7 @@
"homepage": "https://nette.org/contributors"
}
],
- "description": "💎 Nette Dependency Injection Container: Flexible, compiled and full-featured DIC with perfectly usable autowiring and support for all new PHP 7.1 features.",
+ "description": "💎 Nette Dependency Injection Container: Flexible, compiled and full-featured DIC with perfectly usable autowiring and support for all new PHP features.",
"homepage": "https://nette.org",
"keywords": [
"compiled",
@@ -1530,22 +1627,22 @@
],
"support": {
"issues": "https://github.com/nette/di/issues",
- "source": "https://github.com/nette/di/tree/master"
+ "source": "https://github.com/nette/di/tree/v3.0.11"
},
- "time": "2020-08-13T13:04:23+00:00"
+ "time": "2021-10-26T11:44:44+00:00"
},
{
"name": "nette/finder",
- "version": "v2.5.2",
+ "version": "v2.5.3",
"source": {
"type": "git",
"url": "https://github.com/nette/finder.git",
- "reference": "4ad2c298eb8c687dd0e74ae84206a4186eeaed50"
+ "reference": "64dc25b7929b731e72a1bc84a9e57727f5d5d3e8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/finder/zipball/4ad2c298eb8c687dd0e74ae84206a4186eeaed50",
- "reference": "4ad2c298eb8c687dd0e74ae84206a4186eeaed50",
+ "url": "https://api.github.com/repos/nette/finder/zipball/64dc25b7929b731e72a1bc84a9e57727f5d5d3e8",
+ "reference": "64dc25b7929b731e72a1bc84a9e57727f5d5d3e8",
"shasum": ""
},
"require": {
@@ -1574,8 +1671,8 @@
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
- "GPL-2.0",
- "GPL-3.0"
+ "GPL-2.0-only",
+ "GPL-3.0-only"
],
"authors": [
{
@@ -1597,38 +1694,40 @@
],
"support": {
"issues": "https://github.com/nette/finder/issues",
- "source": "https://github.com/nette/finder/tree/v2.5.2"
+ "source": "https://github.com/nette/finder/tree/v2.5.3"
},
- "time": "2020-01-03T20:35:40+00:00"
+ "time": "2021-12-12T17:43:24+00:00"
},
{
"name": "nette/neon",
- "version": "v3.2.1",
+ "version": "v3.3.2",
"source": {
"type": "git",
"url": "https://github.com/nette/neon.git",
- "reference": "a5b3a60833d2ef55283a82d0c30b45d136b29e75"
+ "reference": "54b287d8c2cdbe577b02e28ca1713e275b05ece2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/neon/zipball/a5b3a60833d2ef55283a82d0c30b45d136b29e75",
- "reference": "a5b3a60833d2ef55283a82d0c30b45d136b29e75",
+ "url": "https://api.github.com/repos/nette/neon/zipball/54b287d8c2cdbe577b02e28ca1713e275b05ece2",
+ "reference": "54b287d8c2cdbe577b02e28ca1713e275b05ece2",
"shasum": ""
},
"require": {
- "ext-iconv": "*",
"ext-json": "*",
"php": ">=7.1"
},
"require-dev": {
"nette/tester": "^2.0",
"phpstan/phpstan": "^0.12",
- "tracy/tracy": "^2.3"
+ "tracy/tracy": "^2.7"
},
+ "bin": [
+ "bin/neon-lint"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.2-dev"
+ "dev-master": "3.3-dev"
}
},
"autoload": {
@@ -1663,33 +1762,33 @@
],
"support": {
"issues": "https://github.com/nette/neon/issues",
- "source": "https://github.com/nette/neon/tree/master"
+ "source": "https://github.com/nette/neon/tree/v3.3.2"
},
- "time": "2020-07-31T12:28:05+00:00"
+ "time": "2021-11-25T15:57:41+00:00"
},
{
"name": "nette/php-generator",
- "version": "v3.5.0",
+ "version": "v3.6.5",
"source": {
"type": "git",
"url": "https://github.com/nette/php-generator.git",
- "reference": "9162f7455059755dcbece1b5570d1bbfc6f0ab0d"
+ "reference": "9370403f9d9c25b51c4596ded1fbfe70347f7c82"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/php-generator/zipball/9162f7455059755dcbece1b5570d1bbfc6f0ab0d",
- "reference": "9162f7455059755dcbece1b5570d1bbfc6f0ab0d",
+ "url": "https://api.github.com/repos/nette/php-generator/zipball/9370403f9d9c25b51c4596ded1fbfe70347f7c82",
+ "reference": "9370403f9d9c25b51c4596ded1fbfe70347f7c82",
"shasum": ""
},
"require": {
"nette/utils": "^3.1.2",
- "php": ">=7.1"
+ "php": ">=7.2 <8.2"
},
"require-dev": {
- "nette/tester": "^2.0",
- "nikic/php-parser": "^4.4",
+ "nette/tester": "^2.4",
+ "nikic/php-parser": "^4.13",
"phpstan/phpstan": "^0.12",
- "tracy/tracy": "^2.3"
+ "tracy/tracy": "^2.8"
},
"suggest": {
"nikic/php-parser": "to use ClassType::withBodiesFrom() & GlobalFunction::withBodyFrom()"
@@ -1697,7 +1796,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.5-dev"
+ "dev-master": "3.6-dev"
}
},
"autoload": {
@@ -1721,7 +1820,7 @@
"homepage": "https://nette.org/contributors"
}
],
- "description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 7.4 features.",
+ "description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 8.1 features.",
"homepage": "https://nette.org",
"keywords": [
"code",
@@ -1731,22 +1830,22 @@
],
"support": {
"issues": "https://github.com/nette/php-generator/issues",
- "source": "https://github.com/nette/php-generator/tree/v3.5.0"
+ "source": "https://github.com/nette/php-generator/tree/v3.6.5"
},
- "time": "2020-11-02T16:16:58+00:00"
+ "time": "2021-11-24T16:23:44+00:00"
},
{
"name": "nette/robot-loader",
- "version": "v3.3.1",
+ "version": "v3.4.1",
"source": {
"type": "git",
"url": "https://github.com/nette/robot-loader.git",
- "reference": "15c1ecd0e6e69e8d908dfc4cca7b14f3b850a96b"
+ "reference": "e2adc334cb958164c050f485d99c44c430f51fe2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/robot-loader/zipball/15c1ecd0e6e69e8d908dfc4cca7b14f3b850a96b",
- "reference": "15c1ecd0e6e69e8d908dfc4cca7b14f3b850a96b",
+ "url": "https://api.github.com/repos/nette/robot-loader/zipball/e2adc334cb958164c050f485d99c44c430f51fe2",
+ "reference": "e2adc334cb958164c050f485d99c44c430f51fe2",
"shasum": ""
},
"require": {
@@ -1763,7 +1862,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.3-dev"
+ "dev-master": "3.4-dev"
}
},
"autoload": {
@@ -1798,36 +1897,38 @@
],
"support": {
"issues": "https://github.com/nette/robot-loader/issues",
- "source": "https://github.com/nette/robot-loader/tree/v3.3.1"
+ "source": "https://github.com/nette/robot-loader/tree/v3.4.1"
},
- "time": "2020-09-15T15:14:17+00:00"
+ "time": "2021-08-25T15:53:54+00:00"
},
{
"name": "nette/schema",
- "version": "v1.0.2",
+ "version": "v1.2.2",
"source": {
"type": "git",
"url": "https://github.com/nette/schema.git",
- "reference": "febf71fb4052c824046f5a33f4f769a6e7fa0cb4"
+ "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/schema/zipball/febf71fb4052c824046f5a33f4f769a6e7fa0cb4",
- "reference": "febf71fb4052c824046f5a33f4f769a6e7fa0cb4",
+ "url": "https://api.github.com/repos/nette/schema/zipball/9a39cef03a5b34c7de64f551538cbba05c2be5df",
+ "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df",
"shasum": ""
},
"require": {
- "nette/utils": "^3.1",
- "php": ">=7.1"
+ "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0",
+ "php": ">=7.1 <8.2"
},
"require-dev": {
- "nette/tester": "^2.2",
+ "nette/tester": "^2.3 || ^2.4",
"phpstan/phpstan-nette": "^0.12",
- "tracy/tracy": "^2.3"
+ "tracy/tracy": "^2.7"
},
"type": "library",
"extra": {
- "branch-alias": []
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
},
"autoload": {
"classmap": [
@@ -1837,8 +1938,8 @@
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
- "GPL-2.0",
- "GPL-3.0"
+ "GPL-2.0-only",
+ "GPL-3.0-only"
],
"authors": [
{
@@ -1858,30 +1959,33 @@
],
"support": {
"issues": "https://github.com/nette/schema/issues",
- "source": "https://github.com/nette/schema/tree/v1.0.2"
+ "source": "https://github.com/nette/schema/tree/v1.2.2"
},
- "time": "2020-01-06T22:52:48+00:00"
+ "time": "2021-10-15T11:40:02+00:00"
},
{
"name": "nette/utils",
- "version": "v3.1.3",
+ "version": "v3.2.6",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
- "reference": "c09937fbb24987b2a41c6022ebe84f4f1b8eec0f"
+ "reference": "2f261e55bd6a12057442045bf2c249806abc1d02"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/utils/zipball/c09937fbb24987b2a41c6022ebe84f4f1b8eec0f",
- "reference": "c09937fbb24987b2a41c6022ebe84f4f1b8eec0f",
+ "url": "https://api.github.com/repos/nette/utils/zipball/2f261e55bd6a12057442045bf2c249806abc1d02",
+ "reference": "2f261e55bd6a12057442045bf2c249806abc1d02",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2 <8.2"
+ },
+ "conflict": {
+ "nette/di": "<3.0.6"
},
"require-dev": {
"nette/tester": "~2.0",
- "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan": "^1.0",
"tracy/tracy": "^2.3"
},
"suggest": {
@@ -1896,7 +2000,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.1-dev"
+ "dev-master": "3.2-dev"
}
},
"autoload": {
@@ -1920,7 +2024,7 @@
"homepage": "https://nette.org/contributors"
}
],
- "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+ "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
"homepage": "https://nette.org",
"keywords": [
"array",
@@ -1940,22 +2044,22 @@
],
"support": {
"issues": "https://github.com/nette/utils/issues",
- "source": "https://github.com/nette/utils/tree/v3.1.3"
+ "source": "https://github.com/nette/utils/tree/v3.2.6"
},
- "time": "2020-08-07T10:34:21+00:00"
+ "time": "2021-11-24T15:47:23+00:00"
},
{
"name": "nikic/php-parser",
- "version": "v4.10.5",
+ "version": "v4.13.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f"
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4432ba399e47c66624bc73c8c0f811e5c109576f",
- "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077",
"shasum": ""
},
"require": {
@@ -1996,9 +2100,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.5"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
},
- "time": "2021-05-03T19:11:20+00:00"
+ "time": "2021-11-30T19:35:32+00:00"
},
{
"name": "ondram/ci-detector",
@@ -2074,37 +2178,37 @@
},
{
"name": "ondrejmirtes/better-reflection",
- "version": "4.3.60",
+ "version": "5.0.7.2",
"source": {
"type": "git",
"url": "https://github.com/ondrejmirtes/BetterReflection.git",
- "reference": "0a94d3041d5801da091d0ecce0ef4e077f83467a"
+ "reference": "62e6d33070d670cf7385b248371179cb3b552c2d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ondrejmirtes/BetterReflection/zipball/0a94d3041d5801da091d0ecce0ef4e077f83467a",
- "reference": "0a94d3041d5801da091d0ecce0ef4e077f83467a",
+ "url": "https://api.github.com/repos/ondrejmirtes/BetterReflection/zipball/62e6d33070d670cf7385b248371179cb3b552c2d",
+ "reference": "62e6d33070d670cf7385b248371179cb3b552c2d",
"shasum": ""
},
"require": {
"ext-json": "*",
- "jetbrains/phpstorm-stubs": "dev-master#0a73df114cdea7f30c8b5f6fbfbf8e6839a89e88",
- "nikic/php-parser": "4.10.5",
- "php": ">=7.1.0"
+ "jetbrains/phpstorm-stubs": "dev-master#3a6d6053bcc6c9a154827b2624e10b5c428b7eb0",
+ "nikic/php-parser": "^4.13.2",
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "thecodingmachine/safe": "<1.1.3"
},
"require-dev": {
- "phpstan/phpstan": "^0.12.49",
- "phpunit/phpunit": "^7.5.18"
+ "doctrine/coding-standard": "^9.0.0",
+ "phpstan/phpstan": "^1.3.0",
+ "phpunit/phpunit": "^9.5.11",
+ "rector/rector": "0.12.5"
},
"suggest": {
"composer/composer": "Required to use the ComposerSourceLocator"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.0-dev"
- }
- },
"autoload": {
"psr-4": {
"PHPStan\\BetterReflection\\": "src"
@@ -2138,22 +2242,22 @@
],
"description": "Better Reflection - an improved code reflection API",
"support": {
- "source": "https://github.com/ondrejmirtes/BetterReflection/tree/4.3.60"
+ "source": "https://github.com/ondrejmirtes/BetterReflection/tree/5.0.7.2"
},
- "time": "2021-06-17T17:55:18+00:00"
+ "time": "2022-02-25T19:46:30+00:00"
},
{
"name": "phpstan/php-8-stubs",
- "version": "0.1.21",
+ "version": "0.1.49",
"source": {
"type": "git",
"url": "https://github.com/phpstan/php-8-stubs.git",
- "reference": "687165b0b4b0ef908278c03e65b2e1c05ecf8e92"
+ "reference": "095a11fc8ba747bd200fc91701ef1c824818bf49"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/php-8-stubs/zipball/687165b0b4b0ef908278c03e65b2e1c05ecf8e92",
- "reference": "687165b0b4b0ef908278c03e65b2e1c05ecf8e92",
+ "url": "https://api.github.com/repos/phpstan/php-8-stubs/zipball/095a11fc8ba747bd200fc91701ef1c824818bf49",
+ "reference": "095a11fc8ba747bd200fc91701ef1c824818bf49",
"shasum": ""
},
"type": "library",
@@ -2170,22 +2274,22 @@
"description": "PHP stubs extracted from php-src",
"support": {
"issues": "https://github.com/phpstan/php-8-stubs/issues",
- "source": "https://github.com/phpstan/php-8-stubs/tree/0.1.21"
+ "source": "https://github.com/phpstan/php-8-stubs/tree/0.1.49"
},
- "time": "2021-06-06T16:07:43+00:00"
+ "time": "2022-03-11T00:14:16+00:00"
},
{
"name": "phpstan/phpdoc-parser",
- "version": "0.5.5",
+ "version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "ea0b17460ec38e20d7eb64e7ec49b5d44af5d28c"
+ "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ea0b17460ec38e20d7eb64e7ec49b5d44af5d28c",
- "reference": "ea0b17460ec38e20d7eb64e7ec49b5d44af5d28c",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/dbc093d7af60eff5cd575d2ed761b15ed40bd08e",
+ "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e",
"shasum": ""
},
"require": {
@@ -2194,15 +2298,15 @@
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12.87",
- "phpstan/phpstan-strict-rules": "^0.12.5",
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "0.5-dev"
+ "dev-master": "1.0-dev"
}
},
"autoload": {
@@ -2219,9 +2323,9 @@
"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/0.5.5"
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/1.2.0"
},
- "time": "2021-06-11T13:24:46+00:00"
+ "time": "2021-09-16T20:46:02+00:00"
},
{
"name": "psr/container",
@@ -2376,16 +2480,16 @@
},
{
"name": "react/cache",
- "version": "v1.1.0",
+ "version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/reactphp/cache.git",
- "reference": "44a568925556b0bd8cacc7b49fb0f1cf0d706a0c"
+ "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/cache/zipball/44a568925556b0bd8cacc7b49fb0f1cf0d706a0c",
- "reference": "44a568925556b0bd8cacc7b49fb0f1cf0d706a0c",
+ "url": "https://api.github.com/repos/reactphp/cache/zipball/4bf736a2cccec7298bdf745db77585966fc2ca7e",
+ "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e",
"shasum": ""
},
"require": {
@@ -2436,7 +2540,7 @@
],
"support": {
"issues": "https://github.com/reactphp/cache/issues",
- "source": "https://github.com/reactphp/cache/tree/v1.1.0"
+ "source": "https://github.com/reactphp/cache/tree/v1.1.1"
},
"funding": [
{
@@ -2448,32 +2552,32 @@
"type": "github"
}
],
- "time": "2020-09-18T12:12:35+00:00"
+ "time": "2021-02-02T06:47:52+00:00"
},
{
"name": "react/child-process",
- "version": "v0.6.1",
+ "version": "v0.6.4",
"source": {
"type": "git",
"url": "https://github.com/reactphp/child-process.git",
- "reference": "6895afa583d51dc10a4b9e93cd3bce17b3b77ac3"
+ "reference": "a778f3fb828d68caf8a9ab6567fd8342a86f12fe"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/child-process/zipball/6895afa583d51dc10a4b9e93cd3bce17b3b77ac3",
- "reference": "6895afa583d51dc10a4b9e93cd3bce17b3b77ac3",
+ "url": "https://api.github.com/repos/reactphp/child-process/zipball/a778f3fb828d68caf8a9ab6567fd8342a86f12fe",
+ "reference": "a778f3fb828d68caf8a9ab6567fd8342a86f12fe",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.0",
- "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
- "react/stream": "^1.0 || ^0.7.6"
+ "react/event-loop": "^1.2",
+ "react/stream": "^1.2"
},
"require-dev": {
- "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35",
- "react/socket": "^1.0",
- "sebastian/environment": "^3.0 || ^2.0 || ^1.0"
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
+ "react/socket": "^1.8",
+ "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0"
},
"type": "library",
"autoload": {
@@ -2485,6 +2589,28 @@
"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",
@@ -2493,28 +2619,38 @@
],
"support": {
"issues": "https://github.com/reactphp/child-process/issues",
- "source": "https://github.com/reactphp/child-process/tree/v0.6.1"
+ "source": "https://github.com/reactphp/child-process/tree/v0.6.4"
},
- "time": "2019-02-15T13:48:16+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-10-12T10:37:07+00:00"
},
{
"name": "react/dns",
- "version": "v1.4.0",
+ "version": "v1.8.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/dns.git",
- "reference": "665260757171e2ab17485b44e7ffffa7acb6ca1f"
+ "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/dns/zipball/665260757171e2ab17485b44e7ffffa7acb6ca1f",
- "reference": "665260757171e2ab17485b44e7ffffa7acb6ca1f",
+ "url": "https://api.github.com/repos/reactphp/dns/zipball/2a5a74ab751e53863b45fb87e1d3913884f88248",
+ "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"react/cache": "^1.0 || ^0.6 || ^0.5",
- "react/event-loop": "^1.0 || ^0.5",
+ "react/event-loop": "^1.2",
"react/promise": "^3.0 || ^2.7 || ^1.2.1",
"react/promise-timer": "^1.2"
},
@@ -2563,7 +2699,7 @@
],
"support": {
"issues": "https://github.com/reactphp/dns/issues",
- "source": "https://github.com/reactphp/dns/tree/v1.4.0"
+ "source": "https://github.com/reactphp/dns/tree/v1.8.0"
},
"funding": [
{
@@ -2575,27 +2711,27 @@
"type": "github"
}
],
- "time": "2020-09-18T12:12:55+00:00"
+ "time": "2021-07-11T12:40:34+00:00"
},
{
"name": "react/event-loop",
- "version": "v1.1.1",
+ "version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/event-loop.git",
- "reference": "6d24de090cd59cfc830263cfba965be77b563c13"
+ "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/event-loop/zipball/6d24de090cd59cfc830263cfba965be77b563c13",
- "reference": "6d24de090cd59cfc830263cfba965be77b563c13",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/be6dee480fc4692cec0504e65eb486e3be1aa6f2",
+ "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
- "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35"
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
},
"suggest": {
"ext-event": "~1.0 for ExtEventLoop",
@@ -2612,6 +2748,28 @@
"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",
@@ -2619,33 +2777,43 @@
],
"support": {
"issues": "https://github.com/reactphp/event-loop/issues",
- "source": "https://github.com/reactphp/event-loop/tree/v1.1.1"
+ "source": "https://github.com/reactphp/event-loop/tree/v1.2.0"
},
- "time": "2020-01-01T18:39:52+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-11T12:31:24+00:00"
},
{
"name": "react/http",
- "version": "v1.1.0",
+ "version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/http.git",
- "reference": "754b0c18545d258922ffa907f3b18598280fdecd"
+ "reference": "8a0fd7c0aa74f0db3008b1e47ca86c613cbb040e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/http/zipball/754b0c18545d258922ffa907f3b18598280fdecd",
- "reference": "754b0c18545d258922ffa907f3b18598280fdecd",
+ "url": "https://api.github.com/repos/reactphp/http/zipball/8a0fd7c0aa74f0db3008b1e47ca86c613cbb040e",
+ "reference": "8a0fd7c0aa74f0db3008b1e47ca86c613cbb040e",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.0",
"psr/http-message": "^1.0",
- "react/event-loop": "^1.0 || ^0.5",
+ "react/event-loop": "^1.2",
"react/promise": "^2.3 || ^1.2.1",
"react/promise-stream": "^1.1",
- "react/socket": "^1.6",
- "react/stream": "^1.1",
+ "react/socket": "^1.9",
+ "react/stream": "^1.2",
"ringcentral/psr7": "^1.2"
},
"require-dev": {
@@ -2703,7 +2871,7 @@
],
"support": {
"issues": "https://github.com/reactphp/http/issues",
- "source": "https://github.com/reactphp/http/tree/v1.1.0"
+ "source": "https://github.com/reactphp/http/tree/v1.5.0"
},
"funding": [
{
@@ -2715,7 +2883,7 @@
"type": "github"
}
],
- "time": "2020-09-11T11:01:51+00:00"
+ "time": "2021-08-04T12:24:55+00:00"
},
{
"name": "react/promise",
@@ -2739,12 +2907,12 @@
},
"type": "library",
"autoload": {
- "psr-4": {
- "React\\Promise\\": "src/"
- },
"files": [
"src/functions_include.php"
- ]
+ ],
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -2769,16 +2937,16 @@
},
{
"name": "react/promise-stream",
- "version": "v1.2.0",
+ "version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise-stream.git",
- "reference": "6384d8b76cf7dcc44b0bf3343fb2b2928412d1fe"
+ "reference": "3ebd94fe0d8edbf44937948af28d02d5437e9949"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/promise-stream/zipball/6384d8b76cf7dcc44b0bf3343fb2b2928412d1fe",
- "reference": "6384d8b76cf7dcc44b0bf3343fb2b2928412d1fe",
+ "url": "https://api.github.com/repos/reactphp/promise-stream/zipball/3ebd94fe0d8edbf44937948af28d02d5437e9949",
+ "reference": "3ebd94fe0d8edbf44937948af28d02d5437e9949",
"shasum": ""
},
"require": {
@@ -2788,18 +2956,18 @@
},
"require-dev": {
"clue/block-react": "^1.0",
- "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35",
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3",
"react/promise-timer": "^1.0"
},
"type": "library",
"autoload": {
- "psr-4": {
- "React\\Promise\\Stream\\": "src/"
- },
"files": [
"src/functions_include.php"
- ]
+ ],
+ "psr-4": {
+ "React\\Promise\\Stream\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -2808,7 +2976,23 @@
"authors": [
{
"name": "Christian Lück",
- "email": "christian@lueck.tv"
+ "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": "The missing link between Promise-land and Stream-land for ReactPHP",
@@ -2823,40 +3007,50 @@
],
"support": {
"issues": "https://github.com/reactphp/promise-stream/issues",
- "source": "https://github.com/reactphp/promise-stream/tree/v1.2.0"
+ "source": "https://github.com/reactphp/promise-stream/tree/v1.3.0"
},
- "time": "2019-07-03T12:29:10+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-10-18T10:47:09+00:00"
},
{
"name": "react/promise-timer",
- "version": "v1.6.0",
+ "version": "v1.8.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise-timer.git",
- "reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6"
+ "reference": "0bbbcc79589e5bfdddba68a287f1cb805581a479"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/daee9baf6ef30c43ea4c86399f828bb5f558f6e6",
- "reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6",
+ "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/0bbbcc79589e5bfdddba68a287f1cb805581a479",
+ "reference": "0bbbcc79589e5bfdddba68a287f1cb805581a479",
"shasum": ""
},
"require": {
"php": ">=5.3",
- "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/event-loop": "^1.2",
"react/promise": "^3.0 || ^2.7.0 || ^1.2.1"
},
"require-dev": {
- "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35"
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
},
"type": "library",
"autoload": {
- "psr-4": {
- "React\\Promise\\Timer\\": "src/"
- },
"files": [
"src/functions_include.php"
- ]
+ ],
+ "psr-4": {
+ "React\\Promise\\Timer\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -2865,7 +3059,23 @@
"authors": [
{
"name": "Christian Lück",
- "email": "christian@lueck.tv"
+ "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": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
@@ -2880,32 +3090,42 @@
],
"support": {
"issues": "https://github.com/reactphp/promise-timer/issues",
- "source": "https://github.com/reactphp/promise-timer/tree/v1.6.0"
+ "source": "https://github.com/reactphp/promise-timer/tree/v1.8.0"
},
- "time": "2020-07-10T12:18:06+00:00"
- },
- {
- "name": "react/socket",
- "version": "v1.6.0",
- "source": {
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-06T11:08:48+00:00"
+ },
+ {
+ "name": "react/socket",
+ "version": "v1.10.0",
+ "source": {
"type": "git",
"url": "https://github.com/reactphp/socket.git",
- "reference": "e2b96b23a13ca9b41ab343268dbce3f8ef4d524a"
+ "reference": "d132fde589ea97f4165f2d94b5296499eac125ec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/socket/zipball/e2b96b23a13ca9b41ab343268dbce3f8ef4d524a",
- "reference": "e2b96b23a13ca9b41ab343268dbce3f8ef4d524a",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/d132fde589ea97f4165f2d94b5296499eac125ec",
+ "reference": "d132fde589ea97f4165f2d94b5296499eac125ec",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.0",
- "react/dns": "^1.1",
- "react/event-loop": "^1.0 || ^0.5",
+ "react/dns": "^1.8",
+ "react/event-loop": "^1.2",
"react/promise": "^2.6.0 || ^1.2.1",
"react/promise-timer": "^1.4.0",
- "react/stream": "^1.1"
+ "react/stream": "^1.2"
},
"require-dev": {
"clue/block-react": "^1.2",
@@ -2954,7 +3174,7 @@
],
"support": {
"issues": "https://github.com/reactphp/socket/issues",
- "source": "https://github.com/reactphp/socket/tree/v1.6.0"
+ "source": "https://github.com/reactphp/socket/tree/v1.10.0"
},
"funding": [
{
@@ -2966,30 +3186,30 @@
"type": "github"
}
],
- "time": "2020-08-28T12:49:05+00:00"
+ "time": "2021-11-29T10:08:24+00:00"
},
{
"name": "react/stream",
- "version": "v1.1.1",
+ "version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/stream.git",
- "reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a"
+ "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/stream/zipball/7c02b510ee3f582c810aeccd3a197b9c2f52ff1a",
- "reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9",
+ "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.8",
- "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5"
+ "react/event-loop": "^1.2"
},
"require-dev": {
"clue/stream-filter": "~1.2",
- "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35"
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
},
"type": "library",
"autoload": {
@@ -3001,6 +3221,28 @@
"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",
@@ -3014,9 +3256,19 @@
],
"support": {
"issues": "https://github.com/reactphp/stream/issues",
- "source": "https://github.com/reactphp/stream/tree/v1.1.1"
+ "source": "https://github.com/reactphp/stream/tree/v1.2.0"
},
- "time": "2020-05-04T10:17:57+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-11T12:37:55+00:00"
},
{
"name": "ringcentral/psr7",
@@ -3049,12 +3301,12 @@
}
},
"autoload": {
- "psr-4": {
- "RingCentral\\Psr7\\": "src/"
- },
"files": [
"src/functions_include.php"
- ]
+ ],
+ "psr-4": {
+ "RingCentral\\Psr7\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -3081,42 +3333,46 @@
},
{
"name": "symfony/console",
- "version": "v4.4.21",
+ "version": "v5.4.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "1ba4560dbbb9fcf5ae28b61f71f49c678086cf23"
+ "reference": "a2a86ec353d825c75856c6fd14fac416a7bdb6b8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/1ba4560dbbb9fcf5ae28b61f71f49c678086cf23",
- "reference": "1ba4560dbbb9fcf5ae28b61f71f49c678086cf23",
+ "url": "https://api.github.com/repos/symfony/console/zipball/a2a86ec353d825c75856c6fd14fac416a7bdb6b8",
+ "reference": "a2a86ec353d825c75856c6fd14fac416a7bdb6b8",
"shasum": ""
},
"require": {
- "php": ">=7.1.3",
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
"symfony/polyfill-mbstring": "~1.0",
- "symfony/polyfill-php73": "^1.8",
- "symfony/polyfill-php80": "^1.15",
- "symfony/service-contracts": "^1.1|^2"
+ "symfony/polyfill-php73": "^1.9",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/string": "^5.1|^6.0"
},
"conflict": {
- "symfony/dependency-injection": "<3.4",
- "symfony/event-dispatcher": "<4.3|>=5",
+ "psr/log": ">=3",
+ "symfony/dependency-injection": "<4.4",
+ "symfony/dotenv": "<5.1",
+ "symfony/event-dispatcher": "<4.4",
"symfony/lock": "<4.4",
- "symfony/process": "<3.3"
+ "symfony/process": "<4.4"
},
"provide": {
- "psr/log-implementation": "1.0"
+ "psr/log-implementation": "1.0|2.0"
},
"require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "^3.4|^4.0|^5.0",
- "symfony/dependency-injection": "^3.4|^4.0|^5.0",
- "symfony/event-dispatcher": "^4.3",
- "symfony/lock": "^4.4|^5.0",
- "symfony/process": "^3.4|^4.0|^5.0",
- "symfony/var-dumper": "^4.3|^5.0"
+ "psr/log": "^1|^2",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/event-dispatcher": "^4.4|^5.0|^6.0",
+ "symfony/lock": "^4.4|^5.0|^6.0",
+ "symfony/process": "^4.4|^5.0|^6.0",
+ "symfony/var-dumper": "^4.4|^5.0|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
@@ -3149,8 +3405,81 @@
],
"description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v5.4.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-01-26T16:28:35+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8",
+ "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/console/tree/v4.4.21"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0"
},
"funding": [
{
@@ -3166,24 +3495,26 @@
"type": "tidelift"
}
],
- "time": "2021-03-26T09:23:24+00:00"
+ "time": "2021-07-12T14:48:14+00:00"
},
{
"name": "symfony/finder",
- "version": "v4.4.16",
+ "version": "v5.4.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "26f63b8d4e92f2eecd90f6791a563ebb001abe31"
+ "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/26f63b8d4e92f2eecd90f6791a563ebb001abe31",
- "reference": "26f63b8d4e92f2eecd90f6791a563ebb001abe31",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/231313534dded84c7ecaa79d14bc5da4ccb69b7d",
+ "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d",
"shasum": ""
},
"require": {
- "php": ">=7.1.3"
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-php80": "^1.16"
},
"type": "library",
"autoload": {
@@ -3208,10 +3539,10 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Finder Component",
+ "description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v4.4.16"
+ "source": "https://github.com/symfony/finder/tree/v5.4.3"
},
"funding": [
{
@@ -3227,32 +3558,35 @@
"type": "tidelift"
}
],
- "time": "2020-10-24T11:50:19+00:00"
+ "time": "2022-01-26T16:34:36+00:00"
},
{
- "name": "symfony/polyfill-mbstring",
- "version": "v1.22.1",
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.24.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "30885182c981ab175d4d034db0f6f469898070ab"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
- "reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
+ "reference": "30885182c981ab175d4d034db0f6f469898070ab",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
+ "provide": {
+ "ext-ctype": "*"
+ },
"suggest": {
- "ext-mbstring": "For best performance"
+ "ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -3260,12 +3594,91 @@
}
},
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
"psr-4": {
- "Symfony\\Polyfill\\Mbstring\\": ""
+ "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.24.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": "2021-10-20T20:35:02+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.24.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "81b86b50cf841a64252b439e738e97f4a34e2783"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783",
+ "reference": "81b86b50cf841a64252b439e738e97f4a34e2783",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
},
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
"files": [
"bootstrap.php"
- ]
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -3281,17 +3694,18 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill for the Mbstring extension",
+ "description": "Symfony polyfill for intl's grapheme_* functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
- "mbstring",
+ "grapheme",
+ "intl",
"polyfill",
"portable",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.24.0"
},
"funding": [
{
@@ -3307,29 +3721,32 @@
"type": "tidelift"
}
],
- "time": "2021-01-22T09:19:47+00:00"
+ "time": "2021-11-23T21:10:46+00:00"
},
{
- "name": "symfony/polyfill-php73",
- "version": "v1.22.1",
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.24.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php73.git",
- "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
- "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
+ "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -3337,12 +3754,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php73\\": ""
- },
"files": [
"bootstrap.php"
],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
"classmap": [
"Resources/stubs"
]
@@ -3361,16 +3778,18 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "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-php73/tree/v1.22.1"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.24.0"
},
"funding": [
{
@@ -3386,29 +3805,35 @@
"type": "tidelift"
}
],
- "time": "2021-01-07T16:49:33+00:00"
+ "time": "2021-02-19T12:13:01+00:00"
},
{
- "name": "symfony/polyfill-php80",
- "version": "v1.22.1",
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.24.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
- "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
+ "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -3416,15 +3841,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php80\\": ""
- },
"files": [
"bootstrap.php"
],
- "classmap": [
- "Resources/stubs"
- ]
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -3432,9 +3854,467 @@
],
"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 for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.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": "2021-11-30T18:21:41+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "v1.24.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "9a142215a36a3888e30d0a9eeea9766764e96976"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976",
+ "reference": "9a142215a36a3888e30d0a9eeea9766764e96976",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.24.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": "2021-05-27T09:17:38+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php73",
+ "version": "v1.24.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php73.git",
+ "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5",
+ "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php73\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.24.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": "2021-06-05T21:20:04+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php74",
+ "version": "v1.24.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php74.git",
+ "reference": "a5d80cdf049bd3b0af6da91184a2cd37533c0fd8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php74/zipball/a5d80cdf049bd3b0af6da91184a2cd37533c0fd8",
+ "reference": "a5d80cdf049bd3b0af6da91184a2cd37533c0fd8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php74\\": ""
+ }
+ },
+ "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 7.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php74/tree/v1.24.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": "2021-02-19T12:13:01+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.24.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9",
+ "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.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": "2021-09-13T13:58:33+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v5.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "553f50487389a977eb31cf6b37faae56da00f753"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/553f50487389a977eb31cf6b37faae56da00f753",
+ "reference": "553f50487389a977eb31cf6b37faae56da00f753",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v5.4.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-01-26T16:28:35+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
+ "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/container": "^1.1",
+ "symfony/deprecation-contracts": "^2.1"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "suggest": {
+ "symfony/service-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
@@ -3444,16 +4324,18 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "description": "Generic abstractions related to writing services",
"homepage": "https://symfony.com",
"keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1"
+ "source": "https://github.com/symfony/service-contracts/tree/v2.5.0"
},
"funding": [
{
@@ -3469,39 +4351,50 @@
"type": "tidelift"
}
],
- "time": "2021-01-07T16:49:33+00:00"
+ "time": "2021-11-04T16:48:04+00:00"
},
{
- "name": "symfony/service-contracts",
- "version": "v1.1.8",
+ "name": "symfony/string",
+ "version": "v5.4.3",
"source": {
"type": "git",
- "url": "https://github.com/symfony/service-contracts.git",
- "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf"
+ "url": "https://github.com/symfony/string.git",
+ "reference": "92043b7d8383e48104e411bc9434b260dbeb5a10"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf",
- "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf",
+ "url": "https://api.github.com/repos/symfony/string/zipball/92043b7d8383e48104e411bc9434b260dbeb5a10",
+ "reference": "92043b7d8383e48104e411bc9434b260dbeb5a10",
"shasum": ""
},
"require": {
- "php": "^7.1.3",
- "psr/container": "^1.0"
+ "php": ">=7.2.5",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php80": "~1.15"
},
- "suggest": {
- "symfony/service-implementation": ""
+ "conflict": {
+ "symfony/translation-contracts": ">=3.0"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.1-dev"
- }
+ "require-dev": {
+ "symfony/error-handler": "^4.4|^5.0|^6.0",
+ "symfony/http-client": "^4.4|^5.0|^6.0",
+ "symfony/translation-contracts": "^1.1|^2",
+ "symfony/var-exporter": "^4.4|^5.0|^6.0"
},
+ "type": "library",
"autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
"psr-4": {
- "Symfony\\Contracts\\Service\\": ""
- }
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -3517,20 +4410,34 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Generic abstractions related to writing services",
+ "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": [
- "abstractions",
- "contracts",
- "decoupling",
- "interfaces",
- "interoperability",
- "standards"
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v1.1.8"
+ "source": "https://github.com/symfony/string/tree/v5.4.3"
},
- "time": "2019-10-14T12:27:06+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-01-02T09:53:40+00:00"
}
],
"packages-dev": [
@@ -3756,9 +4663,6 @@
"require": {
"php": "^7.1 || ^8.0"
},
- "replace": {
- "myclabs/deep-copy": "self.version"
- },
"require-dev": {
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
@@ -3766,12 +4670,12 @@
},
"type": "library",
"autoload": {
- "psr-4": {
- "DeepCopy\\": "src/DeepCopy/"
- },
"files": [
"src/DeepCopy/deep_copy.php"
- ]
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -4246,36 +5150,31 @@
},
{
"name": "phpstan/phpstan-deprecation-rules",
- "version": "0.12.5",
+ "version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
- "reference": "bfabc6a1b4617fbcbff43f03a4c04eae9bafae21"
+ "reference": "e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/bfabc6a1b4617fbcbff43f03a4c04eae9bafae21",
- "reference": "bfabc6a1b4617fbcbff43f03a4c04eae9bafae21",
+ "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682",
+ "reference": "e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
- "phpstan/phpstan": "^0.12.26"
+ "phpstan/phpstan": "^1.0"
},
"require-dev": {
- "consistence/coding-standard": "^3.0.1",
- "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
- "ergebnis/composer-normalize": "^2.0.2",
- "jakub-onderka/php-parallel-lint": "^1.0",
- "phing/phing": "^2.16.0",
- "phpstan/phpstan-phpunit": "^0.12",
- "phpunit/phpunit": "^7.0",
- "slevomat/coding-standard": "^4.5.2"
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^9.5"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
- "dev-master": "0.12-dev"
+ "dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
@@ -4295,27 +5194,27 @@
"description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
"support": {
"issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
- "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/0.12.5"
+ "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.0.0"
},
- "time": "2020-07-21T14:52:30+00:00"
+ "time": "2021-09-23T11:02:21+00:00"
},
{
"name": "phpstan/phpstan-nette",
- "version": "dev-master",
+ "version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-nette.git",
- "reference": "9feb3cafff0f1d3bba38f0e8680085089deceb62"
+ "reference": "f4654b27b107241e052755ec187a0b1964541ba6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-nette/zipball/9feb3cafff0f1d3bba38f0e8680085089deceb62",
- "reference": "9feb3cafff0f1d3bba38f0e8680085089deceb62",
+ "url": "https://api.github.com/repos/phpstan/phpstan-nette/zipball/f4654b27b107241e052755ec187a0b1964541ba6",
+ "reference": "f4654b27b107241e052755ec187a0b1964541ba6",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
- "phpstan/phpstan": "^0.12.86"
+ "phpstan/phpstan": "^1.0"
},
"conflict": {
"nette/application": "<2.3.0",
@@ -4328,18 +5227,17 @@
"require-dev": {
"nette/forms": "^3.0",
"nette/utils": "^2.3.0 || ^3.0.0",
- "phing/phing": "^2.16.3",
+ "nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-php-parser": "^0.12.2",
- "phpstan/phpstan-phpunit": "^0.12.16",
- "phpstan/phpstan-strict-rules": "^0.12.5",
- "phpunit/phpunit": "^7.5.20"
+ "phpstan/phpstan-php-parser": "^1.0",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.0",
+ "phpunit/phpunit": "^9.5"
},
- "default-branch": true,
"type": "phpstan-extension",
"extra": {
"branch-alias": {
- "dev-master": "0.12-dev"
+ "dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
@@ -4360,43 +5258,38 @@
"description": "Nette Framework class reflection extension for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-nette/issues",
- "source": "https://github.com/phpstan/phpstan-nette/tree/master"
+ "source": "https://github.com/phpstan/phpstan-nette/tree/1.0.0"
},
- "time": "2021-04-30T11:16:08+00:00"
+ "time": "2021-09-20T16:12:57+00:00"
},
{
"name": "phpstan/phpstan-php-parser",
- "version": "0.12.2",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-php-parser.git",
- "reference": "0b27eec1e92d48fa82199844dec119b1b22baba0"
+ "reference": "1c7670dd92da864b5d019f22d9f512a6ae18b78e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-php-parser/zipball/0b27eec1e92d48fa82199844dec119b1b22baba0",
- "reference": "0b27eec1e92d48fa82199844dec119b1b22baba0",
+ "url": "https://api.github.com/repos/phpstan/phpstan-php-parser/zipball/1c7670dd92da864b5d019f22d9f512a6ae18b78e",
+ "reference": "1c7670dd92da864b5d019f22d9f512a6ae18b78e",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
- "phpstan/phpstan": "^0.12"
+ "phpstan/phpstan": "^1.3"
},
"require-dev": {
- "consistence/coding-standard": "^3.0.1",
- "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
- "ergebnis/composer-normalize": "^2.0.2",
- "jakub-onderka/php-parallel-lint": "^1.0",
- "phing/phing": "^2.16.0",
- "phpstan/phpstan-phpunit": "^0.12",
- "phpstan/phpstan-strict-rules": "^0.12",
- "phpunit/phpunit": "^7.0",
- "slevomat/coding-standard": "^4.5.2"
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.0",
+ "phpunit/phpunit": "^9.5"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
- "dev-master": "0.12-dev"
+ "dev-master": "1.1-dev"
},
"phpstan": {
"includes": [
@@ -4416,42 +5309,41 @@
"description": "PHP-Parser extensions for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-php-parser/issues",
- "source": "https://github.com/phpstan/phpstan-php-parser/tree/0.12.2"
+ "source": "https://github.com/phpstan/phpstan-php-parser/tree/1.1.0"
},
- "time": "2020-07-21T14:50:29+00:00"
+ "time": "2021-12-16T19:43:32+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
- "version": "dev-master",
+ "version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
- "reference": "52f7072ddc5f81492f9d2de65a24813a48c90b18"
+ "reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/52f7072ddc5f81492f9d2de65a24813a48c90b18",
- "reference": "52f7072ddc5f81492f9d2de65a24813a48c90b18",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
+ "reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
- "phpstan/phpstan": "^0.12.86"
+ "phpstan/phpstan": "^1.0"
},
"conflict": {
"phpunit/phpunit": "<7.0"
},
"require-dev": {
- "phing/phing": "^2.16.3",
+ "nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-strict-rules": "^0.12.6",
- "phpunit/phpunit": "^7.5.20"
+ "phpstan/phpstan-strict-rules": "^1.0",
+ "phpunit/phpunit": "^9.5"
},
- "default-branch": true,
"type": "phpstan-extension",
"extra": {
"branch-alias": {
- "dev-master": "0.12-dev"
+ "dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
@@ -4472,38 +5364,38 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
- "source": "https://github.com/phpstan/phpstan-phpunit/tree/master"
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.0.0"
},
- "time": "2021-04-30T11:10:37+00:00"
+ "time": "2021-10-14T08:03:54+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
- "version": "0.12.9",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
- "reference": "0705fefc7c9168529fd130e341428f5f10f4f01d"
+ "reference": "e12d55f74a8cca18c6e684c6450767e055ba7717"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/0705fefc7c9168529fd130e341428f5f10f4f01d",
- "reference": "0705fefc7c9168529fd130e341428f5f10f4f01d",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/e12d55f74a8cca18c6e684c6450767e055ba7717",
+ "reference": "e12d55f74a8cca18c6e684c6450767e055ba7717",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
- "phpstan/phpstan": "^0.12.66"
+ "phpstan/phpstan": "^1.2.0"
},
"require-dev": {
- "phing/phing": "^2.16.3",
+ "nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-phpunit": "^0.12.16",
- "phpunit/phpunit": "^7.5.20"
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^9.5"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
- "dev-master": "0.12-dev"
+ "dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
@@ -4523,29 +5415,29 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
- "source": "https://github.com/phpstan/phpstan-strict-rules/tree/0.12.9"
+ "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.1.0"
},
- "time": "2021-01-13T08:50:28+00:00"
+ "time": "2021-11-18T09:30:29+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "9.2.6",
+ "version": "9.2.10",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "f6293e1b30a2354e8428e004689671b83871edde"
+ "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
- "reference": "f6293e1b30a2354e8428e004689671b83871edde",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687",
+ "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
- "nikic/php-parser": "^4.10.2",
+ "nikic/php-parser": "^4.13.0",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
@@ -4594,7 +5486,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10"
},
"funding": [
{
@@ -4602,20 +5494,20 @@
"type": "github"
}
],
- "time": "2021-03-28T07:26:59+00:00"
+ "time": "2021-12-05T09:12:13+00:00"
},
{
"name": "phpunit/php-file-iterator",
- "version": "3.0.5",
+ "version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8"
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8",
- "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"shasum": ""
},
"require": {
@@ -4654,7 +5546,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
- "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5"
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
},
"funding": [
{
@@ -4662,7 +5554,7 @@
"type": "github"
}
],
- "time": "2020-09-28T05:57:25+00:00"
+ "time": "2021-12-02T12:48:52+00:00"
},
{
"name": "phpunit/php-invoker",
@@ -4847,16 +5739,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.5.4",
+ "version": "9.5.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "c73c6737305e779771147af66c96ca6a7ed8a741"
+ "reference": "d0dc8b6999c937616df4fb046792004b33fd31c5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741",
- "reference": "c73c6737305e779771147af66c96ca6a7ed8a741",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0dc8b6999c937616df4fb046792004b33fd31c5",
+ "reference": "d0dc8b6999c937616df4fb046792004b33fd31c5",
"shasum": ""
},
"require": {
@@ -4886,7 +5778,7 @@
"sebastian/global-state": "^5.0.1",
"sebastian/object-enumerator": "^4.0.3",
"sebastian/resource-operations": "^3.0.3",
- "sebastian/type": "^2.3",
+ "sebastian/type": "^2.3.4",
"sebastian/version": "^3.0.2"
},
"require-dev": {
@@ -4907,11 +5799,11 @@
}
},
"autoload": {
- "classmap": [
- "src/"
- ],
"files": [
"src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -4934,7 +5826,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.7"
},
"funding": [
{
@@ -4946,7 +5838,67 @@
"type": "github"
}
],
- "time": "2021-03-23T07:16:29+00:00"
+ "time": "2021-07-19T06:14:47+00:00"
+ },
+ {
+ "name": "rector/rector",
+ "version": "0.12.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/rectorphp/rector.git",
+ "reference": "e923bcd0d675dc4d8746da0554089b484044d0b5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/e923bcd0d675dc4d8746da0554089b484044d0b5",
+ "reference": "e923bcd0d675dc4d8746da0554089b484044d0b5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1|^8.0",
+ "phpstan/phpstan": "^1.4.2"
+ },
+ "conflict": {
+ "phpstan/phpdoc-parser": "<1.2",
+ "rector/rector-cakephp": "*",
+ "rector/rector-doctrine": "*",
+ "rector/rector-laravel": "*",
+ "rector/rector-nette": "*",
+ "rector/rector-phpoffice": "*",
+ "rector/rector-phpunit": "*",
+ "rector/rector-prefixed": "*",
+ "rector/rector-symfony": "*"
+ },
+ "bin": [
+ "bin/rector"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "0.12-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Instant Upgrade and Automated Refactoring of any PHP code",
+ "support": {
+ "issues": "https://github.com/rectorphp/rector/issues",
+ "source": "https://github.com/rectorphp/rector/tree/0.12.15"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/tomasvotruba",
+ "type": "github"
+ }
+ ],
+ "time": "2022-01-26T12:02:35+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -5377,16 +6329,16 @@
},
{
"name": "sebastian/exporter",
- "version": "4.0.3",
+ "version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65"
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65",
- "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9",
"shasum": ""
},
"require": {
@@ -5435,14 +6387,14 @@
}
],
"description": "Provides the functionality to export PHP variables for visualization",
- "homepage": "http://www.github.com/sebastianbergmann/exporter",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
- "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3"
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4"
},
"funding": [
{
@@ -5450,20 +6402,20 @@
"type": "github"
}
],
- "time": "2020-09-28T05:24:23+00:00"
+ "time": "2021-11-11T14:18:36+00:00"
},
{
"name": "sebastian/global-state",
- "version": "5.0.2",
+ "version": "5.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "a90ccbddffa067b51f574dea6eb25d5680839455"
+ "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455",
- "reference": "a90ccbddffa067b51f574dea6eb25d5680839455",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49",
+ "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49",
"shasum": ""
},
"require": {
@@ -5506,7 +6458,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
- "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2"
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3"
},
"funding": [
{
@@ -5514,7 +6466,7 @@
"type": "github"
}
],
- "time": "2020-10-26T15:55:19+00:00"
+ "time": "2021-06-11T13:31:12+00:00"
},
{
"name": "sebastian/lines-of-code",
@@ -5805,16 +6757,16 @@
},
{
"name": "sebastian/type",
- "version": "2.3.1",
+ "version": "2.3.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
- "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2"
+ "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
- "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914",
+ "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914",
"shasum": ""
},
"require": {
@@ -5849,7 +6801,7 @@
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
- "source": "https://github.com/sebastianbergmann/type/tree/2.3.1"
+ "source": "https://github.com/sebastianbergmann/type/tree/2.3.4"
},
"funding": [
{
@@ -5857,7 +6809,7 @@
"type": "github"
}
],
- "time": "2020-10-26T13:18:59+00:00"
+ "time": "2021-06-15T12:49:02+00:00"
},
{
"name": "sebastian/version",
@@ -5975,147 +6927,6 @@
],
"time": "2020-11-11T09:19:24+00:00"
},
- {
- "name": "symfony/polyfill-ctype",
- "version": "v1.22.1",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
- "reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1"
- },
- "suggest": {
- "ext-ctype": "For best performance"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "1.22-dev"
- },
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Ctype\\": ""
- },
- "files": [
- "bootstrap.php"
- ]
- },
- "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.22.1"
- },
- "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": "2021-01-07T16:49:33+00:00"
- },
- {
- "name": "symfony/process",
- "version": "v5.2.4",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/process.git",
- "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f",
- "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f",
- "shasum": ""
- },
- "require": {
- "php": ">=7.2.5",
- "symfony/polyfill-php80": "^1.15"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Process\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Executes commands in sub-processes",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/symfony/process/tree/v5.2.4"
- },
- "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": "2021-01-27T10:15:41+00:00"
- },
{
"name": "theseer/tokenizer",
"version": "1.2.0",
@@ -6393,11 +7204,11 @@
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
- "php": "^7.4 || ^8.0"
+ "php": "^8.0"
},
"platform-dev": [],
"platform-overrides": {
- "php": "7.4.6"
+ "php": "8.0.99"
},
- "plugin-api-version": "2.1.0"
+ "plugin-api-version": "2.2.0"
}
diff --git a/conf/bleedingEdge.neon b/conf/bleedingEdge.neon
index ea66c26407..5af3a763db 100644
--- a/conf/bleedingEdge.neon
+++ b/conf/bleedingEdge.neon
@@ -1,25 +1,7 @@
parameters:
featureToggles:
bleedingEdge: true
- closureUsesThis: true
- randomIntParameters: true
- nullCoalesce: true
- fileWhitespace: true
- unusedClassElements: true
- readComposerPhpVersion: true
- dateTimeInstantiation: true
- detectDuplicateStubFiles: true
- checkLogicalAndConstantCondition: true
- checkLogicalOrConstantCondition: true
- checkMissingTemplateTypeInParameter: true
- wrongVarUsage: true
- arrayDestructuring: true
- objectFromNewClass: true
skipCheckGenericClasses: []
- rememberFunctionValues: true
- preciseExceptionTracking: true
- apiRules: true
- deepInspectTypes: true
- neverInGenericReturnType: true
+ explicitMixedInUnknownGenericNew: true
stubFiles:
- - ../stubs/arrayFunctions.stub
+ - ../stubs/bleedingEdge/Countable.stub
diff --git a/conf/config.level0.neon b/conf/config.level0.neon
index 5df0e46ec7..9b911c7b1f 100644
--- a/conf/config.level0.neon
+++ b/conf/config.level0.neon
@@ -1,37 +1,19 @@
parameters:
customRulesetUsed: false
- missingClosureNativeReturnCheckObjectTypehint: false
conditionalTags:
- PHPStan\Rules\Api\ApiInstantiationRule:
- phpstan.rules.rule: %featureToggles.apiRules%
- PHPStan\Rules\Api\ApiClassExtendsRule:
- phpstan.rules.rule: %featureToggles.apiRules%
- PHPStan\Rules\Api\ApiClassImplementsRule:
- phpstan.rules.rule: %featureToggles.apiRules%
- PHPStan\Rules\Api\ApiInterfaceExtendsRule:
- phpstan.rules.rule: %featureToggles.apiRules%
- PHPStan\Rules\Api\ApiMethodCallRule:
- phpstan.rules.rule: %featureToggles.apiRules%
- PHPStan\Rules\Api\ApiStaticCallRule:
- phpstan.rules.rule: %featureToggles.apiRules%
- PHPStan\Rules\Api\ApiTraitUseRule:
- phpstan.rules.rule: %featureToggles.apiRules%
- PHPStan\Rules\Api\PhpStanNamespaceIn3rdPartyPackageRule:
- phpstan.rules.rule: %featureToggles.apiRules%
- PHPStan\Rules\Functions\ClosureUsesThisRule:
- phpstan.rules.rule: %featureToggles.closureUsesThis%
- PHPStan\Rules\Missing\MissingClosureNativeReturnTypehintRule:
- phpstan.rules.rule: %checkMissingClosureNativeReturnTypehintRule%
- PHPStan\Rules\Whitespace\FileWhitespaceRule:
- phpstan.rules.rule: %featureToggles.fileWhitespace%
PHPStan\Rules\Properties\UninitializedPropertyRule:
phpstan.rules.rule: %checkUninitializedProperties%
-parametersSchema:
- missingClosureNativeReturnCheckObjectTypehint: bool()
-
rules:
+ - PHPStan\Rules\Api\ApiInstantiationRule
+ - PHPStan\Rules\Api\ApiClassExtendsRule
+ - PHPStan\Rules\Api\ApiClassImplementsRule
+ - PHPStan\Rules\Api\ApiInterfaceExtendsRule
+ - PHPStan\Rules\Api\ApiMethodCallRule
+ - PHPStan\Rules\Api\ApiStaticCallRule
+ - PHPStan\Rules\Api\ApiTraitUseRule
+ - PHPStan\Rules\Api\PhpStanNamespaceIn3rdPartyPackageRule
- PHPStan\Rules\Arrays\DuplicateKeysInLiteralArraysRule
- PHPStan\Rules\Arrays\EmptyArrayItemRule
- PHPStan\Rules\Arrays\OffsetAccessWithoutDimForReadingRule
@@ -40,20 +22,25 @@ rules:
- PHPStan\Rules\Classes\ClassConstantAttributesRule
- PHPStan\Rules\Classes\ClassConstantRule
- PHPStan\Rules\Classes\DuplicateDeclarationRule
+ - PHPStan\Rules\Classes\EnumSanityRule
- PHPStan\Rules\Classes\ExistingClassesInClassImplementsRule
+ - PHPStan\Rules\Classes\ExistingClassesInEnumImplementsRule
- PHPStan\Rules\Classes\ExistingClassesInInterfaceExtendsRule
- - PHPStan\Rules\Classes\ExistingClassInClassExtendsRule
- PHPStan\Rules\Classes\ExistingClassInTraitUseRule
- PHPStan\Rules\Classes\InstantiationRule
+ - PHPStan\Rules\Classes\InstantiationCallableRule
- PHPStan\Rules\Classes\InvalidPromotedPropertiesRule
- PHPStan\Rules\Classes\NewStaticRule
- PHPStan\Rules\Classes\NonClassAttributeClassRule
- PHPStan\Rules\Classes\TraitAttributeClassRule
+ - PHPStan\Rules\Constants\FinalConstantRule
+ - PHPStan\Rules\EnumCases\EnumCaseAttributesRule
- PHPStan\Rules\Exceptions\ThrowExpressionRule
- PHPStan\Rules\Functions\ArrowFunctionAttributesRule
- PHPStan\Rules\Functions\ArrowFunctionReturnNullsafeByRefRule
- PHPStan\Rules\Functions\CallToFunctionParametersRule
- PHPStan\Rules\Functions\ClosureAttributesRule
+ - PHPStan\Rules\Functions\DefineParametersRule
- PHPStan\Rules\Functions\ExistingClassesInArrowFunctionTypehintsRule
- PHPStan\Rules\Functions\ExistingClassesInClosureTypehintsRule
- PHPStan\Rules\Functions\ExistingClassesInTypehintsRule
@@ -64,39 +51,26 @@ rules:
- PHPStan\Rules\Functions\ReturnNullsafeByRefRule
- PHPStan\Rules\Keywords\ContinueBreakInLoopRule
- PHPStan\Rules\Methods\AbstractMethodInNonAbstractClassRule
+ - PHPStan\Rules\Methods\CallMethodsRule
+ - PHPStan\Rules\Methods\CallStaticMethodsRule
- PHPStan\Rules\Methods\ExistingClassesInTypehintsRule
+ - PHPStan\Rules\Methods\MethodCallableRule
- PHPStan\Rules\Methods\MissingMethodImplementationRule
- PHPStan\Rules\Methods\MethodAttributesRule
+ - PHPStan\Rules\Methods\StaticMethodCallableRule
- PHPStan\Rules\Operators\InvalidAssignVarRule
- PHPStan\Rules\Properties\AccessPropertiesInAssignRule
- PHPStan\Rules\Properties\AccessStaticPropertiesInAssignRule
- PHPStan\Rules\Properties\PropertyAttributesRule
+ - PHPStan\Rules\Properties\ReadOnlyPropertyRule
- PHPStan\Rules\Variables\UnsetRule
+ - PHPStan\Rules\Whitespace\FileWhitespaceRule
services:
-
- class: PHPStan\Rules\Api\ApiInstantiationRule
-
- -
- class: PHPStan\Rules\Api\ApiClassExtendsRule
-
- -
- class: PHPStan\Rules\Api\ApiClassImplementsRule
-
- -
- class: PHPStan\Rules\Api\ApiInterfaceExtendsRule
-
- -
- class: PHPStan\Rules\Api\ApiMethodCallRule
-
- -
- class: PHPStan\Rules\Api\ApiStaticCallRule
-
- -
- class: PHPStan\Rules\Api\ApiTraitUseRule
-
- -
- class: PHPStan\Rules\Api\PhpStanNamespaceIn3rdPartyPackageRule
+ class: PHPStan\Rules\Classes\ExistingClassInClassExtendsRule
+ tags:
+ - phpstan.rules.rule
-
class: PHPStan\Rules\Classes\ExistingClassInInstanceOfRule
@@ -120,23 +94,11 @@ services:
checkFunctionNameCase: %checkFunctionNameCase%
-
- class: PHPStan\Rules\Functions\ClosureUsesThisRule
-
- -
- class: PHPStan\Rules\Methods\CallMethodsRule
- tags:
- - phpstan.rules.rule
+ class: PHPStan\Rules\Constants\OverridingConstantRule
arguments:
- checkFunctionNameCase: %checkFunctionNameCase%
- reportMagicMethods: %reportMagicMethods%
-
- -
- class: PHPStan\Rules\Methods\CallStaticMethodsRule
+ checkPhpDocMethodSignatures: %checkPhpDocMethodSignatures%
tags:
- phpstan.rules.rule
- arguments:
- checkFunctionNameCase: %checkFunctionNameCase%
- reportMagicMethods: %reportMagicMethods%
-
class: PHPStan\Rules\Methods\OverridingMethodRule
@@ -145,11 +107,6 @@ services:
tags:
- phpstan.rules.rule
- -
- class: PHPStan\Rules\Missing\MissingClosureNativeReturnTypehintRule
- arguments:
- checkObjectTypehint: %missingClosureNativeReturnCheckObjectTypehint%
-
-
class: PHPStan\Rules\Missing\MissingReturnRule
arguments:
@@ -199,6 +156,29 @@ services:
checkClassCaseSensitivity: %checkClassCaseSensitivity%
checkThisOnly: %checkThisOnly%
+ -
+ class: PHPStan\Rules\Functions\FunctionCallableRule
+ arguments:
+ checkFunctionNameCase: %checkFunctionNameCase%
+ reportMaybes: %reportMaybes%
+ tags:
+ - phpstan.rules.rule
+
+ -
+ class: PHPStan\Rules\Properties\MissingReadOnlyPropertyAssignRule
+ arguments:
+ additionalConstructors: %additionalConstructors%
+ tags:
+ - phpstan.rules.rule
+
+ -
+ class: PHPStan\Rules\Properties\OverridingPropertyRule
+ arguments:
+ checkPhpDocMethodSignatures: %checkPhpDocMethodSignatures%
+ reportMaybes: %reportMaybesInPropertyPhpDocTypes%
+ tags:
+ - phpstan.rules.rule
+
-
class: PHPStan\Rules\Properties\UninitializedPropertyRule
arguments:
@@ -238,9 +218,6 @@ services:
tags:
- phpstan.rules.rule
- -
- class: PHPStan\Rules\Whitespace\FileWhitespaceRule
-
-
class: PHPStan\Rules\Classes\LocalTypeAliasesRule
arguments:
diff --git a/conf/config.level1.neon b/conf/config.level1.neon
index 2499484180..3b5f68d64a 100644
--- a/conf/config.level1.neon
+++ b/conf/config.level1.neon
@@ -7,17 +7,10 @@ parameters:
reportMagicMethods: true
reportMagicProperties: true
-
-conditionalTags:
- PHPStan\Rules\Variables\VariableCertaintyNullCoalesceRule:
- phpstan.rules.rule: %featureToggles.nullCoalesce%
-
rules:
- PHPStan\Rules\Classes\UnusedConstructorParametersRule
- PHPStan\Rules\Constants\ConstantRule
- PHPStan\Rules\Functions\UnusedClosureUsesRule
- - PHPStan\Rules\Variables\VariableCertaintyInIssetRule
-
-services:
- -
- class: PHPStan\Rules\Variables\VariableCertaintyNullCoalesceRule
+ - PHPStan\Rules\Variables\EmptyRule
+ - PHPStan\Rules\Variables\IssetRule
+ - PHPStan\Rules\Variables\NullCoalesceRule
diff --git a/conf/config.level2.neon b/conf/config.level2.neon
index c284232e28..becc7b5b02 100644
--- a/conf/config.level2.neon
+++ b/conf/config.level2.neon
@@ -11,10 +11,13 @@ rules:
- PHPStan\Rules\Cast\InvalidCastRule
- PHPStan\Rules\Cast\InvalidPartOfEncapsedStringRule
- PHPStan\Rules\Cast\PrintRule
+ - PHPStan\Rules\Classes\AccessPrivateConstantThroughStaticRule
- PHPStan\Rules\Comparison\UsageOfVoidMatchExpressionRule
- PHPStan\Rules\Functions\IncompatibleDefaultParameterTypeRule
- PHPStan\Rules\Generics\ClassAncestorsRule
- PHPStan\Rules\Generics\ClassTemplateTypeRule
+ - PHPStan\Rules\Generics\EnumAncestorsRule
+ - PHPStan\Rules\Generics\EnumTemplateTypeRule
- PHPStan\Rules\Generics\FunctionTemplateTypeRule
- PHPStan\Rules\Generics\FunctionSignatureVarianceRule
- PHPStan\Rules\Generics\InterfaceAncestorsRule
@@ -23,15 +26,19 @@ rules:
- PHPStan\Rules\Generics\MethodSignatureVarianceRule
- PHPStan\Rules\Generics\TraitTemplateTypeRule
- PHPStan\Rules\Generics\UsedTraitsRule
+ - PHPStan\Rules\Methods\CallPrivateMethodThroughStaticRule
- PHPStan\Rules\Methods\IncompatibleDefaultParameterTypeRule
- PHPStan\Rules\Operators\InvalidBinaryOperationRule
- PHPStan\Rules\Operators\InvalidUnaryOperationRule
- PHPStan\Rules\Operators\InvalidComparisonOperationRule
+ - PHPStan\Rules\PhpDoc\IncompatibleClassConstantPhpDocTypeRule
- PHPStan\Rules\PhpDoc\IncompatiblePhpDocTypeRule
- PHPStan\Rules\PhpDoc\IncompatiblePropertyPhpDocTypeRule
- PHPStan\Rules\PhpDoc\InvalidPhpDocTagValueRule
- PHPStan\Rules\PhpDoc\InvalidPHPStanDocTagRule
- PHPStan\Rules\PhpDoc\InvalidThrowsPhpDocValueRule
+ - PHPStan\Rules\PhpDoc\WrongVariableNameInVarTagRule
+ - PHPStan\Rules\Properties\AccessPrivatePropertyThroughStaticRule
services:
-
@@ -53,9 +60,3 @@ services:
checkMissingVarTagTypehint: %checkMissingVarTagTypehint%
tags:
- phpstan.rules.rule
- -
- class: PHPStan\Rules\PhpDoc\WrongVariableNameInVarTagRule
- arguments:
- checkWrongVarUsage: %featureToggles.wrongVarUsage%
- tags:
- - phpstan.rules.rule
diff --git a/conf/config.level3.neon b/conf/config.level3.neon
index 45c6d129a0..51450cddef 100644
--- a/conf/config.level3.neon
+++ b/conf/config.level3.neon
@@ -2,7 +2,7 @@ includes:
- config.level2.neon
rules:
- - PHPStan\Rules\Arrays\AppendedArrayItemTypeRule
+ - PHPStan\Rules\Arrays\ArrayDestructuringRule
- PHPStan\Rules\Arrays\IterableInForeachRule
- PHPStan\Rules\Arrays\OffsetAccessAssignmentRule
- PHPStan\Rules\Arrays\OffsetAccessAssignOpRule
@@ -10,56 +10,54 @@ rules:
- PHPStan\Rules\Arrays\UnpackIterableInArrayRule
- PHPStan\Rules\Functions\ArrowFunctionReturnTypeRule
- PHPStan\Rules\Functions\ClosureReturnTypeRule
+ - PHPStan\Rules\Functions\ReturnTypeRule
- PHPStan\Rules\Generators\YieldTypeRule
- PHPStan\Rules\Methods\ReturnTypeRule
- PHPStan\Rules\Properties\DefaultValueTypesAssignedToPropertiesRule
+ - PHPStan\Rules\Properties\ReadOnlyPropertyAssignRule
+ - PHPStan\Rules\Properties\ReadOnlyPropertyAssignRefRule
- PHPStan\Rules\Properties\TypesAssignedToPropertiesRule
- PHPStan\Rules\Variables\ThrowTypeRule
- PHPStan\Rules\Variables\VariableCloningRule
-conditionalTags:
- PHPStan\Rules\Arrays\ArrayDestructuringRule:
- phpstan.rules.rule: %featureToggles.arrayDestructuring%
-
parameters:
checkPhpDocMethodSignatures: true
services:
-
- class: PHPStan\Rules\Arrays\AppendedArrayKeyTypeRule
+ class: PHPStan\Rules\Arrays\InvalidKeyInArrayDimFetchRule
arguments:
- checkUnionTypes: %checkUnionTypes%
+ reportMaybes: %reportMaybes%
tags:
- phpstan.rules.rule
-
- class: PHPStan\Rules\Arrays\ArrayDestructuringRule
-
- -
- class: PHPStan\Rules\Arrays\InvalidKeyInArrayDimFetchRule
+ class: PHPStan\Rules\Arrays\InvalidKeyInArrayItemRule
arguments:
reportMaybes: %reportMaybes%
tags:
- phpstan.rules.rule
-
- class: PHPStan\Rules\Arrays\InvalidKeyInArrayItemRule
+ class: PHPStan\Rules\Arrays\NonexistentOffsetInArrayDimFetchRule
arguments:
reportMaybes: %reportMaybes%
tags:
- phpstan.rules.rule
-
- class: PHPStan\Rules\Arrays\NonexistentOffsetInArrayDimFetchRule
+ class: PHPStan\Rules\Exceptions\ThrowsVoidFunctionWithExplicitThrowPointRule
arguments:
- reportMaybes: %reportMaybes%
+ exceptionTypeResolver: @exceptionTypeResolver
+ missingCheckedExceptionInThrows: %exceptions.check.missingCheckedExceptionInThrows%
tags:
- phpstan.rules.rule
-
- class: PHPStan\Rules\Functions\ReturnTypeRule
+ class: PHPStan\Rules\Exceptions\ThrowsVoidMethodWithExplicitThrowPointRule
arguments:
- functionReflector: @betterReflectionFunctionReflector
+ exceptionTypeResolver: @exceptionTypeResolver
+ missingCheckedExceptionInThrows: %exceptions.check.missingCheckedExceptionInThrows%
tags:
- phpstan.rules.rule
diff --git a/conf/config.level4.neon b/conf/config.level4.neon
index 86c6f528c0..61f32bd2f7 100644
--- a/conf/config.level4.neon
+++ b/conf/config.level4.neon
@@ -6,32 +6,22 @@ rules:
- PHPStan\Rules\Comparison\NumberComparisonOperatorsConstantConditionRule
- PHPStan\Rules\DeadCode\NoopRule
- PHPStan\Rules\DeadCode\UnreachableStatementRule
- - PHPStan\Rules\Exceptions\DeadCatchRule
- - PHPStan\Rules\Functions\CallToFunctionStamentWithoutSideEffectsRule
+ - PHPStan\Rules\DeadCode\UnusedPrivateConstantRule
+ - PHPStan\Rules\DeadCode\UnusedPrivateMethodRule
+ - PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule
+ - PHPStan\Rules\Exceptions\OverwrittenExitPointByFinallyRule
+ - PHPStan\Rules\Functions\CallToFunctionStatementWithoutSideEffectsRule
- PHPStan\Rules\Methods\CallToConstructorStatementWithoutSideEffectsRule
- - PHPStan\Rules\Methods\CallToMethodStamentWithoutSideEffectsRule
- - PHPStan\Rules\Methods\CallToStaticMethodStamentWithoutSideEffectsRule
+ - PHPStan\Rules\Methods\CallToMethodStatementWithoutSideEffectsRule
+ - PHPStan\Rules\Methods\CallToStaticMethodStatementWithoutSideEffectsRule
- PHPStan\Rules\Methods\NullsafeMethodCallRule
- PHPStan\Rules\Properties\NullsafePropertyFetchRule
- PHPStan\Rules\TooWideTypehints\TooWideArrowFunctionReturnTypehintRule
- PHPStan\Rules\TooWideTypehints\TooWideClosureReturnTypehintRule
- PHPStan\Rules\TooWideTypehints\TooWideFunctionReturnTypehintRule
-conditionalTags:
- PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule:
- phpstan.rules.rule: %featureToggles.preciseExceptionTracking%
- PHPStan\Rules\Exceptions\OverwrittenExitPointByFinallyRule:
- phpstan.rules.rule: %featureToggles.preciseExceptionTracking%
- PHPStan\Rules\DeadCode\UnusedPrivateConstantRule:
- phpstan.rules.rule: %featureToggles.unusedClassElements%
- PHPStan\Rules\DeadCode\UnusedPrivateMethodRule:
- phpstan.rules.rule: %featureToggles.unusedClassElements%
- PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule:
- phpstan.rules.rule: %featureToggles.unusedClassElements%
- PHPStan\Rules\Variables\IssetRule:
- phpstan.rules.rule: %featureToggles.nullCoalesce%
- PHPStan\Rules\Variables\NullCoalesceRule:
- phpstan.rules.rule: %featureToggles.nullCoalesce%
+parameters:
+ checkAdvancedIsset: true
services:
-
@@ -46,7 +36,6 @@ services:
class: PHPStan\Rules\Comparison\BooleanAndConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
- checkLogicalAndConstantCondition: %featureToggles.checkLogicalAndConstantCondition%
tags:
- phpstan.rules.rule
@@ -54,7 +43,6 @@ services:
class: PHPStan\Rules\Comparison\BooleanOrConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
- checkLogicalOrConstantCondition: %featureToggles.checkLogicalOrConstantCondition%
tags:
- phpstan.rules.rule
@@ -65,18 +53,21 @@ services:
tags:
- phpstan.rules.rule
- -
- class: PHPStan\Rules\DeadCode\UnusedPrivateConstantRule
-
- -
- class: PHPStan\Rules\DeadCode\UnusedPrivateMethodRule
-
-
class: PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule
arguments:
alwaysWrittenTags: %propertyAlwaysWrittenTags%
alwaysReadTags: %propertyAlwaysReadTags%
checkUninitializedProperties: %checkUninitializedProperties%
+ tags:
+ - phpstan.rules.rule
+
+ -
+ class: PHPStan\Rules\Comparison\DoWhileLoopConstantConditionRule
+ arguments:
+ treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
+ tags:
+ - phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\ElseIfConstantConditionRule
@@ -152,10 +143,18 @@ services:
- phpstan.rules.rule
-
- class: PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule
+ class: PHPStan\Rules\Comparison\WhileLoopAlwaysFalseConditionRule
+ arguments:
+ treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
+ tags:
+ - phpstan.rules.rule
-
- class: PHPStan\Rules\Exceptions\OverwrittenExitPointByFinallyRule
+ class: PHPStan\Rules\Comparison\WhileLoopAlwaysTrueConditionRule
+ arguments:
+ treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
+ tags:
+ - phpstan.rules.rule
-
class: PHPStan\Rules\TooWideTypehints\TooWideMethodReturnTypehintRule
@@ -163,9 +162,3 @@ services:
checkProtectedAndPublicMethods: %checkTooWideReturnTypesInProtectedAndPublicMethods%
tags:
- phpstan.rules.rule
-
- -
- class: PHPStan\Rules\Variables\IssetRule
-
- -
- class: PHPStan\Rules\Variables\NullCoalesceRule
diff --git a/conf/config.level5.neon b/conf/config.level5.neon
index 57dc11d4a0..c890be88ec 100644
--- a/conf/config.level5.neon
+++ b/conf/config.level5.neon
@@ -1,21 +1,18 @@
includes:
- config.level4.neon
-conditionalTags:
- PHPStan\Rules\Functions\RandomIntParametersRule:
- phpstan.rules.rule: %featureToggles.randomIntParameters%
- PHPStan\Rules\DateTimeInstantiationRule:
- phpstan.rules.rule: %featureToggles.dateTimeInstantiation%
-
parameters:
checkFunctionArgumentTypes: true
checkArgumentsPassedByReference: true
+rules:
+ - PHPStan\Rules\DateTimeInstantiationRule
+ - PHPStan\Rules\Functions\ImplodeFunctionRule
services:
-
class: PHPStan\Rules\Functions\RandomIntParametersRule
arguments:
reportMaybes: %reportMaybes%
- -
- class: PHPStan\Rules\DateTimeInstantiationRule
+ tags:
+ - phpstan.rules.rule
diff --git a/conf/config.level6.neon b/conf/config.level6.neon
index 4fdeb19345..05f3616832 100644
--- a/conf/config.level6.neon
+++ b/conf/config.level6.neon
@@ -8,6 +8,7 @@ parameters:
checkMissingTypehints: true
rules:
+ - PHPStan\Rules\Constants\MissingClassConstantTypehintRule
- PHPStan\Rules\Functions\MissingFunctionParameterTypehintRule
- PHPStan\Rules\Functions\MissingFunctionReturnTypehintRule
- PHPStan\Rules\Methods\MissingMethodParameterTypehintRule
diff --git a/conf/config.level9.neon b/conf/config.level9.neon
new file mode 100644
index 0000000000..efb3e30ffa
--- /dev/null
+++ b/conf/config.level9.neon
@@ -0,0 +1,5 @@
+includes:
+ - config.level8.neon
+
+parameters:
+ checkExplicitMixed: true
diff --git a/conf/config.levelmax.neon b/conf/config.levelmax.neon
index 789cb34109..da48578fe3 100644
--- a/conf/config.levelmax.neon
+++ b/conf/config.levelmax.neon
@@ -1,2 +1,2 @@
includes:
- - config.level8.neon
+ - config.level9.neon
diff --git a/conf/config.neon b/conf/config.neon
index 6ae19bb2db..0b86120409 100644
--- a/conf/config.neon
+++ b/conf/config.neon
@@ -1,17 +1,17 @@
includes:
- config.stubFiles.neon
parameters:
- bootstrap: null
bootstrapFiles:
- ../stubs/runtime/ReflectionUnionType.php
+ - ../stubs/runtime/ReflectionAttribute.php
- ../stubs/runtime/Attribute.php
+ - ../stubs/runtime/ReflectionIntersectionType.php
excludes_analyse: []
excludePaths: null
- autoload_directories: []
- autoload_files: []
level: null
paths: []
exceptions:
+ implicitThrows: true
uncheckedExceptionRegexes: []
uncheckedExceptionClasses: []
checkedExceptionRegexes: []
@@ -22,30 +22,15 @@ parameters:
featureToggles:
bleedingEdge: false
disableRuntimeReflectionProvider: false
- closureUsesThis: false
- randomIntParameters: false
- nullCoalesce: false
- fileWhitespace: false
- unusedClassElements: false
- readComposerPhpVersion: false
- dateTimeInstantiation: false
- detectDuplicateStubFiles: false
- checkLogicalAndConstantCondition: false
- checkLogicalOrConstantCondition: false
- checkMissingTemplateTypeInParameter: false
- wrongVarUsage: false
- arrayDestructuring: false
- objectFromNewClass: false
skipCheckGenericClasses:
- - RecursiveIterator
- - RecursiveArrayIterator
- rememberFunctionValues: false
- preciseExceptionTracking: false
- apiRules: false
- deepInspectTypes: false
- neverInGenericReturnType: false
+ - DatePeriod
+ - CallbackFilterIterator
+ - FilterIterator
+ - RecursiveCallbackFilterIterator
+ explicitMixedInUnknownGenericNew: false
fileExtensions:
- php
+ checkAdvancedIsset: false
checkAlwaysTrueCheckTypeFunctionCall: false
checkAlwaysTrueInstanceof: false
checkAlwaysTrueStrictComparison: false
@@ -67,28 +52,26 @@ parameters:
checkPhpDocMissingReturn: false
checkPhpDocMethodSignatures: false
checkExtraArguments: false
- checkMissingClosureNativeReturnTypehintRule: false
checkMissingTypehints: false
checkTooWideReturnTypesInProtectedAndPublicMethods: false
checkUninitializedProperties: false
inferPrivatePropertyTypeFromConstructor: false
- implicitThrows: true
reportMaybes: false
reportMaybesInMethodSignatures: false
+ reportMaybesInPropertyPhpDocTypes: false
reportStaticMethodSignatures: false
mixinExcludeClasses: []
scanFiles: []
scanDirectories: []
parallel:
jobSize: 20
- processTimeout: 60.0
+ processTimeout: 600.0
maximumNumberOfProcesses: 32
minimumNumberOfJobsPerProcess: 2
buffer: 134217728 # 128 MB
phpVersion: null
polluteScopeWithLoopInitialAssignments: true
polluteScopeWithAlwaysIterableForeach: true
- polluteCatchScopeWithTryAssignments: false
propertyAlwaysWrittenTags: []
propertyAlwaysReadTags: []
additionalConstructors: []
@@ -111,10 +94,28 @@ parameters:
memoryLimitFile: %tmpDir%/.memory_limit
tempResultCachePath: %tmpDir%/resultCaches
resultCachePath: %tmpDir%/resultCache.php
+ resultCacheChecksProjectExtensionFilesDependencies: false
staticReflectionClassNamePatterns:
- - '#^PhpParser\\#'
- - '#^PHPStan\\#'
- - '#^Hoa\\#'
+ - '#^PhpParser\\#i'
+ - '#^PHPStan\\#i'
+ - '#^Hoa\\#i'
+ - '#^Symfony\\Polyfill\\Php80\\#i'
+ - '#^Symfony\\Polyfill\\Mbstring\\#i'
+ - '#^Symfony\\Polyfill\\Intl\\Normalizer\\#i'
+ - '#^Symfony\\Polyfill\\Php73\\#i'
+ - '#^Symfony\\Polyfill\\Php74\\#i'
+ - '#^Symfony\\Polyfill\\Php72\\#i'
+ - '#^Symfony\\Polyfill\\Intl\\Grapheme\\#i'
+ - '#^Composer\\#i'
+ - '#^ReflectionUnionType$#i'
+ - '#^Attribute$#i'
+ - '#^ReturnTypeWillChange$#i'
+ - '#^ReflectionIntersectionType$#i'
+ - '#^UnitEnum$#i'
+ - '#^BackedEnum$#i'
+ - '#^ReflectionEnum$#i'
+ - '#^ReflectionEnumUnitCase$#i'
+ - '#^ReflectionEnumBackedCase$#i'
dynamicConstantNames:
- ICONV_IMPL
- LIBXML_VERSION
@@ -125,6 +126,9 @@ parameters:
- PHP_RELEASE_VERSION
- PHP_VERSION_ID
- PHP_EXTRA_VERSION
+ - PHP_WINDOWS_VERSION_MAJOR
+ - PHP_WINDOWS_VERSION_MINOR
+ - PHP_WINDOWS_VERSION_BUILD
- PHP_ZTS
- PHP_DEBUG
- PHP_MAXPATHLEN
@@ -156,15 +160,19 @@ parameters:
- PHP_SHLIB_SUFFIX
- PHP_FD_SETSIZE
- OPENSSL_VERSION_NUMBER
+ - ZEND_DEBUG_BUILD
+ - ZEND_THREAD_SAFE
+ customRulesetUsed: null
editorUrl: null
+ __validate: true
extensions:
rules: PHPStan\DependencyInjection\RulesExtension
conditionalTags: PHPStan\DependencyInjection\ConditionalTagsExtension
parametersSchema: PHPStan\DependencyInjection\ParametersSchemaExtension
+ validateIgnoredErrors: PHPStan\DependencyInjection\ValidateIgnoredErrorsExtension
parametersSchema:
- bootstrap: schema(string(), nullable())
bootstrapFiles: listOf(string())
excludes_analyse: listOf(string())
excludePaths: schema(anyOf(
@@ -179,11 +187,10 @@ parametersSchema:
analyseAndScan: listOf(string())
])
), nullable())
- autoload_directories: listOf(string())
- autoload_files: listOf(string())
level: schema(anyOf(int(), string()), nullable())
paths: listOf(string())
exceptions: structure([
+ implicitThrows: bool(),
uncheckedExceptionRegexes: listOf(string()),
uncheckedExceptionClasses: listOf(string()),
checkedExceptionRegexes: listOf(string()),
@@ -196,28 +203,11 @@ parametersSchema:
featureToggles: structure([
bleedingEdge: bool(),
disableRuntimeReflectionProvider: bool(),
- closureUsesThis: bool(),
- randomIntParameters: bool(),
- nullCoalesce: bool(),
- fileWhitespace: bool(),
- unusedClassElements: bool(),
- readComposerPhpVersion: bool(),
- dateTimeInstantiation: bool(),
- detectDuplicateStubFiles: bool(),
- checkLogicalAndConstantCondition: bool(),
- checkLogicalOrConstantCondition: bool(),
- checkMissingTemplateTypeInParameter: bool(),
- wrongVarUsage: bool(),
- arrayDestructuring: bool(),
- objectFromNewClass: bool(),
skipCheckGenericClasses: listOf(string()),
- rememberFunctionValues: bool(),
- preciseExceptionTracking: bool(),
- apiRules: bool(),
- deepInspectTypes: bool(),
- neverInGenericReturnType: bool()
+ explicitMixedInUnknownGenericNew: bool(),
])
fileExtensions: listOf(string())
+ checkAdvancedIsset: bool()
checkAlwaysTrueCheckTypeFunctionCall: bool()
checkAlwaysTrueInstanceof: bool()
checkAlwaysTrueStrictComparison: bool()
@@ -239,15 +229,15 @@ parametersSchema:
checkPhpDocMissingReturn: bool()
checkPhpDocMethodSignatures: bool()
checkExtraArguments: bool()
- checkMissingClosureNativeReturnTypehintRule: bool()
checkMissingTypehints: bool()
checkTooWideReturnTypesInProtectedAndPublicMethods: bool()
checkUninitializedProperties: bool()
inferPrivatePropertyTypeFromConstructor: bool()
- implicitThrows: bool()
+
tipsOfTheDay: bool()
reportMaybes: bool()
reportMaybesInMethodSignatures: bool()
+ reportMaybesInPropertyPhpDocTypes: bool()
reportStaticMethodSignatures: bool()
parallel: structure([
jobSize: int(),
@@ -256,10 +246,9 @@ parametersSchema:
minimumNumberOfJobsPerProcess: int(),
buffer: int()
])
- phpVersion: schema(anyOf(schema(int(), min(70100), max(80099))), nullable())
+ phpVersion: schema(anyOf(schema(int(), min(70100), max(80199))), nullable())
polluteScopeWithLoopInitialAssignments: bool()
polluteScopeWithAlwaysIterableForeach: bool()
- polluteCatchScopeWithTryAssignments: bool()
propertyAlwaysWrittenTags: listOf(string())
propertyAlwaysReadTags: listOf(string())
additionalConstructors: listOf(string())
@@ -299,9 +288,10 @@ parametersSchema:
memoryLimitFile: string()
tempResultCachePath: string()
resultCachePath: string()
+ resultCacheChecksProjectExtensionFilesDependencies: bool()
staticReflectionClassNamePatterns: listOf(string())
dynamicConstantNames: listOf(string())
- customRulesetUsed: bool()
+ customRulesetUsed: schema(bool(), nullable())
rootDir: string()
tmpDir: string()
currentWorkingDirectory: string()
@@ -316,9 +306,10 @@ parametersSchema:
debugMode: bool()
productionMode: bool()
tempDir: string()
+ __validate: bool()
# internal parameters only for DerivativeContainerFactory
- additionalConfigFiles: listOf(string())
+ additionalConfigFiles: arrayOf(string())
generateBaselineFile: schema(string(), nullable())
analysedPaths: listOf(string())
composerAutoloaderProjectPaths: listOf(string())
@@ -353,6 +344,9 @@ services:
-
class: PhpParser\NodeVisitor\NameResolver
+ arguments:
+ options:
+ preserveOriginalNames: true
-
class: PhpParser\NodeVisitor\NodeConnectingVisitor
@@ -377,7 +371,6 @@ services:
class: PHPStan\Php\PhpVersionFactoryFactory
arguments:
versionId: %phpVersion%
- readComposerPhpVersion: %featureToggles.readComposerPhpVersion%
composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths%
-
@@ -406,8 +399,6 @@ services:
-
class: PHPStan\PhpDoc\TypeNodeResolver
- arguments:
- deepInspectTypes: %featureToggles.deepInspectTypes%
-
class: PHPStan\PhpDoc\TypeNodeResolverExtensionRegistryProvider
@@ -427,6 +418,7 @@ services:
-
class: PHPStan\Analyser\FileAnalyser
arguments:
+ parser: @defaultAnalysisParser
reportUnmatchedIgnoredErrors: %reportUnmatchedIgnoredErrors%
-
@@ -445,14 +437,13 @@ services:
-
class: PHPStan\Analyser\NodeScopeResolver
arguments:
- classReflector: @nodeScopeResolverClassReflector
+ parser: @defaultAnalysisParser
+ reflector: @nodeScopeResolverReflector
polluteScopeWithLoopInitialAssignments: %polluteScopeWithLoopInitialAssignments%
- polluteCatchScopeWithTryAssignments: %polluteCatchScopeWithTryAssignments%
polluteScopeWithAlwaysIterableForeach: %polluteScopeWithAlwaysIterableForeach%
earlyTerminatingMethodCalls: %earlyTerminatingMethodCalls%
earlyTerminatingFunctionCalls: %earlyTerminatingFunctionCalls%
- implicitThrows: %implicitThrows%
- preciseExceptionTracking: %featureToggles.preciseExceptionTracking%
+ implicitThrows: %exceptions.implicitThrows%
-
implement: PHPStan\Analyser\ResultCache\ResultCacheManagerFactory
@@ -468,6 +459,7 @@ services:
bootstrapFiles: %bootstrapFiles%
scanFiles: %scanFiles%
scanDirectories: %scanDirectories%
+ checkDependenciesOfProjectExtensionFiles: %resultCacheChecksProjectExtensionFilesDependencies%
-
class: PHPStan\Analyser\ResultCache\ResultCacheClearer
@@ -483,7 +475,6 @@ services:
-
class: PHPStan\Command\AnalyseApplication
arguments:
- memoryLimitFile: %memoryLimitFile%
internalErrorsCountLimit: %internalErrorsCountLimit%
-
@@ -497,21 +488,13 @@ services:
fixerTmpDir: %fixerTmpDir%
maximumNumberOfProcesses: %parallel.maximumNumberOfProcesses%
- -
- class: PHPStan\Command\IgnoredRegexValidator
- arguments:
- parser: @regexParser
-
- -
- class: PHPStan\Dependency\DependencyDumper
- arguments:
- fileFinder: @fileFinderAnalyse
-
-
class: PHPStan\Dependency\DependencyResolver
-
class: PHPStan\Dependency\ExportedNodeFetcher
+ arguments:
+ parser: @defaultAnalysisParser
-
class: PHPStan\Dependency\ExportedNodeResolver
@@ -541,6 +524,9 @@ services:
analysedPathsFromConfig: %analysedPathsFromConfig%
usedLevel: %usedLevel%
generateBaselineFile: %generateBaselineFile%
+ cliAutoloadFile: %cliAutoloadFile%
+ singleReflectionFile: %singleReflectionFile%
+ singleReflectionInsteadOfFile: %singleReflectionInsteadOfFile%
-
class: PHPStan\DependencyInjection\Reflection\ClassReflectionExtensionRegistryProvider
@@ -620,12 +606,6 @@ services:
maximumNumberOfProcesses: %parallel.maximumNumberOfProcesses%
minimumNumberOfJobsPerProcess: %parallel.minimumNumberOfJobsPerProcess%
- -
- class: PHPStan\Parser\CachedParser
- arguments:
- originalParser: @pathRoutingParser
- cachedNodesByStringCountMax: %cache.nodesByStringCountMax%
-
-
class: PHPStan\Parser\FunctionCallStatementFinder
@@ -634,6 +614,8 @@ services:
-
implement: PHPStan\Reflection\FunctionReflectionFactory
+ arguments:
+ parser: @defaultAnalysisParser
-
class: PHPStan\Reflection\Annotations\AnnotationsMethodsClassReflectionExtension
@@ -646,9 +628,13 @@ services:
-
class: PHPStan\Reflection\BetterReflection\SourceLocator\FileNodesFetcher
+ arguments:
+ parser: @defaultAnalysisParser
-
class: PHPStan\Reflection\BetterReflection\SourceLocator\AutoloadSourceLocator
+ arguments:
+ disableRuntimeReflectionProvider: %featureToggles.disableRuntimeReflectionProvider%
-
class: PHPStan\Reflection\BetterReflection\SourceLocator\ComposerJsonAndInstalledJsonSourceLocatorMaker
@@ -687,11 +673,14 @@ services:
-
class: PHPStan\Reflection\Php\PhpClassReflectionExtension
arguments:
+ parser: @defaultAnalysisParser
inferPrivatePropertyTypeFromConstructor: %inferPrivatePropertyTypeFromConstructor%
universalObjectCratesClasses: %universalObjectCratesClasses%
-
implement: PHPStan\Reflection\Php\PhpMethodReflectionFactory
+ arguments:
+ parser: @defaultAnalysisParser
-
class: PHPStan\Reflection\Php\Soap\SoapClientMethodsClassReflectionExtension
@@ -712,7 +701,7 @@ services:
-
class: PHPStan\Reflection\SignatureMap\NativeFunctionReflectionProvider
arguments:
- functionReflector: @betterReflectionFunctionReflector
+ reflector: @betterReflectionReflector
-
class: PHPStan\Reflection\SignatureMap\SignatureMapParser
@@ -798,18 +787,19 @@ services:
checkArgumentsPassedByReference: %checkArgumentsPassedByReference%
checkExtraArguments: %checkExtraArguments%
checkMissingTypehints: %checkMissingTypehints%
- checkNeverInGenericReturnType: %featureToggles.neverInGenericReturnType%
-
class: PHPStan\Rules\FunctionDefinitionCheck
arguments:
checkClassCaseSensitivity: %checkClassCaseSensitivity%
checkThisOnly: %checkThisOnly%
- checkMissingTemplateTypeInParameter: %featureToggles.checkMissingTemplateTypeInParameter%
-
class: PHPStan\Rules\FunctionReturnTypeCheck
+ -
+ class: PHPStan\Rules\Generics\CrossCheckInterfacesHelper
+
-
class: PHPStan\Rules\Generics\GenericAncestorsCheck
arguments:
@@ -829,6 +819,21 @@ services:
-
class: PHPStan\Rules\IssetCheck
+ arguments:
+ checkAdvancedIsset: %checkAdvancedIsset%
+ treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
+
+ -
+ class: PHPStan\Rules\Methods\MethodCallCheck
+ arguments:
+ checkFunctionNameCase: %checkFunctionNameCase%
+ reportMagicMethods: %reportMagicMethods%
+
+ -
+ class: PHPStan\Rules\Methods\StaticMethodCallCheck
+ arguments:
+ checkFunctionNameCase: %checkFunctionNameCase%
+ reportMagicMethods: %reportMagicMethods%
-
# checked as part of OverridingMethodRule
@@ -844,7 +849,6 @@ services:
checkGenericClassInNonGenericObjectType: %checkGenericClassInNonGenericObjectType%
checkMissingCallableSignature: %checkMissingCallableSignature%
skipCheckGenericClasses: %featureToggles.skipCheckGenericClasses%
- deepInspectTypes: %featureToggles.deepInspectTypes%
-
class: PHPStan\Rules\NullsafeCheck
@@ -854,8 +858,6 @@ services:
-
class: PHPStan\Rules\PhpDoc\UnresolvableTypeHelper
- arguments:
- deepInspectTypes: %featureToggles.deepInspectTypes%
-
class: PHPStan\Rules\Properties\LazyReadWritePropertiesExtensionProvider
@@ -882,17 +884,29 @@ services:
-
class: PHPStan\Type\FileTypeMapper
+ arguments:
+ phpParser: @defaultAnalysisParser
-
class: PHPStan\Type\TypeAliasResolver
+ factory: PHPStan\Type\UsefulTypeAliasResolver
arguments:
globalTypeAliases: %typeAliases%
+ -
+ class: PHPStan\Type\TypeAliasResolverProvider
+ factory: PHPStan\Type\LazyTypeAliasResolverProvider
+
-
class: PHPStan\Type\Php\ArgumentBasedFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\ArrayColumnFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\ArrayCombineFunctionReturnTypeExtension
tags:
@@ -918,6 +932,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\ArrayFlipFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\ArrayKeyDynamicReturnTypeExtension
tags:
@@ -953,6 +972,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\ArrayNextDynamicReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\ArrayPopFunctionReturnTypeExtension
tags:
@@ -983,6 +1007,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\ArraySpliceFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\ArraySearchFunctionDynamicReturnTypeExtension
tags:
@@ -1045,6 +1074,16 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\DateFormatFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\DateFormatMethodReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
+
-
class: PHPStan\Type\Php\DateFunctionReturnTypeExtension
tags:
@@ -1095,11 +1134,6 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
- -
- class: PHPStan\Type\Php\GetoptFunctionDynamicReturnTypeExtension
- tags:
- - phpstan.broker.dynamicFunctionReturnTypeExtension
-
-
class: PHPStan\Type\Php\GetParentClassDynamicFunctionReturnTypeExtension
tags:
@@ -1109,11 +1143,6 @@ services:
class: PHPStan\Type\Php\GettimeofdayDynamicFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
- -
- class: PHPStan\Type\Php\HashHmacFunctionsReturnTypeExtension
- tags:
- - phpstan.broker.dynamicFunctionReturnTypeExtension
-
-
class: PHPStan\Type\Php\HashFunctionsReturnTypeExtension
tags:
@@ -1190,6 +1219,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\PregFilterFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\PregSplitDynamicReturnTypeExtension
tags:
@@ -1225,6 +1259,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\MbSubstituteCharacterDynamicReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\MicrotimeFunctionReturnTypeExtension
tags:
@@ -1235,11 +1274,51 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\ImplodeFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\NonEmptyStringFunctionsReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\StrlenFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\StrPadFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\StrRepeatFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\SubstrDynamicReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\ThrowableReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
+
-
class: PHPStan\Type\Php\ParseUrlFunctionDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\TriggerErrorDynamicReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\VersionCompareFunctionDynamicReturnTypeExtension
tags:
@@ -1250,6 +1329,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\RoundFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\StrtotimeFunctionReturnTypeExtension
tags:
@@ -1285,6 +1369,11 @@ services:
tags:
- phpstan.typeSpecifier.functionTypeSpecifyingExtension
+ -
+ class: PHPStan\Type\Php\FunctionExistsFunctionTypeSpecifyingExtension
+ tags:
+ - phpstan.typeSpecifier.functionTypeSpecifyingExtension
+
-
class: PHPStan\Type\Php\InArrayFunctionTypeSpecifyingExtension
tags:
@@ -1345,6 +1434,11 @@ services:
tags:
- phpstan.typeSpecifier.functionTypeSpecifyingExtension
+ -
+ class: PHPStan\Type\Php\IteratorToArrayFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\IsObjectFunctionTypeSpecifyingExtension
tags:
@@ -1365,6 +1459,14 @@ services:
tags:
- phpstan.typeSpecifier.functionTypeSpecifyingExtension
+ -
+ class: PHPStan\Type\Php\IsAFunctionTypeSpecifyingHelper
+
+ -
+ class: PHPStan\Type\Php\ArrayIsListFunctionTypeSpecifyingExtension
+ tags:
+ - phpstan.typeSpecifier.functionTypeSpecifyingExtension
+
-
class: PHPStan\Type\Php\JsonThrowOnErrorDynamicReturnTypeExtension
tags:
@@ -1374,6 +1476,7 @@ services:
class: PHPStan\Type\Php\TypeSpecifyingFunctionsDynamicReturnTypeExtension
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
+ universalObjectCratesClasses: %universalObjectCratesClasses%
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
@@ -1392,13 +1495,18 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\StrTokFunctionReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicFunctionReturnTypeExtension
+
-
class: PHPStan\Type\Php\SprintfFunctionDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
- class: PHPStan\Type\Php\StrvalFunctionReturnTypeExtension
+ class: PHPStan\Type\Php\StrvalFamilyFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
@@ -1413,6 +1521,46 @@ services:
- phpstan.broker.dynamicMethodReturnTypeExtension
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
+ -
+ class: PHPStan\Type\Php\ReflectionGetAttributesMethodReturnTypeExtension
+ arguments:
+ className: ReflectionClass
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\ReflectionGetAttributesMethodReturnTypeExtension
+ arguments:
+ className: ReflectionClassConstant
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\ReflectionGetAttributesMethodReturnTypeExtension
+ arguments:
+ className: ReflectionFunctionAbstract
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\ReflectionGetAttributesMethodReturnTypeExtension
+ arguments:
+ className: ReflectionParameter
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\ReflectionGetAttributesMethodReturnTypeExtension
+ arguments:
+ className: ReflectionProperty
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
+
+ -
+ class: PHPStan\Type\Php\DatePeriodConstructorReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicStaticMethodReturnTypeExtension
+
exceptionTypeResolver:
class: PHPStan\Rules\Exceptions\ExceptionTypeResolver
factory: @PHPStan\Rules\Exceptions\DefaultExceptionTypeResolver
@@ -1464,20 +1612,33 @@ services:
class: PHPStan\Parser\RichParser
arguments:
parser: @currentPhpVersionPhpParser
+ lexer: @currentPhpVersionLexer
autowired: no
currentPhpVersionSimpleParser:
+ class: PHPStan\Parser\CleaningParser
+ arguments:
+ wrappedParser: @currentPhpVersionSimpleDirectParser
+ autowired: no
+
+ currentPhpVersionSimpleDirectParser:
class: PHPStan\Parser\SimpleParser
arguments:
parser: @currentPhpVersionPhpParser
autowired: no
+ defaultAnalysisParser:
+ class: PHPStan\Parser\CachedParser
+ arguments:
+ originalParser: @pathRoutingParser
+ cachedNodesByStringCountMax: %cache.nodesByStringCountMax%
+ autowired: false
+
phpParserDecorator:
class: PHPStan\Parser\PhpParserDecorator
arguments:
- wrappedParser: @PHPStan\Parser\Parser
- autowired:
- - PhpParser\Parser
+ wrappedParser: @defaultAnalysisParser
+ autowired: false
currentPhpVersionLexer:
class: PhpParser\Lexer
@@ -1497,6 +1658,7 @@ services:
stubPhpDocProvider:
class: PHPStan\PhpDoc\StubPhpDocProvider
arguments:
+ parser: @defaultAnalysisParser
stubFiles: %stubFiles%
# Reflection providers
@@ -1518,47 +1680,48 @@ services:
factory: @PHPStan\Reflection\BetterReflection\BetterReflectionSourceLocatorFactory::create
autowired: false
- betterReflectionClassReflector:
- class: PHPStan\Reflection\BetterReflection\Reflector\MemoizingClassReflector
+ originalBetterReflectionReflector:
+ class: PHPStan\BetterReflection\Reflector\DefaultReflector
arguments:
sourceLocator: @betterReflectionSourceLocator
+
+ betterReflectionReflector:
+ class: PHPStan\Reflection\BetterReflection\Reflector\MemoizingReflector
+ arguments:
+ reflector: @originalBetterReflectionReflector
autowired: false
- nodeScopeResolverClassReflector:
- factory: @betterReflectionClassReflector
+ # deprecated
+ betterReflectionClassReflector:
+ class: PHPStan\BetterReflection\Reflector\ClassReflector
+ arguments:
+ sourceLocator: @betterReflectionSourceLocator
autowired: false
+ # deprecated
betterReflectionFunctionReflector:
- class: PHPStan\Reflection\BetterReflection\Reflector\MemoizingFunctionReflector
+ class: PHPStan\BetterReflection\Reflector\FunctionReflector
arguments:
- classReflector: @betterReflectionClassReflector
sourceLocator: @betterReflectionSourceLocator
autowired: false
+ # deprecated
betterReflectionConstantReflector:
- class: PHPStan\Reflection\BetterReflection\Reflector\MemoizingConstantReflector
+ class: PHPStan\BetterReflection\Reflector\ConstantReflector
arguments:
- classReflector: @betterReflectionClassReflector
sourceLocator: @betterReflectionSourceLocator
autowired: false
+ nodeScopeResolverReflector:
+ factory: @betterReflectionReflector
+ autowired: false
+
betterReflectionProvider:
class: PHPStan\Reflection\BetterReflection\BetterReflectionProvider
arguments:
- classReflector: @betterReflectionClassReflector
- functionReflector: @betterReflectionFunctionReflector
- constantReflector: @betterReflectionConstantReflector
+ reflector: @betterReflectionReflector
autowired: false
- regexParser:
- class: Hoa\Compiler\Llk\Parser
- factory: Hoa\Compiler\Llk\Llk::load(@regexGrammarStream)
-
- regexGrammarStream:
- class: Hoa\File\Read
- arguments:
- streamName: 'hoa://Library/Regex/Grammar.pp'
-
runtimeReflectionProvider:
class: PHPStan\Reflection\ReflectionProvider\ClassBlacklistReflectionProvider
arguments:
@@ -1575,8 +1738,6 @@ services:
arguments:
parser: @phpParserDecorator
php8Parser: @php8PhpParser
- autoloadDirectories: %autoload_directories%
- autoloadFiles: %autoload_files%
scanFiles: %scanFiles%
scanDirectories: %scanDirectories%
analysedPaths: %analysedPaths%
@@ -1589,10 +1750,12 @@ services:
implement: PHPStan\Reflection\BetterReflection\BetterReflectionProviderFactory
-
- class: PHPStan\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber
+ class: PHPStan\Reflection\BetterReflection\SourceStubber\PhpStormStubsSourceStubberFactory
arguments:
phpParser: @php8PhpParser
- phpVersionId: %phpVersion%
+
+ -
+ factory: @PHPStan\Reflection\BetterReflection\SourceStubber\PhpStormStubsSourceStubberFactory::create()
autowired:
- PHPStan\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber
@@ -1630,11 +1793,6 @@ services:
errorFormatter.raw:
class: PHPStan\Command\ErrorFormatter\RawErrorFormatter
- errorFormatter.baselineNeon:
- class: PHPStan\Command\ErrorFormatter\BaselineNeonErrorFormatter
- arguments:
- relativePathHelper: @simpleRelativePathHelper
-
errorFormatter.table:
class: PHPStan\Command\ErrorFormatter\TableErrorFormatter
arguments:
@@ -1670,6 +1828,7 @@ services:
class: PHPStan\Command\ErrorFormatter\GithubErrorFormatter
arguments:
relativePathHelper: @simpleRelativePathHelper
+ errorFormatter: @errorFormatter.table
errorFormatter.teamcity:
class: PHPStan\Command\ErrorFormatter\TeamcityErrorFormatter
diff --git a/conf/config.stubFiles.neon b/conf/config.stubFiles.neon
index 158fdc5a6f..7f8c6e81d6 100644
--- a/conf/config.stubFiles.neon
+++ b/conf/config.stubFiles.neon
@@ -1,14 +1,21 @@
parameters:
stubFiles:
+ - ../stubs/ReflectionAttribute.stub
- ../stubs/ReflectionClass.stub
+ - ../stubs/ReflectionClassConstant.stub
+ - ../stubs/ReflectionFunctionAbstract.stub
+ - ../stubs/ReflectionParameter.stub
+ - ../stubs/ReflectionProperty.stub
- ../stubs/iterable.stub
- ../stubs/ArrayObject.stub
- ../stubs/WeakReference.stub
- ../stubs/ext-ds.stub
- ../stubs/PDOStatement.stub
- - ../stubs/ReflectionFunctionAbstract.stub
- ../stubs/date.stub
+ - ../stubs/mysqli.stub
- ../stubs/zip.stub
- ../stubs/dom.stub
- ../stubs/spl.stub
- ../stubs/SplObjectStorage.stub
+ - ../stubs/Exception.stub
+ - ../stubs/arrayFunctions.stub
diff --git a/conf/config.stubValidator.neon b/conf/config.stubValidator.neon
index 86e25998d0..d08129c1d2 100644
--- a/conf/config.stubValidator.neon
+++ b/conf/config.stubValidator.neon
@@ -5,45 +5,30 @@ parameters:
checkMissingIterableValueType: true
checkMissingTypehints: true
checkMissingCallableSignature: false
+ __validate: false
services:
-
class: PHPStan\PhpDoc\StubSourceLocatorFactory
arguments:
- parser: @phpParserDecorator
+ php8Parser: @php8PhpParser
stubFiles: %stubFiles%
nodeScopeResolverClassReflector:
- factory: @stubClassReflector
+ factory: @stubReflector
stubBetterReflectionProvider:
class: PHPStan\Reflection\BetterReflection\BetterReflectionProvider
arguments:
- classReflector: @stubClassReflector
- functionReflector: @stubFunctionReflector
- constantReflector: @stubConstantReflector
+ reflector: @stubReflector
autowired: false
- stubClassReflector:
- class: PHPStan\BetterReflection\Reflector\ClassReflector
+ stubReflector:
+ class: PHPStan\BetterReflection\Reflector\DefaultReflector
arguments:
sourceLocator: @stubSourceLocator
autowired: false
- stubFunctionReflector:
- class: PHPStan\BetterReflection\Reflector\FunctionReflector
- arguments:
- classReflector: @stubClassReflector
- sourceLocator: @stubSourceLocator
- autowired: false
-
- stubConstantReflector:
- class: PHPStan\BetterReflection\Reflector\ConstantReflector
- arguments:
- classReflector: @stubClassReflector
- sourceLocator: @stubSourceLocator
- autowired: false
-
stubSourceLocator:
class: PHPStan\BetterReflection\SourceLocator\Type\SourceLocator
factory: @PHPStan\PhpDoc\StubSourceLocatorFactory::create()
diff --git a/patches/BooleanTypeMapper.patch b/patches/BooleanTypeMapper.patch
new file mode 100644
index 0000000000..dfb247bb93
--- /dev/null
+++ b/patches/BooleanTypeMapper.patch
@@ -0,0 +1,13 @@
+@package rector/rector
+
+--- packages/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php 2021-12-31 13:57:22.000000000 +0100
++++ packages/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php 2022-01-05 00:05:20.000000000 +0100
+@@ -45,7 +45,7 @@
+ }
+ if ($type instanceof \PHPStan\Type\Constant\ConstantBooleanType) {
+ // cannot be parent of union
+- return new \PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode('true');
++ return new \PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode('false');
+ }
+ return new \PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode('bool');
+ }
diff --git a/patches/Buffer.patch b/patches/Buffer.patch
new file mode 100644
index 0000000000..9b2e2c7f86
--- /dev/null
+++ b/patches/Buffer.patch
@@ -0,0 +1,56 @@
+@package hoa/iterator
+
+--- Buffer.php 2017-01-10 11:34:47.000000000 +0100
++++ Buffer.php 2021-10-30 16:36:22.000000000 +0200
+@@ -103,7 +103,7 @@
+ *
+ * @return \Iterator
+ */
+- public function getInnerIterator()
++ public function getInnerIterator(): ?\Iterator
+ {
+ return $this->_iterator;
+ }
+@@ -133,6 +133,7 @@
+ *
+ * @return mixed
+ */
++ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->getBuffer()->current()[self::BUFFER_VALUE];
+@@ -143,6 +144,7 @@
+ *
+ * @return mixed
+ */
++ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->getBuffer()->current()[self::BUFFER_KEY];
+@@ -153,7 +155,7 @@
+ *
+ * @return void
+ */
+- public function next()
++ public function next(): void
+ {
+ $innerIterator = $this->getInnerIterator();
+ $buffer = $this->getBuffer();
+@@ -204,7 +206,7 @@
+ *
+ * @return void
+ */
+- public function rewind()
++ public function rewind(): void
+ {
+ $innerIterator = $this->getInnerIterator();
+ $buffer = $this->getBuffer();
+@@ -228,7 +230,7 @@
+ *
+ * @return bool
+ */
+- public function valid()
++ public function valid(): bool
+ {
+ return
+ $this->getBuffer()->valid() &&
diff --git a/patches/Lookahead.patch b/patches/Lookahead.patch
new file mode 100644
index 0000000000..b6c283492c
--- /dev/null
+++ b/patches/Lookahead.patch
@@ -0,0 +1,54 @@
+@package hoa/iterator
+
+--- Lookahead.php 2017-01-10 11:34:47.000000000 +0100
++++ Lookahead.php 2021-10-30 16:35:30.000000000 +0200
+@@ -93,7 +93,7 @@
+ *
+ * @return \Iterator
+ */
+- public function getInnerIterator()
++ public function getInnerIterator(): ?\Iterator
+ {
+ return $this->_iterator;
+ }
+@@ -103,6 +103,7 @@
+ *
+ * @return mixed
+ */
++ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->_current;
+@@ -113,6 +114,7 @@
+ *
+ * @return mixed
+ */
++ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->_key;
+@@ -123,6 +125,7 @@
+ *
+ * @return void
+ */
++ #[\ReturnTypeWillChange]
+ public function next()
+ {
+ $innerIterator = $this->getInnerIterator();
+@@ -143,6 +146,7 @@
+ *
+ * @return void
+ */
++ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ $out = $this->getInnerIterator()->rewind();
+@@ -156,7 +160,7 @@
+ *
+ * @return bool
+ */
+- public function valid()
++ public function valid(): bool
+ {
+ return $this->_valid;
+ }
diff --git a/patches/NameNodeMapper.patch b/patches/NameNodeMapper.patch
new file mode 100644
index 0000000000..6ca3b0a748
--- /dev/null
+++ b/patches/NameNodeMapper.patch
@@ -0,0 +1,13 @@
+@package rector/rector
+
+--- packages/StaticTypeMapper/PhpParser/NameNodeMapper.php 2021-11-23 18:38:29.000000000 +0100
++++ packages/StaticTypeMapper/PhpParser/NameNodeMapper.php 2021-12-16 23:09:30.000000000 +0100
+@@ -106,7 +106,7 @@
+ }
+ return new \Rector\StaticTypeMapper\ValueObject\Type\ParentObjectWithoutClassType();
+ }
+- return new \PHPStan\Type\ThisType($classReflection);
++ return new \PHPStan\Type\ObjectType($classReflection->getName());
+ }
+ /**
+ * @return \PHPStan\Type\ArrayType|\PHPStan\Type\BooleanType|\PHPStan\Type\Constant\ConstantBooleanType|\PHPStan\Type\FloatType|\PHPStan\Type\IntegerType|\PHPStan\Type\MixedType|\PHPStan\Type\StringType
diff --git a/patches/Node.patch b/patches/Node.patch
new file mode 100644
index 0000000000..303ab36e75
--- /dev/null
+++ b/patches/Node.patch
@@ -0,0 +1,48 @@
+@package hoa/protocol
+
+--- Node/Node.php 2017-01-14 13:26:10.000000000 +0100
++++ Node/Node.php 2021-10-30 16:32:43.000000000 +0200
+@@ -108,7 +108,7 @@
+ * @return \Hoa\Protocol\Protocol
+ * @throws \Hoa\Protocol\Exception
+ */
+- public function offsetSet($name, $node)
++ public function offsetSet($name, $node): void
+ {
+ if (!($node instanceof self)) {
+ throw new Protocol\Exception(
+@@ -141,6 +141,7 @@
+ * @return \Hoa\Protocol\Protocol
+ * @throws \Hoa\Protocol\Exception
+ */
++ #[\ReturnTypeWillChange]
+ public function offsetGet($name)
+ {
+ if (!isset($this[$name])) {
+@@ -160,7 +161,7 @@
+ * @param string $name Node's name.
+ * @return bool
+ */
+- public function offsetExists($name)
++ public function offsetExists($name): bool
+ {
+ return true === array_key_exists($name, $this->_children);
+ }
+@@ -171,7 +172,7 @@
+ * @param string $name Node's name to remove.
+ * @return void
+ */
+- public function offsetUnset($name)
++ public function offsetUnset($name): void
+ {
+ unset($this->_children[$name]);
+
+@@ -365,7 +366,7 @@
+ *
+ * @return \ArrayIterator
+ */
+- public function getIterator()
++ public function getIterator(): \Traversable
+ {
+ return new \ArrayIterator($this->_children);
+ }
diff --git a/patches/PDO.patch b/patches/PDO.patch
new file mode 100644
index 0000000000..51d125c9d0
--- /dev/null
+++ b/patches/PDO.patch
@@ -0,0 +1,14 @@
+@package jetbrains/phpstorm-stubs
+@version dev-master
+
+--- PDO/PDO.php 2021-12-26 15:44:39.000000000 +0100
++++ PDO/PDO.php 2022-01-03 22:54:21.000000000 +0100
+@@ -1415,7 +1415,7 @@
+ * @return array|false if one or more notifications is pending, returns a single row,
+ * with fields message and pid, otherwise FALSE.
+ */
+- public function pgsqlGetNotify(int $fetchMode = PDO::FETCH_DEFAULT, int $timeoutMilliseconds = 0): array|false {}
++ public function pgsqlGetNotify(int $fetchMode = 1, int $timeoutMilliseconds = 0): array|false {}
+
+ /**
+ * (PHP 5 >= 5.6.0, PHP 7, PHP 8)
diff --git a/patches/Rule.patch b/patches/Rule.patch
new file mode 100644
index 0000000000..2efb475fe8
--- /dev/null
+++ b/patches/Rule.patch
@@ -0,0 +1,16 @@
+@package hoa/compiler
+
+--- Llk/Rule/Rule.php 2017-08-08 09:44:07.000000000 +0200
++++ Llk/Rule/Rule.php 2021-10-29 16:42:12.000000000 +0200
+@@ -118,7 +118,10 @@
+ {
+ $this->setName($name);
+ $this->setChildren($children);
+- $this->setNodeId($nodeId);
++
++ if ($nodeId !== null) {
++ $this->setNodeId($nodeId);
++ }
+
+ return;
+ }
diff --git a/patches/SessionHandler.patch b/patches/SessionHandler.patch
new file mode 100644
index 0000000000..598c201951
--- /dev/null
+++ b/patches/SessionHandler.patch
@@ -0,0 +1,23 @@
+@package jetbrains/phpstorm-stubs
+@version dev-master
+
+--- session/SessionHandler.php 2021-11-04 14:27:30.000000000 +0100
++++ session/SessionHandler.php 2021-11-05 11:26:14.000000000 +0100
+@@ -147,7 +147,7 @@
+ *
+ */
+ #[TentativeType]
+- public function validateId(string $id): bool;
++ public function validateId($id): bool;
+
+ /**
+ * Update timestamp of a session
+@@ -163,7 +163,7 @@
+ * @return bool
+ */
+ #[TentativeType]
+- public function updateTimestamp(string $id, string $data): bool;
++ public function updateTimestamp($id, $data): bool;
+ }
+
+ /**
diff --git a/phpcs.xml b/phpcs.xml
index 03130b34b2..a8579080da 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,58 +1,71 @@
+
-
+
src
tests
compiler/src
+ compiler/tests
-
-
+
-
-
+
+
+
-
+
+
-
+
-
+
+
+ 10
+
-
+
+
-
+
+
+ 10
+
+
-
+
-
+
+
+ 10
src/Command/CommandHelper.php
- src/Testing/TestCase.php
+ src/Testing/PHPStanTestCase.php
tests
@@ -62,6 +75,11 @@
+
+
+
+
+
@@ -69,7 +87,13 @@
-
+
+
+
+
+
+
+
@@ -83,7 +107,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -99,7 +137,41 @@
src/Reflection/BetterReflection/SourceLocator/AutoloadSourceLocator.php
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
tests/*/data
+ tests/*/Fixture
tests/e2e/resultCache_1.php
tests/e2e/resultCache_2.php
tests/e2e/resultCache_3.php
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 255ba887d3..2d8e472cdb 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -10,9 +10,19 @@ parameters:
count: 1
path: src/Analyser/MutatingScope.php
+ -
+ message: "#^Parameter \\#2 \\$node of method PHPStan\\\\BetterReflection\\\\SourceLocator\\\\Ast\\\\Strategy\\\\NodeToReflection\\:\\:__invoke\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\ArrowFunction\\|PhpParser\\\\Node\\\\Expr\\\\Closure\\|PhpParser\\\\Node\\\\Expr\\\\FuncCall\\|PhpParser\\\\Node\\\\Stmt\\\\Class_\\|PhpParser\\\\Node\\\\Stmt\\\\Const_\\|PhpParser\\\\Node\\\\Stmt\\\\Enum_\\|PhpParser\\\\Node\\\\Stmt\\\\Function_\\|PhpParser\\\\Node\\\\Stmt\\\\Interface_\\|PhpParser\\\\Node\\\\Stmt\\\\Trait_, PhpParser\\\\Node\\\\Stmt\\\\ClassLike given\\.$#"
+ count: 1
+ path: src/Analyser/NodeScopeResolver.php
+
+ -
+ message: "#^Parameter \\#9 \\$reflection of class PHPStan\\\\Reflection\\\\ClassReflection constructor expects ReflectionClass, object given\\.$#"
+ count: 1
+ path: src/Analyser/NodeScopeResolver.php
+
-
message: "#^Anonymous function has an unused use \\$container\\.$#"
- count: 2
+ count: 1
path: src/Command/CommandHelper.php
-
@@ -20,6 +30,11 @@ parameters:
count: 1
path: src/Command/CommandHelper.php
+ -
+ message: "#^Static property PHPStan\\\\Command\\\\CommandHelper\\:\\:\\$reservedMemory is never read, only written\\.$#"
+ count: 1
+ path: src/Command/CommandHelper.php
+
-
message: "#^Parameter \\#1 \\$headers \\(array\\\\) of method PHPStan\\\\Command\\\\ErrorsConsoleStyle\\:\\:table\\(\\) should be contravariant with parameter \\$headers \\(array\\) of method Symfony\\\\Component\\\\Console\\\\Style\\\\StyleInterface\\:\\:table\\(\\)$#"
count: 1
@@ -46,7 +61,7 @@ parameters:
path: src/Command/FixerApplication.php
-
- message: "#^Call to an undefined method React\\\\Promise\\\\PromiseInterface\\\\:\\:done\\(\\)\\.$#"
+ message: "#^Call to an undefined method React\\\\Promise\\\\PromiseInterface\\\\:\\:done\\(\\)\\.$#"
count: 1
path: src/Command/FixerApplication.php
@@ -90,6 +105,14 @@ parameters:
count: 1
path: src/PhpDoc/ResolvedPhpDocBlock.php
+ -
+ message: """
+ #^Call to deprecated method getInstance\\(\\) of class PHPStan\\\\Broker\\\\Broker\\:
+ Use PHPStan\\\\Reflection\\\\ReflectionProviderStaticAccessor instead$#
+ """
+ count: 1
+ path: src/PhpDoc/StubValidator.php
+
-
message: "#^Return type \\(PHPStan\\\\PhpDoc\\\\Tag\\\\ParamTag\\) of method PHPStan\\\\PhpDoc\\\\Tag\\\\ParamTag\\:\\:withType\\(\\) should be covariant with return type \\(static\\(PHPStan\\\\PhpDoc\\\\Tag\\\\TypedTag\\)\\) of method PHPStan\\\\PhpDoc\\\\Tag\\\\TypedTag\\:\\:withType\\(\\)$#"
count: 1
@@ -116,44 +139,39 @@ parameters:
path: src/Reflection/BetterReflection/BetterReflectionProvider.php
-
- message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
+ message: "#^Parameter \\#9 \\$reflection of class PHPStan\\\\Reflection\\\\ClassReflection constructor expects ReflectionClass, object given\\.$#"
count: 1
- path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php
+ path: src/Reflection/BetterReflection/BetterReflectionProvider.php
-
- message: "#^Only booleans are allowed in a negated boolean, int\\|false given\\.$#"
- count: 1
- path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php
+ message: "#^Parameter \\#2 \\$node of method PHPStan\\\\BetterReflection\\\\SourceLocator\\\\Ast\\\\Strategy\\\\NodeToReflection\\:\\:__invoke\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\ArrowFunction\\|PhpParser\\\\Node\\\\Expr\\\\Closure\\|PhpParser\\\\Node\\\\Expr\\\\FuncCall\\|PhpParser\\\\Node\\\\Stmt\\\\Class_\\|PhpParser\\\\Node\\\\Stmt\\\\Const_\\|PhpParser\\\\Node\\\\Stmt\\\\Enum_\\|PhpParser\\\\Node\\\\Stmt\\\\Function_\\|PhpParser\\\\Node\\\\Stmt\\\\Interface_\\|PhpParser\\\\Node\\\\Stmt\\\\Trait_, PhpParser\\\\Node\\\\Stmt\\\\ClassLike given\\.$#"
+ count: 2
+ path: src/Reflection/BetterReflection/SourceLocator/AutoloadSourceLocator.php
-
- message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
+ message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php
-
- message: "#^Parameter \\#1 \\$haystack of function strrpos expects string, string\\|null given\\.$#"
+ message: "#^Parameter \\#2 \\$node of method PHPStan\\\\BetterReflection\\\\SourceLocator\\\\Ast\\\\Strategy\\\\NodeToReflection\\:\\:__invoke\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\ArrowFunction\\|PhpParser\\\\Node\\\\Expr\\\\Closure\\|PhpParser\\\\Node\\\\Expr\\\\FuncCall\\|PhpParser\\\\Node\\\\Stmt\\\\Class_\\|PhpParser\\\\Node\\\\Stmt\\\\Const_\\|PhpParser\\\\Node\\\\Stmt\\\\Enum_\\|PhpParser\\\\Node\\\\Stmt\\\\Function_\\|PhpParser\\\\Node\\\\Stmt\\\\Interface_\\|PhpParser\\\\Node\\\\Stmt\\\\Trait_, PhpParser\\\\Node\\\\Stmt\\\\ClassLike\\|PhpParser\\\\Node\\\\Stmt\\\\Function_ given\\.$#"
count: 1
path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php
-
- message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|null given\\.$#"
- count: 3
- path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php
-
- -
- message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|null given\\.$#"
+ message: "#^Parameter \\#2 \\$node of method PHPStan\\\\BetterReflection\\\\SourceLocator\\\\Ast\\\\Strategy\\\\NodeToReflection\\:\\:__invoke\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\ArrowFunction\\|PhpParser\\\\Node\\\\Expr\\\\Closure\\|PhpParser\\\\Node\\\\Expr\\\\FuncCall\\|PhpParser\\\\Node\\\\Stmt\\\\Class_\\|PhpParser\\\\Node\\\\Stmt\\\\Const_\\|PhpParser\\\\Node\\\\Stmt\\\\Enum_\\|PhpParser\\\\Node\\\\Stmt\\\\Function_\\|PhpParser\\\\Node\\\\Stmt\\\\Interface_\\|PhpParser\\\\Node\\\\Stmt\\\\Trait_, PhpParser\\\\Node\\\\Stmt\\\\ClassLike given\\.$#"
count: 1
- path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php
+ path: src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php
-
- message: "#^Parameter \\#2 \\$subject of function preg_match_all expects string, string\\|null given\\.$#"
+ message: "#^Only booleans are allowed in &&, int\\|false given on the right side\\.$#"
count: 1
- path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php
+ path: src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php
-
- message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#"
- count: 4
- path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php
+ message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
+ count: 1
+ path: src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php
-
message: "#^Method PHPStan\\\\Reflection\\\\ClassReflection\\:\\:__construct\\(\\) has parameter \\$reflection with generic class ReflectionClass but does not specify its types\\: T$#"
@@ -246,7 +264,7 @@ parameters:
path: src/Rules/Api/ApiTraitUseRule.php
-
- message: "#^Binary operation \"\\+\" between array\\(class\\-string\\\\) and array\\\\|false results in an error\\.$#"
+ message: "#^Binary operation \"\\+\" between array\\{class\\-string\\\\} and array\\\\|false results in an error\\.$#"
count: 1
path: src/Rules/Registry.php
@@ -268,7 +286,23 @@ parameters:
-
message: "#^Anonymous function has an unused use \\$container\\.$#"
count: 1
- path: src/Testing/TestCase.php
+ path: src/Testing/PHPStanTestCase.php
+
+ -
+ message: """
+ #^Call to deprecated method getInstance\\(\\) of class PHPStan\\\\Broker\\\\Broker\\:
+ Use PHPStan\\\\Reflection\\\\ReflectionProviderStaticAccessor instead$#
+ """
+ count: 1
+ path: src/Type/ObjectType.php
+
+ -
+ message: """
+ #^Call to deprecated method getUniversalObjectCratesClasses\\(\\) of class PHPStan\\\\Broker\\\\Broker\\:
+ Inject %%universalObjectCratesClasses%% parameter instead\\.$#
+ """
+ count: 1
+ path: src/Type/ObjectType.php
-
message: "#^Unreachable statement \\- code above always terminates\\.$#"
@@ -305,6 +339,30 @@ parameters:
count: 1
path: tests/PHPStan/Analyser/EvaluationOrderTest.php
+ -
+ message: """
+ #^Call to deprecated method getClass\\(\\) of class PHPStan\\\\Broker\\\\Broker\\:
+ Use PHPStan\\\\Reflection\\\\ReflectionProvider instead$#
+ """
+ count: 1
+ path: tests/PHPStan/Broker/BrokerTest.php
+
+ -
+ message: """
+ #^Call to deprecated method getFunction\\(\\) of class PHPStan\\\\Broker\\\\Broker\\:
+ Use PHPStan\\\\Reflection\\\\ReflectionProvider instead$#
+ """
+ count: 1
+ path: tests/PHPStan/Broker/BrokerTest.php
+
+ -
+ message: """
+ #^Call to deprecated method hasClass\\(\\) of class PHPStan\\\\Broker\\\\Broker\\:
+ Use PHPStan\\\\Reflection\\\\ReflectionProvider instead$#
+ """
+ count: 1
+ path: tests/PHPStan/Broker/BrokerTest.php
+
-
message: "#^Constant SOME_CONSTANT_IN_AUTOLOAD_FILE not found\\.$#"
count: 1
@@ -320,3 +378,35 @@ parameters:
count: 1
path: tests/PHPStan/Node/FileNodeTest.php
+ -
+ message: """
+ #^Instantiation of deprecated class PHPStan\\\\Rules\\\\Arrays\\\\AppendedArrayItemTypeRule\\:
+ Replaced by PHPStan\\\\Rules\\\\Properties\\\\TypesAssignedToPropertiesRule$#
+ """
+ count: 1
+ path: tests/PHPStan/Rules/Arrays/AppendedArrayItemTypeRuleTest.php
+
+ -
+ message: """
+ #^Return type of method PHPStan\\\\Rules\\\\Arrays\\\\AppendedArrayItemTypeRuleTest\\:\\:getRule\\(\\) has typehint with deprecated class PHPStan\\\\Rules\\\\Arrays\\\\AppendedArrayItemTypeRule\\:
+ Replaced by PHPStan\\\\Rules\\\\Properties\\\\TypesAssignedToPropertiesRule$#
+ """
+ count: 1
+ path: tests/PHPStan/Rules/Arrays/AppendedArrayItemTypeRuleTest.php
+
+ -
+ message: """
+ #^Instantiation of deprecated class PHPStan\\\\Rules\\\\Arrays\\\\AppendedArrayKeyTypeRule\\:
+ Replaced by PHPStan\\\\Rules\\\\Properties\\\\TypesAssignedToPropertiesRule$#
+ """
+ count: 1
+ path: tests/PHPStan/Rules/Arrays/AppendedArrayKeyTypeRuleTest.php
+
+ -
+ message: """
+ #^Return type of method PHPStan\\\\Rules\\\\Arrays\\\\AppendedArrayKeyTypeRuleTest\\:\\:getRule\\(\\) has typehint with deprecated class PHPStan\\\\Rules\\\\Arrays\\\\AppendedArrayKeyTypeRule\\:
+ Replaced by PHPStan\\\\Rules\\\\Properties\\\\TypesAssignedToPropertiesRule$#
+ """
+ count: 1
+ path: tests/PHPStan/Rules/Arrays/AppendedArrayKeyTypeRuleTest.php
+
diff --git a/resources/functionMap.php b/resources/functionMap.php
index 0472a4206c..215649b625 100644
--- a/resources/functionMap.php
+++ b/resources/functionMap.php
@@ -57,10 +57,9 @@
return [
'_' => ['string', 'message'=>'string'],
-'__halt_compiler' => ['void'],
-'abs' => ['int', 'number'=>'int'],
+'abs' => ['0|positive-int', 'number'=>'int'],
'abs\'1' => ['float', 'number'=>'float'],
-'abs\'2' => ['float|int', 'number'=>'string'],
+'abs\'2' => ['float|0|positive-int', 'number'=>'string'],
'accelerator_get_configuration' => ['array'],
'accelerator_get_scripts' => ['array'],
'accelerator_get_status' => ['array', 'fetch_scripts'=>'bool'],
@@ -250,7 +249,7 @@
'apd_set_session_trace' => ['void', 'debug_level'=>'int', 'dump_directory='=>'string'],
'apd_set_session_trace_socket' => ['bool', 'tcp_server'=>'string', 'socket_type'=>'int', 'port'=>'int', 'debug_level'=>'int'],
'AppendIterator::__construct' => ['void'],
-'AppendIterator::append' => ['void', 'iterator'=>'iterator'],
+'AppendIterator::append' => ['void', 'iterator'=>'Iterator'],
'AppendIterator::current' => ['mixed'],
'AppendIterator::getArrayIterator' => ['ArrayIterator'],
'AppendIterator::getInnerIterator' => ['iterator'],
@@ -260,10 +259,10 @@
'AppendIterator::rewind' => ['void'],
'AppendIterator::valid' => ['bool'],
'array_change_key_case' => ['array', 'input'=>'array', 'case='=>'int'],
-'array_chunk' => ['array[]', 'input'=>'array', 'size'=>'int', 'preserve_keys='=>'bool'],
+'array_chunk' => ['array[]', 'input'=>'array', 'size'=>'positive-int', 'preserve_keys='=>'bool'],
'array_column' => ['array', 'array'=>'array', 'column_key'=>'mixed', 'index_key='=>'mixed'],
'array_combine' => ['array|false', 'keys'=>'array', 'values'=>'array'],
-'array_count_values' => ['int[]', 'input'=>'array'],
+'array_count_values' => ['array', 'input'=>'array'],
'array_diff' => ['array', 'arr1'=>'array', 'arr2'=>'array', '...args='=>'array'],
'array_diff_assoc' => ['array', 'arr1'=>'array', 'arr2'=>'array', '...args='=>'array'],
'array_diff_key' => ['array', 'arr1'=>'array', 'arr2'=>'array', '...args='=>'array'],
@@ -286,7 +285,7 @@
'array_key_first' => ['int|string|null', 'array' => 'array'],
'array_key_last' => ['int|string|null', 'array' => 'array'],
'array_keys' => ['array', 'input'=>'array', 'search_value='=>'mixed', 'strict='=>'bool'],
-'array_map' => ['array', 'callback'=>'?callable', 'input1'=>'array', '...args='=>'array'],
+'array_map' => ['array', 'callback'=>'?callable', 'array'=>'array', '...args='=>'array'],
'array_merge' => ['array', 'arr1'=>'array', '...args='=>'array'],
'array_merge_recursive' => ['array', 'arr1'=>'array', '...args='=>'array'],
'array_multisort' => ['bool', '&rw_array1'=>'array', 'array1_sort_order='=>'array|int', 'array1_sort_flags='=>'array|int', '...args='=>'array|int'],
@@ -303,7 +302,7 @@
'array_search' => ['int|string|false', 'needle'=>'mixed', 'haystack'=>'array', 'strict='=>'bool'],
'array_shift' => ['mixed', '&rw_stack'=>'array'],
'array_slice' => ['array', 'input'=>'array', 'offset'=>'int', 'length='=>'?int', 'preserve_keys='=>'bool'],
-'array_splice' => ['array', '&rw_input'=>'array', 'offset'=>'int', 'length='=>'int', 'replacement='=>'array|string'],
+'array_splice' => ['array', '&rw_input'=>'array', 'offset'=>'int', 'length='=>'int', 'replacement='=>'mixed'],
'array_sum' => ['int|float', 'input'=>'array'],
'array_udiff' => ['array', 'arr1'=>'array', 'arr2'=>'array', 'data_comp_func'=>'callable(mixed,mixed):int'],
'array_udiff\'1' => ['array', 'arr1'=>'array', 'arr2'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'],
@@ -318,10 +317,10 @@
'array_uintersect_uassoc' => ['array', 'arr1'=>'array', 'arr2'=>'array', 'data_compare_func'=>'callable(mixed,mixed):int', 'key_compare_func'=>'callable(mixed,mixed):int'],
'array_uintersect_uassoc\'1' => ['array', 'arr1'=>'array', 'arr2'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', 'arg5'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'],
'array_unique' => ['array', 'array'=>'array', 'flags='=>'int'],
-'array_unshift' => ['int', '&rw_stack'=>'array', 'var'=>'mixed', '...vars='=>'mixed'],
+'array_unshift' => ['positive-int', '&rw_stack'=>'array', 'var'=>'mixed', '...vars='=>'mixed'],
'array_values' => ['array', 'input'=>'array'],
-'array_walk' => ['bool', '&rw_input'=>'array', 'callback'=>'callable', 'userdata='=>'mixed'],
-'array_walk_recursive' => ['bool', '&rw_input'=>'array', 'callback'=>'callable', 'userdata='=>'mixed'],
+'array_walk' => ['bool', '&rw_input'=>'array|object', 'callback'=>'callable', 'userdata='=>'mixed'],
+'array_walk_recursive' => ['bool', '&rw_input'=>'array|object', 'callback'=>'callable', 'userdata='=>'mixed'],
'ArrayAccess::offsetExists' => ['bool', 'offset'=>'mixed'],
'ArrayAccess::offsetGet' => ['mixed', 'offset'=>'mixed'],
'ArrayAccess::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
@@ -346,8 +345,8 @@
'ArrayIterator::seek' => ['void', 'position'=>'int'],
'ArrayIterator::serialize' => ['string'],
'ArrayIterator::setFlags' => ['void', 'flags'=>'string'],
-'ArrayIterator::uasort' => ['void', 'cmp_function'=>'callable(mixed,mixed):int'],
-'ArrayIterator::uksort' => ['void', 'cmp_function'=>'callable(array-key,array-key):int'],
+'ArrayIterator::uasort' => ['void', 'callback'=>'callable(mixed,mixed):int'],
+'ArrayIterator::uksort' => ['void', 'callback'=>'callable(array-key,array-key):int'],
'ArrayIterator::unserialize' => ['void', 'serialized'=>'string'],
'ArrayIterator::valid' => ['bool'],
'ArrayObject::__construct' => ['void', 'input='=>'array|object', 'flags='=>'int', 'iterator_class='=>'string'],
@@ -369,8 +368,8 @@
'ArrayObject::serialize' => ['string'],
'ArrayObject::setFlags' => ['void', 'flags'=>'int'],
'ArrayObject::setIteratorClass' => ['void', 'iterator_class'=>'string'],
-'ArrayObject::uasort' => ['void', 'cmp_function'=>'callable'],
-'ArrayObject::uksort' => ['void', 'cmp_function'=>'callable(array-key,array-key):int'],
+'ArrayObject::uasort' => ['void', 'callback'=>'callable'],
+'ArrayObject::uksort' => ['void', 'callback'=>'callable(array-key,array-key):int'],
'ArrayObject::unserialize' => ['void', 'serialized'=>'string'],
'arsort' => ['bool', '&rw_array_arg'=>'array', 'sort_flags='=>'int'],
'asin' => ['float', 'number'=>'float'],
@@ -396,7 +395,7 @@
'BadFunctionCallException::getLine' => ['int'],
'BadFunctionCallException::getMessage' => ['string'],
'BadFunctionCallException::getPrevious' => ['(?Throwable)|(?BadFunctionCallException)'],
-'BadFunctionCallException::getTrace' => ['array'],
+'BadFunctionCallException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'BadFunctionCallException::getTraceAsString' => ['string'],
'BadMethodCallException::__clone' => ['void'],
'BadMethodCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'(?Throwable)|(?BadMethodCallException)'],
@@ -406,7 +405,7 @@
'BadMethodCallException::getLine' => ['int'],
'BadMethodCallException::getMessage' => ['string'],
'BadMethodCallException::getPrevious' => ['(?Throwable)|(?BadMethodCallException)'],
-'BadMethodCallException::getTrace' => ['array'],
+'BadMethodCallException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'BadMethodCallException::getTraceAsString' => ['string'],
'base64_decode' => ['string|false', 'str'=>'string', 'strict='=>'bool'],
'base64_encode' => ['string', 'str'=>'string'],
@@ -492,7 +491,7 @@
'bzopen' => ['resource|false', 'file'=>'string|resource', 'mode'=>'string'],
'bzread' => ['string|false', 'bz'=>'resource', 'length='=>'int'],
'bzwrite' => ['int|false', 'bz'=>'resource', 'data'=>'string', 'length='=>'int'],
-'CachingIterator::__construct' => ['void', 'iterator'=>'iterator', 'flags='=>''],
+'CachingIterator::__construct' => ['void', 'iterator'=>'Iterator', 'flags='=>''],
'CachingIterator::__toString' => ['string'],
'CachingIterator::count' => ['0|positive-int'],
'CachingIterator::current' => ['mixed'],
@@ -925,15 +924,15 @@
'call_user_func_array' => ['mixed', 'function'=>'callable', 'parameters'=>'array'],
'call_user_method' => ['mixed', 'method_name'=>'string', 'obj'=>'object', 'parameter='=>'mixed', '...args='=>'mixed'],
'call_user_method_array' => ['mixed', 'method_name'=>'string', 'obj'=>'object', 'params'=>'array'],
-'CallbackFilterIterator::__construct' => ['void', 'iterator'=>'iterator', 'func'=>'callable'],
+'CallbackFilterIterator::__construct' => ['void', 'iterator'=>'Iterator', 'func'=>'callable'],
'CallbackFilterIterator::accept' => ['bool'],
'CallbackFilterIterator::current' => ['mixed'],
-'CallbackFilterIterator::getInnerIterator' => ['iterator'],
+'CallbackFilterIterator::getInnerIterator' => ['Iterator'],
'CallbackFilterIterator::key' => ['mixed'],
'CallbackFilterIterator::next' => ['void'],
'CallbackFilterIterator::rewind' => ['void'],
'CallbackFilterIterator::valid' => ['bool'],
-'ceil' => ['float', 'number'=>'float'],
+'ceil' => ['float|false', 'number'=>'float'],
'chdb::__construct' => ['void', 'pathname'=>'string'],
'chdb::get' => ['string', 'key'=>'string'],
'chdb_create' => ['bool', 'pathname'=>'string', 'data'=>'array'],
@@ -944,9 +943,9 @@
'chmod' => ['bool', 'filename'=>'string', 'mode'=>'int'],
'chop' => ['string', 'str'=>'string', 'character_mask='=>'string'],
'chown' => ['bool', 'filename'=>'string', 'user'=>'string|int'],
-'chr' => ['string', 'ascii'=>'int'],
+'chr' => ['non-empty-string', 'ascii'=>'int'],
'chroot' => ['bool', 'directory'=>'string'],
-'chunk_split' => ['string', 'str'=>'string', 'chunklen='=>'int', 'ending='=>'string'],
+'chunk_split' => ['string', 'str'=>'string', 'chunklen='=>'positive-int', 'ending='=>'string'],
'class_alias' => ['bool', 'user_class_name'=>'string', 'alias_name'=>'string', 'autoload='=>'bool'],
'class_exists' => ['bool', 'classname'=>'string', 'autoload='=>'bool'],
'class_implements' => ['array|false', 'what'=>'object|string', 'autoload='=>'bool'],
@@ -990,7 +989,7 @@
'ClosedGeneratorException::getLine' => ['int'],
'ClosedGeneratorException::getMessage' => ['string'],
'ClosedGeneratorException::getPrevious' => ['Throwable|ClosedGeneratorException|null'],
-'ClosedGeneratorException::getTrace' => ['array'],
+'ClosedGeneratorException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'ClosedGeneratorException::getTraceAsString' => ['string'],
'closedir' => ['void', 'dir_handle='=>'resource'],
'closelog' => ['bool'],
@@ -1060,7 +1059,7 @@
'componere\cast' => ['Type', 'arg1'=>'', 'object'=>''],
'componere\cast_by_ref' => ['Type', 'arg1'=>'', 'object'=>''],
'confirm_pdo_ibm_compiled' => [''],
-'connection_aborted' => ['int'],
+'connection_aborted' => ['0|1'],
'connection_status' => ['int'],
'connection_timeout' => ['int'],
'constant' => ['mixed', 'const_name'=>'string'],
@@ -1366,7 +1365,7 @@
'Couchbase\zlibCompress' => ['string', 'data'=>'string'],
'Couchbase\zlibDecompress' => ['string', 'data'=>'string'],
'count' => ['0|positive-int', 'var'=>'Countable|array', 'mode='=>'int'],
-'count_chars' => ['mixed', 'input'=>'string', 'mode='=>'int'],
+'count_chars' => ['mixed', 'input'=>'string', 'mode='=>'0|1|2|3|4'],
'Countable::count' => ['0|positive-int'],
'crack_check' => ['bool', 'dictionary'=>'', 'password'=>'string'],
'crack_closedict' => ['bool', 'dictionary='=>'resource'],
@@ -1525,7 +1524,7 @@
'CURLFile::getPostFilename' => ['string'],
'CURLFile::setMimeType' => ['void', 'mime'=>'string'],
'CURLFile::setPostFilename' => ['void', 'name'=>'string'],
-'current' => ['mixed', 'array_arg'=>'array'],
+'current' => ['mixed', 'array_arg'=>'array|object'],
'cyrus_authenticate' => ['void', 'connection'=>'resource', 'mechlist='=>'string', 'service='=>'string', 'user='=>'string', 'minssf='=>'int', 'maxssf='=>'int', 'authname='=>'string', 'password='=>'string'],
'cyrus_bind' => ['bool', 'connection'=>'resource', 'callbacks'=>'array'],
'cyrus_close' => ['bool', 'connection'=>'resource'],
@@ -1549,8 +1548,8 @@
'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'day='=>'int|mixed'],
'date_modify' => ['DateTime|false', 'object'=>'DateTime', 'modify'=>'string'],
'date_offset_get' => ['int', 'obj'=>'DateTimeInterface'],
-'date_parse' => ['array|false', 'date'=>'string'],
-'date_parse_from_format' => ['array', 'format'=>'string', 'date'=>'string'],
+'date_parse' => ['array{year: int|false, month: int|false, day: int|false, hour: int|false, minute: int|false, second: int|false, fraction: float|false, warning_count: int, warnings: string[], error_count: int, errors: string[], is_localtime: bool, zone_type?: int|bool, zone?: int|bool, is_dst?: bool, tz_abbr?: string, tz_id?: string, relative?: array{year: int, month: int, day: int, hour: int, minute: int, second: int, weekday?: int, weekdays?: int, first_day_of_month?: bool, last_day_of_month?: bool}}', 'date'=>'string'],
+'date_parse_from_format' => ['array{year: int|false, month: int|false, day: int|false, hour: int|false, minute: int|false, second: int|false, fraction: float|false, warning_count: int, warnings: string[], error_count: int, errors: string[], is_localtime: bool, zone_type?: int|bool, zone?: int|bool, is_dst?: bool, tz_abbr?: string, tz_id?: string, relative?: array{year: int, month: int, day: int, hour: int, minute: int, second: int, weekday?: int, weekdays?: int, first_day_of_month?: bool, last_day_of_month?: bool}}', 'format'=>'string', 'date'=>'string'],
'date_sub' => ['DateTime|false', 'object'=>'DateTime', 'interval'=>'DateInterval'],
'date_sun_info' => ['array', 'time'=>'int', 'latitude'=>'float', 'longitude'=>'float'],
'date_sunrise' => ['mixed', 'time'=>'int', 'format='=>'int', 'latitude='=>'float', 'longitude='=>'float', 'zenith='=>'float', 'gmt_offset='=>'float'],
@@ -1591,7 +1590,7 @@
'DatePeriod::__construct\'2' => ['void', 'iso'=>'string', 'options='=>'int'],
'DatePeriod::__wakeup' => ['void'],
'DatePeriod::getDateInterval' => ['DateInterval'],
-'DatePeriod::getEndDate' => ['DateTimeInterface'],
+'DatePeriod::getEndDate' => ['?DateTimeInterface'],
'DatePeriod::getStartDate' => ['DateTimeInterface'],
'DateTime::__construct' => ['void', 'time='=>'string', 'timezone='=>'?DateTimeZone'],
'DateTime::__set_state' => ['static', 'array'=>'array'],
@@ -1639,12 +1638,12 @@
'DateTimeZone::__construct' => ['void', 'timezone'=>'string'],
'DateTimeZone::__set_state' => ['DateTimeZone', 'array'=>'array'],
'DateTimeZone::__wakeup' => ['void'],
-'DateTimeZone::getLocation' => ['array'],
+'DateTimeZone::getLocation' => ['array{country_code: string, latitude: float, longitude: float, comments: string}|false'],
'DateTimeZone::getName' => ['string'],
'DateTimeZone::getOffset' => ['int', 'datetime'=>'DateTimeInterface'],
'DateTimeZone::getTransitions' => ['array', 'timestamp_begin='=>'int', 'timestamp_end='=>'int'],
'DateTimeZone::listAbbreviations' => ['array'],
-'DateTimeZone::listIdentifiers' => ['array', 'what='=>'int', 'country='=>'string'],
+'DateTimeZone::listIdentifiers' => ['array', 'what='=>'int', 'country='=>'string'],
'db2_autocommit' => ['mixed', 'connection'=>'resource', 'value='=>'int'],
'db2_bind_param' => ['bool', 'stmt'=>'resource', 'parameter_number'=>'int', 'variable_name'=>'string', 'parameter_type='=>'int', 'data_type='=>'int', 'precision='=>'int', 'scale='=>'int'],
'db2_client_info' => ['object|false', 'connection'=>'resource'],
@@ -1787,7 +1786,7 @@
'dcgettext' => ['string', 'domain_name'=>'string', 'msgid'=>'string', 'category'=>'int'],
'dcngettext' => ['string', 'domain'=>'string', 'msgid1'=>'string', 'msgid2'=>'string', 'n'=>'int', 'category'=>'int'],
'deaggregate' => ['', 'object'=>'object', 'class_name='=>'string'],
-'debug_backtrace' => ['array', 'options='=>'int|bool', 'limit='=>'int'],
+'debug_backtrace' => ['list', 'options='=>'int|bool', 'limit='=>'int'],
'debug_print_backtrace' => ['void', 'options='=>'int|bool', 'limit='=>'int'],
'debug_zval_dump' => ['void', '...var'=>'mixed'],
'debugger_connect' => [''],
@@ -1855,7 +1854,7 @@
'DirectoryIterator::setFileClass' => ['void', 'class_name='=>'string'],
'DirectoryIterator::setInfoClass' => ['void', 'class_name='=>'string'],
'DirectoryIterator::valid' => ['bool'],
-'dirname' => ['string', 'path'=>'string', 'levels='=>'int'],
+'dirname' => ['string', 'path'=>'string', 'levels='=>'positive-int'],
'disk_free_space' => ['float|false', 'path'=>'string'],
'disk_total_space' => ['float|false', 'path'=>'string'],
'diskfreespace' => ['float|false', 'path'=>'string'],
@@ -1884,7 +1883,7 @@
'DomainException::getLine' => ['int'],
'DomainException::getMessage' => ['string'],
'DomainException::getPrevious' => ['Throwable|DomainException|null'],
-'DomainException::getTrace' => ['array'],
+'DomainException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'DomainException::getTraceAsString' => ['string'],
'DOMAttr::__construct' => ['void', 'name'=>'string', 'value='=>'string'],
'DOMAttr::isId' => ['bool'],
@@ -1922,7 +1921,7 @@
'DOMDocument::registerNodeClass' => ['bool', 'baseclass'=>'string', 'extendedclass'=>'string'],
'DOMDocument::relaxNGValidate' => ['bool', 'filename'=>'string'],
'DOMDocument::relaxNGValidateSource' => ['bool', 'source'=>'string'],
-'DOMDocument::save' => ['int', 'filename'=>'string', 'options='=>'int'],
+'DOMDocument::save' => ['int|false', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::saveHTML' => ['string|false', 'node='=>'?DOMNode'],
'DOMDocument::saveHTMLFile' => ['int|false', 'filename'=>'string'],
'DOMDocument::saveXML' => ['string|false', 'node='=>'?DOMNode', 'options='=>'int'],
@@ -2172,6 +2171,7 @@
'Ds\Set::join' => ['string', 'glue='=>'string'],
'Ds\Set::jsonSerialize' => ['array'],
'Ds\Set::last' => ['mixed'],
+'Ds\Set::map' => ['Ds\Set', 'callback='=>'callable'],
'Ds\Set::merge' => ['Ds\Set', 'values'=>'mixed'],
'Ds\Set::reduce' => ['mixed', 'callback'=>'callable', 'initial='=>'mixed'],
'Ds\Set::remove' => ['void', '...values='=>'mixed'],
@@ -2233,7 +2233,6 @@
'each' => ['array', '&rw_arr'=>'array'],
'easter_date' => ['int', 'year='=>'int'],
'easter_days' => ['int', 'year='=>'int', 'method='=>'int'],
-'echo' => ['void', 'arg1'=>'string', '...args='=>'string'],
'eio_busy' => ['resource', 'delay'=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'],
'eio_cancel' => ['void', 'req'=>'resource'],
'eio_chmod' => ['resource', 'path'=>'string', 'mode'=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'],
@@ -2293,7 +2292,6 @@
'eio_unlink' => ['resource', 'path'=>'string', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'],
'eio_utime' => ['resource', 'path'=>'string', 'atime'=>'float', 'mtime'=>'float', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'],
'eio_write' => ['resource', 'fd'=>'mixed', 'str'=>'string', 'length='=>'int', 'offset='=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'],
-'empty' => ['bool', 'var'=>'mixed'],
'EmptyIterator::current' => ['mixed'],
'EmptyIterator::key' => ['mixed'],
'EmptyIterator::next' => ['void'],
@@ -2333,7 +2331,7 @@
'Error::getLine' => ['int'],
'Error::getMessage' => ['string'],
'Error::getPrevious' => ['Throwable|Error|null'],
-'Error::getTrace' => ['array'],
+'Error::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'Error::getTraceAsString' => ['string'],
'error_clear_last' => ['void'],
'error_get_last' => ['?array{type:int,message:string,file:string,line:int}'],
@@ -2348,7 +2346,7 @@
'ErrorException::getMessage' => ['string'],
'ErrorException::getPrevious' => ['Throwable|ErrorException|null'],
'ErrorException::getSeverity' => ['int'],
-'ErrorException::getTrace' => ['array'],
+'ErrorException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'ErrorException::getTraceAsString' => ['string'],
'escapeshellarg' => ['string', 'arg'=>'string'],
'escapeshellcmd' => ['string', 'command'=>'string'],
@@ -2369,7 +2367,6 @@
'Ev::suspend' => ['void'],
'Ev::time' => ['float'],
'Ev::verify' => ['void'],
-'eval' => ['mixed', 'code_str'=>'string'],
'EvCheck::__construct' => ['void', 'callback'=>'callable', 'data='=>'mixed', 'priority='=>'int'],
'EvCheck::createStopped' => ['object', 'callback'=>'string', 'data='=>'string', 'priority='=>'string'],
'EvChild::__construct' => ['void', 'pid'=>'int', 'trace'=>'bool', 'callback'=>'callable', 'data='=>'mixed', 'priority='=>'int'],
@@ -2628,18 +2625,17 @@
'Exception::getLine' => ['int'],
'Exception::getMessage' => ['string'],
'Exception::getPrevious' => ['(?Throwable)|(?Exception)'],
-'Exception::getTrace' => ['array'],
+'Exception::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'Exception::getTraceAsString' => ['string'],
'exec' => ['string', 'command'=>'string', '&w_output='=>'array', '&w_return_value='=>'int'],
'exif_imagetype' => ['int|false', 'imagefile'=>'string'],
'exif_read_data' => ['array|false', 'filename'=>'string|resource', 'sections_needed='=>'string', 'sub_arrays='=>'bool', 'read_thumbnail='=>'bool'],
'exif_tagname' => ['string|false', 'index'=>'int'],
'exif_thumbnail' => ['string|false', 'filename'=>'string', '&w_width='=>'int', '&w_height='=>'int', '&w_imagetype='=>'int'],
-'exit' => ['', 'status'=>'string|int'],
'exp' => ['float', 'number'=>'float'],
'expect_expectl' => ['int', 'expect'=>'resource', 'cases'=>'array', 'match='=>'array'],
'expect_popen' => ['resource|false', 'command'=>'string'],
-'explode' => ['array|false', 'separator'=>'string', 'str'=>'string', 'limit='=>'int'],
+'explode' => ['non-empty-array|false', 'separator'=>'string', 'str'=>'string', 'limit='=>'int'],
'expm1' => ['float', 'number'=>'float'],
'extension_loaded' => ['bool', 'extension_name'=>'string'],
'extract' => ['int', '&rw_var_array'=>'array', 'extract_type='=>'int', 'prefix='=>'string|null'],
@@ -2937,13 +2933,13 @@
'ffmpeg_movie::hasAudio' => ['bool'],
'ffmpeg_movie::hasVideo' => ['bool'],
'fgetc' => ['string|false', 'fp'=>'resource'],
-'fgetcsv' => ['(?array)|(?false)', 'fp'=>'resource', 'length='=>'int', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'],
-'fgets' => ['string|false', 'fp'=>'resource', 'length='=>'int'],
-'fgetss' => ['string|false', 'fp'=>'resource', 'length='=>'int', 'allowable_tags='=>'string'],
+'fgetcsv' => ['(?array)|(?false)', 'fp'=>'resource', 'length='=>'0|positive-int', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'],
+'fgets' => ['string|false', 'fp'=>'resource', 'length='=>'0|positive-int'],
+'fgetss' => ['string|false', 'fp'=>'resource', 'length='=>'0|positive-int', 'allowable_tags='=>'string'],
'file' => ['array|false', 'filename'=>'string', 'flags='=>'int', 'context='=>'resource'],
'file_exists' => ['bool', 'filename'=>'string'],
-'file_get_contents' => ['string|false', 'filename'=>'string', 'use_include_path='=>'bool', 'context='=>'?resource', 'offset='=>'int', 'maxlen='=>'int'],
-'file_put_contents' => ['int|false', 'file'=>'string', 'data'=>'mixed', 'flags='=>'int', 'context='=>'?resource'],
+'file_get_contents' => ['string|false', 'filename'=>'string', 'use_include_path='=>'bool', 'context='=>'?resource', 'offset='=>'int', 'maxlen='=>'0|positive-int'],
+'file_put_contents' => ['0|positive-int|false', 'file'=>'string', 'data'=>'mixed', 'flags='=>'int', 'context='=>'?resource'],
'fileatime' => ['int|false', 'filename'=>'string'],
'filectime' => ['int|false', 'filename'=>'string'],
'filegroup' => ['int|false', 'filename'=>'string'],
@@ -2958,7 +2954,7 @@
'filepro_fieldwidth' => ['int', 'field_number'=>'int'],
'filepro_retrieve' => ['string', 'row_number'=>'int', 'field_number'=>'int'],
'filepro_rowcount' => ['int'],
-'filesize' => ['int|false', 'filename'=>'string'],
+'filesize' => ['0|positive-int|false', 'filename'=>'string'],
'FilesystemIterator::__construct' => ['void', 'path'=>'string', 'flags='=>'int'],
'FilesystemIterator::current' => ['string|SplFileInfo'],
'FilesystemIterator::getFlags' => ['int'],
@@ -2974,7 +2970,7 @@
'filter_list' => ['array'],
'filter_var' => ['mixed', 'variable'=>'mixed', 'filter='=>'int', 'options='=>'mixed'],
'filter_var_array' => ['array|false|null', 'data'=>'array', 'definition='=>'mixed', 'add_empty='=>'bool'],
-'FilterIterator::__construct' => ['void', 'iterator'=>'iterator'],
+'FilterIterator::__construct' => ['void', 'iterator'=>'Iterator'],
'FilterIterator::accept' => ['bool'],
'FilterIterator::current' => ['mixed'],
'FilterIterator::getInnerIterator' => ['Iterator'],
@@ -2994,23 +2990,23 @@
'finfo_set_flags' => ['bool', 'finfo'=>'resource', 'options'=>'int'],
'floatval' => ['float', 'var'=>'mixed'],
'flock' => ['bool', 'fp'=>'resource', 'operation'=>'int', '&w_wouldblock='=>'int'],
-'floor' => ['float', 'number'=>'float'],
+'floor' => ['float|false', 'number'=>'float'],
'flush' => ['void'],
'fmod' => ['float', 'x'=>'float', 'y'=>'float'],
'fnmatch' => ['bool', 'pattern'=>'string', 'filename'=>'string', 'flags='=>'int'],
'fopen' => ['resource|false', 'filename'=>'string', 'mode'=>'string', 'use_include_path='=>'bool', 'context='=>'resource'],
'forward_static_call' => ['mixed', 'function'=>'callable', '...parameters='=>'mixed'],
'forward_static_call_array' => ['mixed', 'function'=>'callable', 'parameters'=>'array'],
-'fpassthru' => ['int|false', 'fp'=>'resource'],
+'fpassthru' => ['0|positive-int|false', 'fp'=>'resource'],
'fpm_get_status' => ['array|false'],
'fprintf' => ['int', 'stream'=>'resource', 'format'=>'string', '...values='=>'string|int|float'],
-'fputcsv' => ['int|false', 'fp'=>'resource', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape_char='=>'string'],
-'fputs' => ['int|false', 'fp'=>'resource', 'str'=>'string', 'length='=>'int'],
-'fread' => ['string|false', 'fp'=>'resource', 'length'=>'int'],
+'fputcsv' => ['0|positive-int|false', 'fp'=>'resource', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape_char='=>'string'],
+'fputs' => ['0|positive-int|false', 'fp'=>'resource', 'str'=>'string', 'length='=>'0|positive-int'],
+'fread' => ['string|false', 'fp'=>'resource', 'length'=>'0|positive-int'],
'frenchtojd' => ['int', 'month'=>'int', 'day'=>'int', 'year'=>'int'],
'fribidi_log2vis' => ['string', 'str'=>'string', 'direction'=>'string', 'charset'=>'int'],
'fscanf' => ['array|int|false', 'stream'=>'resource', 'format'=>'string', '&...w_vars='=>'string|int|float|null'],
-'fseek' => ['int', 'fp'=>'resource', 'offset'=>'int', 'whence='=>'int'],
+'fseek' => ['0|-1', 'fp'=>'resource', 'offset'=>'int', 'whence='=>'int'],
'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_errno='=>'int', '&w_errstr='=>'string', 'timeout='=>'float'],
'fstat' => ['array|false', 'fp'=>'resource'],
'ftell' => ['int|false', 'fp'=>'resource'],
@@ -3050,12 +3046,12 @@
'ftp_size' => ['int', 'stream'=>'resource', 'filename'=>'string'],
'ftp_ssl_connect' => ['resource|false', 'host'=>'string', 'port='=>'int', 'timeout='=>'int'],
'ftp_systype' => ['string|false', 'stream'=>'resource'],
-'ftruncate' => ['bool', 'fp'=>'resource', 'size'=>'int'],
-'func_get_arg' => ['mixed', 'arg_num'=>'int'],
+'ftruncate' => ['bool', 'fp'=>'resource', 'size'=>'0|positive-int'],
+'func_get_arg' => ['mixed', 'arg_num'=>'0|positive-int'],
'func_get_args' => ['array'],
-'func_num_args' => ['int'],
+'func_num_args' => ['0|positive-int'],
'function_exists' => ['bool', 'function_name'=>'string'],
-'fwrite' => ['int|false', 'fp'=>'resource', 'str'=>'string', 'length='=>'int'],
+'fwrite' => ['0|positive-int|false', 'fp'=>'resource', 'str'=>'string', 'length='=>'0|positive-int'],
'gc_collect_cycles' => ['int'],
'gc_disable' => ['void'],
'gc_enable' => ['void'],
@@ -3311,23 +3307,23 @@
'get_defined_constants' => ['array', 'categorize='=>'bool'],
'get_defined_functions' => ['array>', 'exclude_disabled='=>'bool'],
'get_defined_vars' => ['array'],
-'get_extension_funcs' => ['array|false', 'extension_name'=>'string'],
+'get_extension_funcs' => ['list|false', 'extension_name'=>'string'],
'get_headers' => ['array|false', 'url'=>'string', 'format='=>'int', 'context='=>'resource'],
'get_html_translation_table' => ['array', 'table='=>'int', 'flags='=>'int', 'encoding='=>'string'],
'get_include_path' => ['string|false'],
-'get_included_files' => ['array'],
-'get_loaded_extensions' => ['array', 'zend_extensions='=>'bool'],
-'get_magic_quotes_gpc' => ['bool'],
-'get_magic_quotes_runtime' => ['bool'],
+'get_included_files' => ['list'],
+'get_loaded_extensions' => ['list', 'zend_extensions='=>'bool'],
+'get_magic_quotes_gpc' => ['false'],
+'get_magic_quotes_runtime' => ['false'],
'get_meta_tags' => ['array|false', 'filename'=>'string', 'use_include_path='=>'bool'],
'get_object_vars' => ['array', 'obj'=>'object'],
'get_parent_class' => ['class-string|false', 'object='=>'mixed'],
-'get_required_files' => ['string[]'],
+'get_required_files' => ['list'],
'get_resource_type' => ['string', 'res'=>'resource'],
-'get_resources' => ['resource[]', 'resource_type'=>'string'],
+'get_resources' => ['array', 'type='=>'string'],
'getallheaders' => ['array'],
'getcwd' => ['string|false'],
-'getdate' => ['array', 'timestamp='=>'int'],
+'getdate' => ['array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int'],
'getenv' => ['string|false', 'varname'=>'string', 'local_only='=>'bool'],
'getenv\'1' => ['string[]'],
'gethostbyaddr' => ['string|false', 'ip_address'=>'string'],
@@ -3342,7 +3338,7 @@
'getmyinode' => ['int|false'],
'getmypid' => ['int|false'],
'getmyuid' => ['int|false'],
-'getopt' => ['array|array|array>|false', 'options'=>'string', 'longopts='=>'array', '&w_optind='=>'int'],
+'getopt' => ['__benevolent|array|array>|false>', 'options'=>'string', 'longopts='=>'array', '&w_optind='=>'int'],
'getprotobyname' => ['int|false', 'name'=>'string'],
'getprotobynumber' => ['string|false', 'proto'=>'int'],
'getrandmax' => ['int'],
@@ -3550,7 +3546,7 @@
'gmp_clrbit' => ['void', 'a'=>'GMP|string|int', 'index'=>'int'],
'gmp_cmp' => ['int', 'a'=>'GMP|string|int', 'b'=>'GMP|string|int'],
'gmp_com' => ['GMP', 'a'=>'GMP|string|int'],
-'gmp_div' => ['GMP', 'a'=>'GMP|resource|string', 'b'=>'GMP|resource|string', 'round='=>'int'],
+'gmp_div' => ['GMP', 'a'=>'GMP|string|int', 'b'=>'GMP|string|int', 'round='=>'int'],
'gmp_div_q' => ['GMP', 'a'=>'GMP|string|int', 'b'=>'GMP|string|int', 'round='=>'int'],
'gmp_div_qr' => ['array', 'a'=>'GMP|string|int', 'b'=>'GMP|string|int', 'round='=>'int'],
'gmp_div_r' => ['GMP', 'a'=>'GMP|string|int', 'b'=>'GMP|string|int', 'round='=>'int'],
@@ -3616,7 +3612,7 @@
'gnupg::seterrormode' => ['void', 'errormode'=>'int'],
'gnupg::setsignmode' => ['bool', 'signmode'=>'int'],
'gnupg::sign' => ['string', 'plaintext'=>'string'],
-'gnupg::verify' => ['array', 'signed_text'=>'string', 'signature'=>'string', '&plaintext='=>'string'],
+'gnupg::verify' => ['array', 'signed_text'=>'string', 'signature'=>'string|false', '&plaintext='=>'string'],
'gnupg_adddecryptkey' => ['bool', 'identifier'=>'resource', 'fingerprint'=>'string', 'passphrase'=>'string'],
'gnupg_addencryptkey' => ['bool', 'identifier'=>'resource', 'fingerprint'=>'string'],
'gnupg_addsignkey' => ['bool', 'identifier'=>'resource', 'fingerprint'=>'string', 'passphrase='=>'string'],
@@ -3637,12 +3633,12 @@
'gnupg_seterrormode' => ['void', 'identifier'=>'resource', 'errormode'=>'int'],
'gnupg_setsignmode' => ['bool', 'identifier'=>'resource', 'signmode'=>'int'],
'gnupg_sign' => ['string', 'identifier'=>'resource', 'plaintext'=>'string'],
-'gnupg_verify' => ['array', 'identifier'=>'resource', 'signed_text'=>'string', 'signature'=>'string', 'plaintext='=>'string'],
+'gnupg_verify' => ['array', 'identifier'=>'resource', 'signed_text'=>'string', 'signature'=>'string|false', '&plaintext='=>'string'],
'gopher_parsedir' => ['array', 'dirent'=>'string'],
'grapheme_extract' => ['string|false', 'str'=>'string', 'size'=>'int', 'extract_type='=>'int', 'start='=>'int', '&w_next='=>'int'],
'grapheme_stripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
'grapheme_stristr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'part='=>'bool'],
-'grapheme_strlen' => ['int|false', 'str'=>'string'],
+'grapheme_strlen' => ['0|positive-int|false', 'str'=>'string'],
'grapheme_strpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
'grapheme_strripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
'grapheme_strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
@@ -3665,7 +3661,7 @@
'Grpc\ChannelCredentials::createComposite' => ['Grpc\ChannelCredentials', 'cred1'=>'Grpc\ChannelCredentials', 'cred2'=>'Grpc\CallCredentials'],
'Grpc\ChannelCredentials::createDefault' => ['Grpc\ChannelCredentials'],
'Grpc\ChannelCredentials::createInsecure' => ['null'],
-'Grpc\ChannelCredentials::createSsl' => ['Grpc\ChannelCredentials', 'pem_root_certs'=>'string', 'pem_private_key='=>'string', 'pem_cert_chain='=>'string'],
+'Grpc\ChannelCredentials::createSsl' => ['Grpc\ChannelCredentials', 'pem_root_certs='=>'string|null', 'pem_private_key='=>'string|null', 'pem_cert_chain='=>'string|null'],
'Grpc\ChannelCredentials::setDefaultRootsPem' => ['', 'pem_roots'=>'string'],
'Grpc\Server::__construct' => ['void', 'args'=>'array'],
'Grpc\Server::addHttp2Port' => ['bool', 'addr'=>'string'],
@@ -3727,8 +3723,8 @@
'gzdecode' => ['string|false', 'data'=>'string', 'length='=>'int'],
'gzdeflate' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'],
'gzencode' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding_mode='=>'int'],
-'gzeof' => ['int', 'zp'=>'resource'],
-'gzfile' => ['array|false', 'filename'=>'string', 'use_include_path='=>'int'],
+'gzeof' => ['bool', 'zp'=>'resource'],
+'gzfile' => ['list|false', 'filename'=>'string', 'use_include_path='=>'int'],
'gzgetc' => ['string|false', 'zp'=>'resource'],
'gzgets' => ['string|false', 'zp'=>'resource', 'length='=>'int'],
'gzgetss' => ['string|false', 'zp'=>'resource', 'length'=>'int', 'allowable_tags='=>'string'],
@@ -3906,18 +3902,18 @@
'HaruPage::stroke' => ['bool', 'close_path='=>'bool'],
'HaruPage::textOut' => ['bool', 'x'=>'float', 'y'=>'float', 'text'=>'string'],
'HaruPage::textRect' => ['bool', 'left'=>'float', 'top'=>'float', 'right'=>'float', 'bottom'=>'float', 'text'=>'string', 'align='=>'int'],
-'hash' => ['string|false', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
-'hash_algos' => ['array'],
+'hash' => ['non-empty-string|false', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
+'hash_algos' => ['array'],
'hash_copy' => ['HashContext', 'context'=>'HashContext'],
'hash_equals' => ['bool', 'known_string'=>'string', 'user_string'=>'string'],
-'hash_file' => ['string|false', 'algo'=>'string', 'filename'=>'string', 'raw_output='=>'bool'],
-'hash_final' => ['string', 'context'=>'HashContext', 'raw_output='=>'bool'],
-'hash_hkdf' => ['string', 'algo'=>'string', 'ikm'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
-'hash_hmac' => ['string|false', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
-'hash_hmac_algos' => ['array'],
-'hash_hmac_file' => ['string|false', 'algo'=>'string', 'filename'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
+'hash_file' => ['non-empty-string|false', 'algo'=>'string', 'filename'=>'string', 'raw_output='=>'bool'],
+'hash_final' => ['non-empty-string', 'context'=>'HashContext', 'raw_output='=>'bool'],
+'hash_hkdf' => ['non-empty-string|false', 'algo'=>'string', 'key'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
+'hash_hmac' => ['non-empty-string|false', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
+'hash_hmac_algos' => ['array'],
+'hash_hmac_file' => ['non-empty-string|false', 'algo'=>'string', 'filename'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
'hash_init' => ['HashContext', 'algo'=>'string', 'options='=>'int', 'key='=>'string'],
-'hash_pbkdf2' => ['string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
+'hash_pbkdf2' => ['non-empty-string|false', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
'hash_update' => ['bool', 'context'=>'HashContext', 'data'=>'string'],
'hash_update_file' => ['bool', 'context'=>'HashContext', 'filename'=>'string', 'scontext='=>'?HashContext'],
'hash_update_stream' => ['int', 'context'=>'HashContext', 'handle'=>'resource', 'length='=>'int'],
@@ -4452,7 +4448,7 @@
'iconv_mime_decode_headers' => ['array|false', 'headers'=>'string', 'mode='=>'int', 'charset='=>'string'],
'iconv_mime_encode' => ['string|false', 'field_name'=>'string', 'field_value'=>'string', 'preference='=>'array'],
'iconv_set_encoding' => ['bool', 'type'=>'string', 'charset'=>'string'],
-'iconv_strlen' => ['int|false', 'str'=>'string', 'charset='=>'string'],
+'iconv_strlen' => ['0|positive-int|false', 'str'=>'string', 'charset='=>'string'],
'iconv_strpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'charset='=>'string'],
'iconv_strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'charset='=>'string'],
'iconv_substr' => ['string|false', 'str'=>'string', 'offset'=>'int', 'length='=>'int', 'charset='=>'string'],
@@ -4507,8 +4503,8 @@
'ifxus_seek_slob' => ['int', 'bid'=>'int', 'mode'=>'int', 'offset'=>'int'],
'ifxus_tell_slob' => ['int', 'bid'=>'int'],
'ifxus_write_slob' => ['int', 'bid'=>'int', 'content'=>'string'],
-'igbinary_serialize' => ['string', 'value'=>''],
-'igbinary_unserialize' => ['', 'str'=>'string'],
+'igbinary_serialize' => ['string|null', 'value'=>'mixed'],
+'igbinary_unserialize' => ['mixed', 'str'=>'string'],
'ignore_user_abort' => ['int', 'value='=>'bool'],
'iis_add_server' => ['int', 'path'=>'string', 'comment'=>'string', 'server_ip'=>'string', 'port'=>'int', 'host_name'=>'string', 'rights'=>'int', 'start_server'=>'int'],
'iis_get_dir_security' => ['int', 'server_instance'=>'int', 'virtual_path'=>'string'],
@@ -4602,7 +4598,6 @@
'imageinterlace' => ['int', 'im'=>'resource', 'interlace='=>'int'],
'imageistruecolor' => ['bool', 'im'=>'resource'],
'imagejpeg' => ['bool', 'im'=>'resource', 'filename='=>'string|resource|null', 'quality='=>'int'],
-'imagejpeg\'1' => ['string|false', 'im'=>'resource', 'filename='=>'null', 'quality='=>'int'],
'imagelayereffect' => ['bool', 'im'=>'resource', 'effect'=>'int'],
'imageline' => ['bool', 'im'=>'resource', 'x1'=>'int', 'y1'=>'int', 'x2'=>'int', 'y2'=>'int', 'col'=>'int'],
'imageloadfont' => ['int|false', 'filename'=>'string'],
@@ -5214,7 +5209,7 @@
'imap_close' => ['bool', 'stream_id'=>'resource', 'options='=>'int'],
'imap_create' => ['bool', 'stream_id'=>'resource', 'mailbox'=>'string'],
'imap_createmailbox' => ['bool', 'stream_id'=>'resource', 'mailbox'=>'string'],
-'imap_delete' => ['bool', 'stream_id'=>'resource', 'msg_no'=>'int', 'options='=>'int'],
+'imap_delete' => ['bool', 'stream_id'=>'resource', 'msg_no'=>'string', 'options='=>'int'],
'imap_deletemailbox' => ['bool', 'stream_id'=>'resource', 'mailbox'=>'string'],
'imap_errors' => ['array|false'],
'imap_expunge' => ['bool', 'stream_id'=>'resource'],
@@ -5271,7 +5266,7 @@
'imap_thread' => ['array|false', 'stream_id'=>'resource', 'options='=>'int'],
'imap_timeout' => ['mixed', 'timeout_type'=>'int', 'timeout='=>'int'],
'imap_uid' => ['int|false', 'stream_id'=>'resource', 'msg_no'=>'int'],
-'imap_undelete' => ['bool', 'stream_id'=>'resource', 'msg_no'=>'int', 'flags='=>'int'],
+'imap_undelete' => ['bool', 'stream_id'=>'resource', 'msg_no'=>'string', 'flags='=>'int'],
'imap_unsubscribe' => ['bool', 'stream_id'=>'resource', 'mailbox'=>'string'],
'imap_utf7_decode' => ['string|false', 'buf'=>'string'],
'imap_utf7_encode' => ['string', 'buf'=>'string'],
@@ -5285,7 +5280,7 @@
'inclued_get_data' => ['array'],
'inet_ntop' => ['string|false', 'in_addr'=>'string'],
'inet_pton' => ['string|false', 'ip_address'=>'string'],
-'InfiniteIterator::__construct' => ['void', 'iterator'=>'iterator'],
+'InfiniteIterator::__construct' => ['void', 'iterator'=>'Iterator'],
'InfiniteIterator::next' => ['void'],
'inflate_add' => ['string|false', 'context'=>'resource', 'encoded_data'=>'string', 'flush_mode='=>'int'],
'inflate_get_read_len' => ['int|false', 'resource'=>'resource'],
@@ -5621,7 +5616,7 @@
'InvalidArgumentException::getLine' => ['int'],
'InvalidArgumentException::getMessage' => ['string'],
'InvalidArgumentException::getPrevious' => ['Throwable|InvalidArgumentException|null'],
-'InvalidArgumentException::getTrace' => ['array'],
+'InvalidArgumentException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'InvalidArgumentException::getTraceAsString' => ['string'],
'ip2long' => ['int|false', 'ip_address'=>'string'],
'iptcembed' => ['string|bool', 'iptcdata'=>'string', 'jpeg_file_name'=>'string', 'spool='=>'int'],
@@ -5658,14 +5653,13 @@
'is_uploaded_file' => ['bool', 'path'=>'string'],
'is_writable' => ['bool', 'filename'=>'string'],
'is_writeable' => ['bool', 'filename'=>'string'],
-'isset' => ['bool', 'var'=>'mixed', '...rest='=>'mixed'],
'Iterator::current' => ['mixed'],
'Iterator::key' => ['mixed'],
'Iterator::next' => ['void'],
'Iterator::rewind' => ['void'],
'Iterator::valid' => ['bool'],
-'iterator_apply' => ['int', 'iterator'=>'Traversable', 'function'=>'callable', 'params='=>'array'],
-'iterator_count' => ['int', 'iterator'=>'Traversable'],
+'iterator_apply' => ['0|positive-int', 'iterator'=>'Traversable', 'function'=>'callable', 'params='=>'array'],
+'iterator_count' => ['0|positive-int', 'iterator'=>'Traversable'],
'iterator_to_array' => ['array', 'iterator'=>'Traversable', 'use_keys='=>'bool'],
'IteratorAggregate::getIterator' => ['Traversable'],
'IteratorIterator::__construct' => ['void', 'iterator'=>'Traversable'],
@@ -5695,8 +5689,8 @@
'join' => ['string', 'glue'=>'string', 'pieces'=>'array'],
'join\'1' => ['string', 'pieces'=>'array'],
'jpeg2wbmp' => ['bool', 'jpegname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'],
-'json_decode' => ['mixed', 'json'=>'string', 'assoc='=>'bool', 'depth='=>'int', 'options='=>'int'],
-'json_encode' => ['string|false', 'data'=>'mixed', 'options='=>'int', 'depth='=>'int'],
+'json_decode' => ['mixed', 'json'=>'string', 'assoc='=>'bool', 'depth='=>'positive-int', 'options='=>'int'],
+'json_encode' => ['string|false', 'data'=>'mixed', 'options='=>'int', 'depth='=>'positive-int'],
'json_last_error' => ['int'],
'json_last_error_msg' => ['string'],
'JsonIncrementalParser::__construct' => ['void', 'depth'=>'', 'options'=>''],
@@ -5739,7 +5733,7 @@
'kadm5_get_principals' => ['array', 'handle'=>'resource'],
'kadm5_init_with_password' => ['resource', 'admin_server'=>'string', 'realm'=>'string', 'principal'=>'string', 'password'=>'string'],
'kadm5_modify_principal' => ['bool', 'handle'=>'resource', 'principal'=>'string', 'options'=>'array'],
-'key' => ['int|string|null', 'array_arg'=>'array'],
+'key' => ['int|string|null', 'array_arg'=>'array|object'],
'key_exists' => ['bool', 'key'=>'string|int', 'search'=>'array'],
'krsort' => ['bool', '&rw_array_arg'=>'array', 'sort_flags='=>'int'],
'ksort' => ['bool', '&rw_array_arg'=>'array', 'sort_flags='=>'int'],
@@ -5900,7 +5894,7 @@
'ldap_sasl_bind' => ['bool', 'link_identifier'=>'resource', 'binddn='=>'string', 'password='=>'string', 'sasl_mech='=>'string', 'sasl_realm='=>'string', 'sasl_authc_id='=>'string', 'sasl_authz_id='=>'string', 'props='=>'string'],
'ldap_search' => ['resource|false', 'link_identifier'=>'resource|array', 'base_dn'=>'string', 'filter'=>'string', 'attrs='=>'array', 'attrsonly='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'servercontrols='=>'array'],
'ldap_set_option' => ['bool', 'link_identifier'=>'resource|null', 'option'=>'int', 'newval'=>'mixed'],
-'ldap_set_rebind_proc' => ['bool', 'link_identifier'=>'resource', 'callback'=>'string'],
+'ldap_set_rebind_proc' => ['bool', 'link_identifier'=>'resource', 'callback'=>'callable'],
'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'],
'ldap_start_tls' => ['bool', 'link_identifier'=>'resource'],
'ldap_t61_to_8859' => ['string|false', 'value'=>'string'],
@@ -5919,7 +5913,7 @@
'LengthException::getLine' => ['int'],
'LengthException::getMessage' => ['string'],
'LengthException::getPrevious' => ['Throwable|LengthException|null'],
-'LengthException::getTrace' => ['array'],
+'LengthException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'LengthException::getTraceAsString' => ['string'],
'levenshtein' => ['int', 'str1'=>'string', 'str2'=>'string'],
'levenshtein\'1' => ['int', 'str1'=>'string', 'str2'=>'string', 'cost_ins'=>'int', 'cost_rep'=>'int', 'cost_del'=>'int'],
@@ -5999,7 +5993,7 @@
'LogicException::getLine' => ['int'],
'LogicException::getMessage' => ['string'],
'LogicException::getPrevious' => ['Throwable|LogicException|null'],
-'LogicException::getTrace' => ['array'],
+'LogicException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'LogicException::getTraceAsString' => ['string'],
'long2ip' => ['string|false', 'proper_address'=>'int'],
'lstat' => ['array|false', 'filename'=>'string'],
@@ -6323,8 +6317,8 @@
'mb_encoding_aliases' => ['array|false', 'encoding'=>'string'],
'mb_ereg' => ['int|false', 'pattern'=>'string', 'string'=>'string', '&w_registers='=>'array'],
'mb_ereg_match' => ['bool', 'pattern'=>'string', 'string'=>'string', 'option='=>'string'],
-'mb_ereg_replace' => ['string|false', 'pattern'=>'string', 'replacement'=>'string', 'string'=>'string', 'option='=>'string'],
-'mb_ereg_replace_callback' => ['string|false', 'pattern'=>'string', 'callback'=>'callable', 'string'=>'string', 'option='=>'string'],
+'mb_ereg_replace' => ['string|false|null', 'pattern'=>'string', 'replacement'=>'string', 'string'=>'string', 'option='=>'string'],
+'mb_ereg_replace_callback' => ['string|false|null', 'pattern'=>'string', 'callback'=>'callable', 'string'=>'string', 'option='=>'string'],
'mb_ereg_search' => ['bool', 'pattern='=>'string', 'option='=>'string'],
'mb_ereg_search_getpos' => ['int'],
'mb_ereg_search_getregs' => ['array|false'],
@@ -6348,24 +6342,24 @@
'mb_regex_set_options' => ['string', 'options='=>'string'],
'mb_scrub' => ['string', 'str'=>'string', 'enc='=>'string'],
'mb_send_mail' => ['bool', 'to'=>'string', 'subject'=>'string', 'message'=>'string', 'additional_headers='=>'string|array|null', 'additional_parameter='=>'string'],
-'mb_split' => ['array|false', 'pattern'=>'string', 'string'=>'string', 'limit='=>'int'],
+'mb_split' => ['list|false', 'pattern'=>'string', 'string'=>'string', 'limit='=>'int'],
'mb_strcut' => ['string', 'str'=>'string', 'start'=>'int', 'length='=>'?int', 'encoding='=>'string'],
'mb_strimwidth' => ['string', 'str'=>'string', 'start'=>'int', 'width'=>'int', 'trimmarker='=>'string', 'encoding='=>'string'],
-'mb_stripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'],
+'mb_stripos' => ['0|positive-int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'],
'mb_stristr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'part='=>'bool', 'encoding='=>'string'],
-'mb_strlen' => ['int|false', 'str'=>'string', 'encoding='=>'string'],
-'mb_strpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'],
+'mb_strlen' => ['0|positive-int|false', 'str'=>'string', 'encoding='=>'string'],
+'mb_strpos' => ['0|positive-int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'],
'mb_strrchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'part='=>'bool', 'encoding='=>'string'],
'mb_strrichr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'part='=>'bool', 'encoding='=>'string'],
-'mb_strripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'],
-'mb_strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'],
+'mb_strripos' => ['0|positive-int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'],
+'mb_strrpos' => ['0|positive-int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'],
'mb_strstr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'part='=>'bool', 'encoding='=>'string'],
'mb_strtolower' => ['string', 'str'=>'string', 'encoding='=>'string'],
'mb_strtoupper' => ['string', 'str'=>'string', 'encoding='=>'string'],
-'mb_strwidth' => ['int', 'str'=>'string', 'encoding='=>'string'],
+'mb_strwidth' => ['0|positive-int', 'str'=>'string', 'encoding='=>'string'],
'mb_substitute_character' => ['mixed', 'substchar='=>'mixed'],
'mb_substr' => ['string', 'str'=>'string', 'start'=>'int', 'length='=>'?int', 'encoding='=>'string'],
-'mb_substr_count' => ['int', 'haystack'=>'string', 'needle'=>'string', 'encoding='=>'string'],
+'mb_substr_count' => ['0|positive-int', 'haystack'=>'string', 'needle'=>'string', 'encoding='=>'string'],
'mcrypt_cbc' => ['string', 'cipher'=>'string', 'key'=>'string', 'data'=>'string', 'mode'=>'int', 'iv='=>'string'],
'mcrypt_cfb' => ['string', 'cipher'=>'string', 'key'=>'string', 'data'=>'string', 'mode'=>'int', 'iv='=>'string'],
'mcrypt_create_iv' => ['string', 'size'=>'int', 'source='=>'int'],
@@ -6402,8 +6396,8 @@
'mcrypt_module_open' => ['resource|false', 'cipher'=>'string', 'cipher_directory'=>'string', 'mode'=>'string', 'mode_directory'=>'string'],
'mcrypt_module_self_test' => ['bool', 'algorithm'=>'string', 'lib_dir='=>'string'],
'mcrypt_ofb' => ['string', 'cipher'=>'string', 'key'=>'string', 'data'=>'string', 'mode'=>'int', 'iv='=>'string'],
-'md5' => ['string', 'str'=>'string', 'raw_output='=>'bool'],
-'md5_file' => ['string|false', 'filename'=>'string', 'raw_output='=>'bool'],
+'md5' => ['non-empty-string', 'str'=>'string', 'raw_output='=>'bool'],
+'md5_file' => ['non-empty-string|false', 'filename'=>'string', 'raw_output='=>'bool'],
'mdecrypt_generic' => ['string', 'td'=>'resource', 'data'=>'string'],
'Memcache::add' => ['bool', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'],
'Memcache::addServer' => ['bool', 'host'=>'string', 'port='=>'int', 'persistent='=>'bool', 'weight='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable', 'timeoutms='=>'int'],
@@ -6412,7 +6406,7 @@
'Memcache::decrement' => ['int', 'key'=>'string', 'value='=>'int'],
'Memcache::delete' => ['bool', 'key'=>'string', 'timeout='=>'int'],
'Memcache::flush' => ['bool'],
-'Memcache::get' => ['string|array|false', 'key'=>'string', 'flags='=>'array', 'keys='=>'array'],
+'Memcache::get' => ['string|array|false', 'key'=>'string', '&flags='=>'array', '&keys='=>'array'],
'Memcache::getExtendedStats' => ['array', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'],
'Memcache::getServerStatus' => ['int', 'host'=>'string', 'port='=>'int'],
'Memcache::getStats' => ['array', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'],
@@ -6436,7 +6430,7 @@
'Memcached::decrementByKey' => ['int|false', 'server_key'=>'string', 'key'=>'string', 'offset='=>'int', 'initial_value='=>'int', 'expiry='=>'int'],
'Memcached::delete' => ['bool', 'key'=>'string', 'time='=>'int'],
'Memcached::deleteByKey' => ['bool', 'server_key'=>'string', 'key'=>'string', 'time='=>'int'],
-'Memcached::deleteMulti' => ['bool', 'keys'=>'array', 'time='=>'int'],
+'Memcached::deleteMulti' => ['array', 'keys'=>'array', 'time='=>'int'],
'Memcached::deleteMultiByKey' => ['bool', 'server_key'=>'string', 'keys'=>'array', 'time='=>'int'],
'Memcached::fetch' => ['array'],
'Memcached::fetchAll' => ['array'],
@@ -6479,7 +6473,7 @@
'MemcachePool::decrement' => ['int', 'key'=>'string', 'value='=>'int'],
'MemcachePool::delete' => ['bool', 'key'=>'string', 'timeout='=>'int'],
'MemcachePool::flush' => ['bool'],
-'MemcachePool::get' => ['string|array|false', 'key'=>'string', 'flags='=>'array', 'keys='=>'array'],
+'MemcachePool::get' => ['string|array|false', 'key'=>'string', '&flags='=>'array', '&keys='=>'array'],
'MemcachePool::getExtendedStats' => ['array', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'],
'MemcachePool::getServerStatus' => ['int', 'host'=>'string', 'port='=>'int'],
'MemcachePool::getStats' => ['array', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'],
@@ -6663,7 +6657,7 @@
'MongoCursorException::getLine' => ['int'],
'MongoCursorException::getMessage' => ['string'],
'MongoCursorException::getPrevious' => ['Exception|Throwable'],
-'MongoCursorException::getTrace' => ['array'],
+'MongoCursorException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'MongoCursorException::getTraceAsString' => ['string'],
'MongoCursorInterface::__construct' => ['void'],
'MongoCursorInterface::batchSize' => ['MongoCursorInterface', 'batchSize'=>'int'],
@@ -6755,7 +6749,7 @@
'MongoDB\Driver\Exception\RuntimeException::getLine' => ['int'],
'MongoDB\Driver\Exception\RuntimeException::getMessage' => ['string'],
'MongoDB\Driver\Exception\RuntimeException::getPrevious' => ['RuntimeException|Throwable'],
-'MongoDB\Driver\Exception\RuntimeException::getTrace' => ['array'],
+'MongoDB\Driver\Exception\RuntimeException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'MongoDB\Driver\Exception\RuntimeException::getTraceAsString' => ['string'],
'MongoDB\Driver\Exception\WriteException::__clone' => ['void'],
'MongoDB\Driver\Exception\WriteException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'(?RuntimeException)|(?Throwable)'],
@@ -6766,7 +6760,7 @@
'MongoDB\Driver\Exception\WriteException::getLine' => ['int'],
'MongoDB\Driver\Exception\WriteException::getMessage' => ['string'],
'MongoDB\Driver\Exception\WriteException::getPrevious' => ['RuntimeException|Throwable'],
-'MongoDB\Driver\Exception\WriteException::getTrace' => ['array'],
+'MongoDB\Driver\Exception\WriteException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'MongoDB\Driver\Exception\WriteException::getTraceAsString' => ['string'],
'MongoDB\Driver\Exception\WriteException::getWriteResult' => ['MongoDB\Driver\WriteResult'],
'MongoDB\Driver\Manager::__construct' => ['void', 'uri'=>'string', 'options='=>'array', 'driverOptions='=>'array'],
@@ -6842,7 +6836,7 @@
'MongoException::getLine' => ['int'],
'MongoException::getMessage' => ['string'],
'MongoException::getPrevious' => ['Exception|Throwable'],
-'MongoException::getTrace' => ['array'],
+'MongoException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'MongoException::getTraceAsString' => ['string'],
'MongoGridFS::__construct' => ['void', 'db'=>'MongoDB', 'prefix='=>'string', 'chunks='=>'mixed'],
'MongoGridFS::__get' => ['MongoCollection', 'name'=>'string'],
@@ -6953,7 +6947,7 @@
'MongoResultException::getLine' => ['int'],
'MongoResultException::getMessage' => ['string'],
'MongoResultException::getPrevious' => ['Exception|Throwable'],
-'MongoResultException::getTrace' => ['array'],
+'MongoResultException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'MongoResultException::getTraceAsString' => ['string'],
'MongoTimestamp::__construct' => ['void', 'sec='=>'int', 'inc='=>'int'],
'MongoTimestamp::__toString' => ['string'],
@@ -6973,7 +6967,7 @@
'MongoWriteConcernException::getLine' => ['int'],
'MongoWriteConcernException::getMessage' => ['string'],
'MongoWriteConcernException::getPrevious' => ['Exception|Throwable'],
-'MongoWriteConcernException::getTrace' => ['array'],
+'MongoWriteConcernException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'MongoWriteConcernException::getTraceAsString' => ['string'],
'monitor_custom_event' => ['void', 'class'=>'string', 'text'=>'string', 'severe='=>'int', 'user_data='=>'mixed'],
'monitor_httperror_event' => ['void', 'error_code'=>'int', 'url'=>'string', 'severe='=>'int'],
@@ -7108,11 +7102,11 @@
'mt_rand\'1' => ['int'],
'mt_srand' => ['void', 'seed='=>'int', 'mode='=>'int'],
'MultipleIterator::__construct' => ['void', 'flags='=>'int'],
-'MultipleIterator::attachIterator' => ['void', 'iterator'=>'iterator', 'infos='=>'string'],
-'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'iterator'],
+'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'infos='=>'string'],
+'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'Iterator'],
'MultipleIterator::countIterators' => ['int'],
'MultipleIterator::current' => ['array'],
-'MultipleIterator::detachIterator' => ['void', 'iterator'=>'iterator'],
+'MultipleIterator::detachIterator' => ['void', 'iterator'=>'Iterator'],
'MultipleIterator::getFlags' => ['int'],
'MultipleIterator::key' => ['array'],
'MultipleIterator::next' => ['void'],
@@ -7240,7 +7234,7 @@
'mysqli_fetch_assoc' => ['array|null', 'result'=>'mysqli_result'],
'mysqli_fetch_field' => ['object|false', 'result'=>'mysqli_result'],
'mysqli_fetch_field_direct' => ['object|false', 'result'=>'mysqli_result', 'fieldnr'=>'int'],
-'mysqli_fetch_fields' => ['array|false', 'result'=>'mysqli_result'],
+'mysqli_fetch_fields' => ['array', 'result'=>'mysqli_result'],
'mysqli_fetch_lengths' => ['array|false', 'result'=>'mysqli_result'],
'mysqli_fetch_object' => ['object|null', 'result'=>'mysqli_result', 'class_name='=>'string', 'params='=>'?array'],
'mysqli_fetch_row' => ['array|null', 'result'=>'mysqli_result'],
@@ -7291,7 +7285,7 @@
'mysqli_result::fetch_assoc' => ['array|null'],
'mysqli_result::fetch_field' => ['object|false'],
'mysqli_result::fetch_field_direct' => ['object|false', 'fieldnr'=>'int'],
-'mysqli_result::fetch_fields' => ['array|false'],
+'mysqli_result::fetch_fields' => ['array'],
'mysqli_result::fetch_object' => ['object|null', 'class_name='=>'string', 'params='=>'array'],
'mysqli_result::fetch_row' => ['array|null'],
'mysqli_result::field_seek' => ['bool', 'fieldnr'=>'int'],
@@ -7341,10 +7335,10 @@
'mysqli_stmt_data_seek' => ['void', 'stmt'=>'mysqli_stmt', 'offset'=>'int'],
'mysqli_stmt_errno' => ['int', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_error' => ['string', 'stmt'=>'mysqli_stmt'],
-'mysqli_stmt_error_list' => ['array', 'stmt'=>'mysqli_stmt'],
+'mysqli_stmt_error_list' => ['list', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_execute' => ['bool', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_fetch' => ['bool|null', 'stmt'=>'mysqli_stmt'],
-'mysqli_stmt_field_count' => ['int', 'stmt'=>'mysqli_stmt'],
+'mysqli_stmt_field_count' => ['0|positive-int', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_free_result' => ['void', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_get_result' => ['mysqli_result|false', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_get_warnings' => ['object|false', 'stmt'=>'mysqli_stmt'],
@@ -7352,13 +7346,13 @@
'mysqli_stmt_insert_id' => ['', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_more_results' => ['bool', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_next_result' => ['bool', 'stmt'=>'mysqli_stmt'],
-'mysqli_stmt_num_rows' => ['int', 'stmt'=>'mysqli_stmt'],
-'mysqli_stmt_param_count' => ['int', 'stmt'=>'mysqli_stmt'],
+'mysqli_stmt_num_rows' => ['0|positive-int', 'stmt'=>'mysqli_stmt'],
+'mysqli_stmt_param_count' => ['0|positive-int', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_prepare' => ['bool', 'stmt'=>'mysqli_stmt', 'query'=>'string'],
'mysqli_stmt_reset' => ['bool', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_result_metadata' => ['mysqli_result|false', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_send_long_data' => ['bool', 'stmt'=>'mysqli_stmt', 'param_nr'=>'int', 'data'=>'string'],
-'mysqli_stmt_sqlstate' => ['string', 'stmt'=>'mysqli_stmt'],
+'mysqli_stmt_sqlstate' => ['non-empty-string', 'stmt'=>'mysqli_stmt'],
'mysqli_stmt_store_result' => ['bool', 'stmt'=>'mysqli_stmt'],
'mysqli_store_result' => ['mysqli_result|false', 'link'=>'mysqli', 'option='=>'int'],
'mysqli_thread_id' => ['int', 'link'=>'mysqli'],
@@ -7751,13 +7745,13 @@
'newt_win_message' => ['void', 'title'=>'string', 'button_text'=>'string', 'format'=>'string', 'args='=>'mixed', '...args='=>'mixed'],
'newt_win_messagev' => ['void', 'title'=>'string', 'button_text'=>'string', 'format'=>'string', 'args'=>'array'],
'newt_win_ternary' => ['int', 'title'=>'string', 'button1_text'=>'string', 'button2_text'=>'string', 'button3_text'=>'string', 'format'=>'string', 'args='=>'mixed', '...args='=>'mixed'],
-'next' => ['mixed', '&rw_array_arg'=>'array'],
+'next' => ['mixed', '&rw_array_arg'=>'array|object'],
'ngettext' => ['string', 'msgid1'=>'string', 'msgid2'=>'string', 'n'=>'int'],
'nl2br' => ['string', 'str'=>'string', 'is_xhtml='=>'bool'],
'nl_langinfo' => ['string|false', 'item'=>'int'],
-'NoRewindIterator::__construct' => ['void', 'iterator'=>'iterator'],
+'NoRewindIterator::__construct' => ['void', 'iterator'=>'Iterator'],
'NoRewindIterator::current' => ['mixed'],
-'NoRewindIterator::getInnerIterator' => ['iterator'],
+'NoRewindIterator::getInnerIterator' => ['Iterator'],
'NoRewindIterator::key' => ['mixed'],
'NoRewindIterator::next' => ['void'],
'NoRewindIterator::rewind' => ['void'],
@@ -8117,7 +8111,7 @@
'openssl_public_decrypt' => ['bool', 'data'=>'string', '&w_decrypted'=>'string', 'key'=>'string|resource', 'padding='=>'int'],
'openssl_public_encrypt' => ['bool', 'data'=>'string', '&w_crypted'=>'string', 'key'=>'string|resource', 'padding='=>'int'],
'openssl_random_pseudo_bytes' => ['string|false', 'length'=>'int', '&w_crypto_strong='=>'bool'],
-'openssl_seal' => ['int|false', 'data'=>'string', '&w_sealed_data'=>'string', '&w_env_keys'=>'array', 'pub_key_ids'=>'array', 'method='=>'string', '&rw_iv='=>'string'],
+'openssl_seal' => ['int|false', 'data'=>'string', '&w_sealed_data'=>'string', '&w_env_keys'=>'array', 'pub_key_ids'=>'array', 'method='=>'string', '&w_iv='=>'string'],
'openssl_sign' => ['bool', 'data'=>'string', '&w_signature'=>'string', 'priv_key_id'=>'resource|string', 'signature_alg='=>'int|string'],
'openssl_spki_export' => ['string|null|false', 'spkac'=>'string'],
'openssl_spki_export_challenge' => ['string|null|false', 'spkac'=>'string'],
@@ -8142,7 +8136,7 @@
'OutOfBoundsException::getLine' => ['int'],
'OutOfBoundsException::getMessage' => ['string'],
'OutOfBoundsException::getPrevious' => ['Throwable|OutOfBoundsException|null'],
-'OutOfBoundsException::getTrace' => ['array'],
+'OutOfBoundsException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'OutOfBoundsException::getTraceAsString' => ['string'],
'OutOfRangeException::__clone' => ['void'],
'OutOfRangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'(?Throwable)|(?OutOfRangeException)'],
@@ -8152,7 +8146,7 @@
'OutOfRangeException::getLine' => ['int'],
'OutOfRangeException::getMessage' => ['string'],
'OutOfRangeException::getPrevious' => ['Throwable|OutOfRangeException|null'],
-'OutOfRangeException::getTrace' => ['array'],
+'OutOfRangeException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'OutOfRangeException::getTraceAsString' => ['string'],
'output_add_rewrite_var' => ['bool', 'name'=>'string', 'value'=>'string'],
'output_reset_rewrite_vars' => ['bool'],
@@ -8164,12 +8158,12 @@
'OverflowException::getLine' => ['int'],
'OverflowException::getMessage' => ['string'],
'OverflowException::getPrevious' => ['Throwable|OverflowException|null'],
-'OverflowException::getTrace' => ['array'],
+'OverflowException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'OverflowException::getTraceAsString' => ['string'],
'overload' => ['', 'class_name'=>'string'],
'override_function' => ['bool', 'function_name'=>'string', 'function_args'=>'string', 'function_code'=>'string'],
'pack' => ['string', 'format'=>'string', '...args='=>'mixed'],
-'ParentIterator::__construct' => ['void', 'iterator'=>'recursiveiterator'],
+'ParentIterator::__construct' => ['void', 'iterator'=>'RecursiveIterator'],
'ParentIterator::accept' => ['bool'],
'ParentIterator::getChildren' => ['ParentIterator'],
'ParentIterator::hasChildren' => ['bool'],
@@ -8231,7 +8225,7 @@
'parse_ini_file' => ['array|false', 'filename'=>'string', 'process_sections='=>'bool', 'scanner_mode='=>'int'],
'parse_ini_string' => ['array|false', 'ini_string'=>'string', 'process_sections='=>'bool', 'scanner_mode='=>'int'],
'parse_str' => ['void', 'encoded_string'=>'string', '&w_result='=>'array'],
-'parse_url' => ['mixed', 'url'=>'string', 'url_component='=>'int'],
+'parse_url' => ['array|int|string|false|null', 'url'=>'string', 'url_component='=>'int'],
'ParseError::__clone' => ['void'],
'ParseError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'(?Throwable)|(?ParseError)'],
'ParseError::__toString' => ['string'],
@@ -8240,7 +8234,7 @@
'ParseError::getLine' => ['int'],
'ParseError::getMessage' => ['string'],
'ParseError::getPrevious' => ['Throwable|ParseError|null'],
-'ParseError::getTrace' => ['array'],
+'ParseError::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'ParseError::getTraceAsString' => ['string'],
'parsekit_compile_file' => ['array', 'filename'=>'string', 'errors='=>'array', 'options='=>'int'],
'parsekit_compile_string' => ['array', 'phpcode'=>'string', 'errors='=>'array', 'options='=>'int'],
@@ -8444,17 +8438,17 @@
'PDO::getAttribute' => ['', 'attribute'=>'int'],
'PDO::getAvailableDrivers' => ['array'],
'PDO::inTransaction' => ['bool'],
-'PDO::lastInsertId' => ['string', 'seqname='=>'string'],
+'PDO::lastInsertId' => ['string|false', 'seqname='=>'string'],
'PDO::pgsqlCopyFromArray' => ['bool', 'table_name'=>'string', 'rows'=>'array', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'],
'PDO::pgsqlCopyFromFile' => ['bool', 'table_name'=>'string', 'filename'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'],
'PDO::pgsqlCopyToArray' => ['array', 'table_name'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'],
'PDO::pgsqlCopyToFile' => ['bool', 'table_name'=>'string', 'filename'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'],
-'PDO::pgsqlGetNotify' => ['array', 'result_type'=>'int', 'ms_timeout'=>'int'],
+'PDO::pgsqlGetNotify' => ['array', 'result_type='=>'int', 'ms_timeout='=>'int'],
'PDO::pgsqlGetPid' => ['int'],
'PDO::pgsqlLOBCreate' => ['string'],
'PDO::pgsqlLOBOpen' => ['resource', 'oid'=>'string', 'mode='=>'string'],
'PDO::pgsqlLOBUnlink' => ['bool', 'oid'=>'string'],
-'PDO::prepare' => ['PDOStatement', 'statement'=>'string', 'options='=>'array'],
+'PDO::prepare' => ['__benevolent', 'statement'=>'string', 'options='=>'array'],
'PDO::query' => ['PDOStatement|false', 'sql'=>'string'],
'PDO::query\'1' => ['PDOStatement|false', 'sql'=>'string', 'fetch_column'=>'int', 'colno'=>'int'],
'PDO::query\'2' => ['PDOStatement|false', 'sql'=>'string', 'fetch_class'=>'int', 'classname'=>'string', 'ctorargs'=>'array'],
@@ -8471,7 +8465,7 @@
'PDOException::getLine' => [''],
'PDOException::getMessage' => [''],
'PDOException::getPrevious' => [''],
-'PDOException::getTrace' => [''],
+'PDOException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'PDOException::getTraceAsString' => [''],
'PDOStatement::__sleep' => ['array'],
'PDOStatement::__wakeup' => ['void'],
@@ -8495,7 +8489,7 @@
'PDOStatement::setAttribute' => ['bool', 'attribute'=>'int', 'value'=>'mixed'],
'PDOStatement::setFetchMode' => ['bool', 'mode'=>'int'],
'PDOStatement::setFetchMode\'1' => ['bool', 'fetch_column'=>'int', 'colno'=>'int'],
-'PDOStatement::setFetchMode\'2' => ['bool', 'fetch_class'=>'int', 'classname'=>'string', 'ctorargs'=>'array'],
+'PDOStatement::setFetchMode\'2' => ['bool', 'fetch_class'=>'int', 'classname'=>'string', 'ctorargs='=>'?array'],
'PDOStatement::setFetchMode\'3' => ['bool', 'fetch_into'=>'int', 'object'=>'object'],
'pfsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_errno='=>'int', '&w_errstr='=>'string', 'timeout='=>'float'],
'pg_affected_rows' => ['int', 'result'=>'resource'],
@@ -8535,7 +8529,7 @@
'pg_fetch_row' => ['array|false', 'result'=>'resource', 'row='=>'?int', 'result_type='=>'int'],
'pg_field_is_null' => ['int|false', 'result'=>'', 'field_name_or_number'=>'string|int'],
'pg_field_is_null\'1' => ['int', 'result'=>'', 'row'=>'int', 'field_name_or_number'=>'string|int'],
-'pg_field_name' => ['string', 'result'=>'resource', 'field_number'=>'int'],
+'pg_field_name' => ['string|false', 'result'=>'resource', 'field_number'=>'int'],
'pg_field_num' => ['int', 'result'=>'resource', 'field_name'=>'string'],
'pg_field_prtlen' => ['int|false', 'result'=>'', 'field_name_or_number'=>''],
'pg_field_prtlen\'1' => ['int', 'result'=>'', 'row'=>'int', 'field_name_or_number'=>'string|int'],
@@ -8613,7 +8607,7 @@
'Phar::addFromString' => ['', 'localname'=>'string', 'contents'=>'string'],
'Phar::apiVersion' => ['string'],
'Phar::buildFromDirectory' => ['array', 'base_dir'=>'string', 'regex='=>'string'],
-'Phar::buildFromIterator' => ['array', 'iter'=>'iterator', 'base_directory='=>'string'],
+'Phar::buildFromIterator' => ['array', 'iter'=>'Iterator', 'base_directory='=>'string'],
'Phar::canCompress' => ['bool', 'method='=>'int'],
'Phar::canWrite' => ['bool'],
'Phar::compress' => ['Phar', 'compression'=>'int', 'extension='=>'string'],
@@ -8629,7 +8623,7 @@
'Phar::decompressFiles' => ['bool'],
'Phar::delete' => ['bool', 'entry'=>'string'],
'Phar::delMetadata' => ['bool'],
-'Phar::extractTo' => ['bool', 'pathto'=>'string', 'files='=>'string|array', 'overwrite='=>'bool'],
+'Phar::extractTo' => ['bool', 'pathto'=>'string', 'files='=>'string|array|null', 'overwrite='=>'bool'],
'Phar::getAlias' => ['string'],
'Phar::getMetadata' => ['mixed'],
'Phar::getModified' => ['bool'],
@@ -8670,7 +8664,7 @@
'PharData::addFile' => ['', 'file'=>'string', 'localname='=>'string'],
'PharData::addFromString' => ['bool', 'localname'=>'string', 'contents'=>'string'],
'PharData::buildFromDirectory' => ['array', 'base_dir'=>'string', 'regex='=>'string'],
-'PharData::buildFromIterator' => ['array', 'iter'=>'iterator', 'base_directory='=>'string'],
+'PharData::buildFromIterator' => ['array', 'iter'=>'Iterator', 'base_directory='=>'string'],
'PharData::compress' => ['PharData', 'compression'=>'int', 'extension='=>'string'],
'PharData::compressFiles' => ['bool', 'compression'=>'int'],
'PharData::convertToData' => ['PharData', 'format='=>'int', 'compression='=>'int', 'extension='=>'string'],
@@ -8680,7 +8674,7 @@
'PharData::decompressFiles' => ['bool'],
'PharData::delete' => ['bool', 'entry'=>'string'],
'PharData::delMetadata' => ['bool'],
-'PharData::extractTo' => ['bool', 'pathto'=>'string', 'files='=>'string|array', 'overwrite='=>'bool'],
+'PharData::extractTo' => ['bool', 'pathto'=>'string', 'files='=>'string|array|null', 'overwrite='=>'bool'],
'PharData::isWritable' => ['bool'],
'PharData::offsetGet' => ['PharFileInfo', 'offset'=>'string'],
'PharData::offsetSet' => ['', 'offset'=>'string', 'value'=>'string'],
@@ -8742,7 +8736,8 @@
'phpdbg_prompt' => ['', 'prompt'=>'string'],
'phpdbg_start_oplog' => [''],
'phpinfo' => ['bool', 'what='=>'int'],
-'phpversion' => ['string|false', 'extension='=>'string'],
+'phpversion' => ['string'],
+'phpversion\'1' => ['string|false', 'extension'=>'string'],
'pht\AtomicInteger::__construct' => ['void', 'value='=>'int'],
'pht\AtomicInteger::dec' => ['void'],
'pht\AtomicInteger::get' => ['int'],
@@ -8831,18 +8826,17 @@
'Postal\Expand::expand_address' => ['string[]', 'address'=>'string', 'options='=>'array'],
'Postal\Parser::parse_address' => ['array', 'address'=>'string', 'options='=>'array'],
'pow' => ['float|int', 'base'=>'int|float', 'exponent'=>'int|float'],
-'preg_filter' => ['mixed', 'regex'=>'mixed', 'replace'=>'mixed', 'subject'=>'mixed', 'limit='=>'int', '&w_count='=>'int'],
+'preg_filter' => ['string|array|null', 'regex'=>'string|array', 'replace'=>'string|array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'],
'preg_grep' => ['array|false', 'regex'=>'string', 'input'=>'array', 'flags='=>'int'],
'preg_last_error' => ['int'],
-'preg_match' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_subpatterns='=>'string[]', 'flags='=>'int', 'offset='=>'int'],
-'preg_match_all' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_subpatterns='=>'array', 'flags='=>'int', 'offset='=>'int'],
+'preg_match' => ['0|1|false', 'pattern'=>'string', 'subject'=>'string', '&w_subpatterns='=>'string[]', 'flags='=>'int', 'offset='=>'int'],
+'preg_match_all' => ['0|positive-int|false|null', 'pattern'=>'string', 'subject'=>'string', '&w_subpatterns='=>'array', 'flags='=>'int', 'offset='=>'int'],
'preg_quote' => ['string', 'str'=>'string', 'delim_char='=>'string'],
'preg_replace' => ['string|array|null', 'regex'=>'string|array', 'replace'=>'string|array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'],
-'preg_replace_callback' => ['string|array|null', 'regex'=>'string|array', 'callback'=>'callable', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'],
+'preg_replace_callback' => ['string|array|null', 'regex'=>'string|array', 'callback'=>'callable(array):string', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'],
'preg_replace_callback_array' => ['string|array|null', 'pattern'=>'array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'],
'preg_split' => ['array|false', 'pattern'=>'string', 'subject'=>'string', 'limit='=>'?int', 'flags='=>'int'],
-'prev' => ['mixed', '&rw_array_arg'=>'array'],
-'print' => ['int', 'arg'=>'string'],
+'prev' => ['mixed', '&rw_array_arg'=>'array|object'],
'print_r' => ['string|true', 'var'=>'mixed', 'return='=>'bool'],
'printf' => ['int', 'format'=>'string', '...values='=>'string|int|float'],
'proc_close' => ['int', 'process'=>'resource'],
@@ -9062,7 +9056,7 @@
'radius_strerror' => ['string', 'radius_handle'=>'resource'],
'rand' => ['int', 'min'=>'int', 'max'=>'int'],
'rand\'1' => ['int'],
-'random_bytes' => ['string', 'length'=>'int'],
+'random_bytes' => ['non-empty-string', 'length'=>'positive-int'],
'random_int' => ['int', 'min'=>'int', 'max'=>'int'],
'range' => ['array', 'low'=>'int|float|string', 'high'=>'int|float|string', 'step='=>'int|float'],
'RangeException::__clone' => ['void'],
@@ -9073,7 +9067,7 @@
'RangeException::getLine' => ['int'],
'RangeException::getMessage' => ['string'],
'RangeException::getPrevious' => ['Throwable|RangeException|null'],
-'RangeException::getTrace' => ['array'],
+'RangeException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'RangeException::getTraceAsString' => ['string'],
'rar_allow_broken_set' => ['bool', 'rarfile'=>'RarArchive', 'allow_broken'=>'bool'],
'rar_broken_is' => ['bool', 'rarfile'=>'RarArchive'],
@@ -9116,7 +9110,7 @@
'RarException::getLine' => ['int'],
'RarException::getMessage' => ['string'],
'RarException::getPrevious' => ['Exception|Throwable'],
-'RarException::getTrace' => ['array'],
+'RarException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'RarException::getTraceAsString' => ['string'],
'RarException::isUsingExceptions' => ['bool'],
'RarException::setUsingExceptions' => ['void', 'using_exceptions'=>'bool'],
@@ -9124,8 +9118,8 @@
'rawurlencode' => ['string', 'str'=>'string'],
'read_exif_data' => ['array', 'filename'=>'string|resource', 'sections_needed='=>'string', 'sub_arrays='=>'bool', 'read_thumbnail='=>'bool'],
'readdir' => ['string|false', 'dir_handle='=>'resource'],
-'readfile' => ['int|false', 'filename'=>'string', 'use_include_path='=>'bool', 'context='=>'resource'],
-'readgzfile' => ['int|false', 'filename'=>'string', 'use_include_path='=>'int'],
+'readfile' => ['0|positive-int|false', 'filename'=>'string', 'use_include_path='=>'bool', 'context='=>'resource'],
+'readgzfile' => ['0|positive-int|false', 'filename'=>'string', 'use_include_path='=>'int'],
'readline' => ['string|false', 'prompt='=>'?string'],
'readline_add_history' => ['bool', 'prompt'=>'string'],
'readline_callback_handler_install' => ['bool', 'prompt'=>'string', 'callback'=>'callable'],
@@ -9175,14 +9169,14 @@
'RecursiveArrayIterator::seek' => ['void', 'position'=>'int'],
'RecursiveArrayIterator::serialize' => ['string'],
'RecursiveArrayIterator::setFlags' => ['void', 'flags'=>'string'],
-'RecursiveArrayIterator::uasort' => ['void', 'cmp_function'=>'callable(mixed,mixed):int'],
-'RecursiveArrayIterator::uksort' => ['void', 'cmp_function'=>'callable(array-key,array-key):int'],
+'RecursiveArrayIterator::uasort' => ['void', 'callback'=>'callable(mixed,mixed):int'],
+'RecursiveArrayIterator::uksort' => ['void', 'callback'=>'callable(array-key,array-key):int'],
'RecursiveArrayIterator::unserialize' => ['string', 'serialized'=>'string'],
'RecursiveArrayIterator::valid' => ['bool'],
'RecursiveCachingIterator::__construct' => ['void', 'iterator'=>'Iterator', 'flags'=>''],
'RecursiveCachingIterator::getChildren' => ['RecursiveCachingIterator'],
'RecursiveCachingIterator::hasChildren' => ['bool'],
-'RecursiveCallbackFilterIterator::__construct' => ['void', 'iterator'=>'recursiveiterator', 'func'=>'callable'],
+'RecursiveCallbackFilterIterator::__construct' => ['void', 'iterator'=>'RecursiveIterator', 'func'=>'callable'],
'RecursiveCallbackFilterIterator::getChildren' => ['RecursiveCallbackFilterIterator'],
'RecursiveCallbackFilterIterator::hasChildren' => ['void'],
'RecursiveDirectoryIterator::__construct' => ['void', 'path'=>'string', 'flags='=>'int'],
@@ -9193,7 +9187,7 @@
'RecursiveDirectoryIterator::key' => ['string'],
'RecursiveDirectoryIterator::next' => ['void'],
'RecursiveDirectoryIterator::rewind' => ['void'],
-'RecursiveFilterIterator::__construct' => ['void', 'iterator'=>'recursiveiterator'],
+'RecursiveFilterIterator::__construct' => ['void', 'iterator'=>'RecursiveIterator'],
'RecursiveFilterIterator::getChildren' => ['RecursiveFilterIterator'],
'RecursiveFilterIterator::hasChildren' => ['bool'],
'RecursiveIterator::getChildren' => ['RecursiveIterator'],
@@ -9216,10 +9210,10 @@
'RecursiveIteratorIterator::rewind' => ['void'],
'RecursiveIteratorIterator::setMaxDepth' => ['void', 'max_depth='=>'int'],
'RecursiveIteratorIterator::valid' => ['bool'],
-'RecursiveRegexIterator::__construct' => ['void', 'iterator'=>'recursiveiterator', 'regex='=>'string', 'mode='=>'int', 'flags='=>'int', 'preg_flags='=>'int'],
+'RecursiveRegexIterator::__construct' => ['void', 'iterator'=>'RecursiveIterator', 'regex='=>'string', 'mode='=>'int', 'flags='=>'int', 'preg_flags='=>'int'],
'RecursiveRegexIterator::getChildren' => ['RecursiveRegexIterator'],
'RecursiveRegexIterator::hasChildren' => ['bool'],
-'RecursiveTreeIterator::__construct' => ['void', 'iterator'=>'recursiveiterator|iteratoraggregate', 'flags='=>'int', 'cit_flags='=>'int', 'mode='=>'int'],
+'RecursiveTreeIterator::__construct' => ['void', 'iterator'=>'RecursiveIterator|IteratorAggregate', 'flags='=>'int', 'cit_flags='=>'int', 'mode='=>'int'],
'RecursiveTreeIterator::beginChildren' => ['void'],
'RecursiveTreeIterator::beginIteration' => ['RecursiveIterator'],
'RecursiveTreeIterator::callGetChildren' => ['RecursiveIterator'],
@@ -9401,7 +9395,7 @@
'Redis::sRem' => ['int', 'key'=>'string', 'member1'=>'string', '...other_members='=>'string'],
'Redis::sRemove' => ['int', 'key'=>'string', 'member1'=>'string', '...other_members='=>'string'],
'Redis::sScan' => ['array|bool', 'key'=>'string', '&iterator'=>'int', 'pattern='=>'string', 'count='=>'int'],
-'Redis::strLen' => ['int', 'key'=>'string'],
+'Redis::strLen' => ['0|positive-int', 'key'=>'string'],
'Redis::subscribe' => ['mixed|null', 'channels'=>'array', 'callback'=>'string|array'],
'Redis::substr' => ['', 'key'=>'string', 'start'=>'int', 'end'=>'int'],
'Redis::sUnion' => ['array', 'key'=>'string', '...other_keys='=>'string'],
@@ -9591,7 +9585,7 @@
'RedisCluster::sRandMember' => ['array|string', 'key'=>'string', 'count='=>'int'],
'RedisCluster::sRem' => ['int', 'key'=>'string', 'member1'=>'string', '...other_members='=>'string'],
'RedisCluster::sScan' => ['array', 'key'=>'string', '&iterator'=>'int', 'pattern='=>'null', 'count='=>'int'],
-'RedisCluster::strlen' => ['int', 'key'=>'string'],
+'RedisCluster::strlen' => ['0|positive-int', 'key'=>'string'],
'RedisCluster::subscribe' => ['mixed', 'channels'=>'array', 'callback'=>'string'],
'RedisCluster::sUnion' => ['array', 'key1'=>'string', '...other_keys='=>'string'],
'RedisCluster::sUnionStore' => ['int', 'dstKey'=>'string', 'key1'=>'string', '...other_keys='=>'string'],
@@ -9673,7 +9667,7 @@
'ReflectionClass::hasConstant' => ['bool', 'name'=>'string'],
'ReflectionClass::hasMethod' => ['bool', 'name'=>'string'],
'ReflectionClass::hasProperty' => ['bool', 'name'=>'string'],
-'ReflectionClass::implementsInterface' => ['bool', 'interface_name'=>'string|reflectionclass'],
+'ReflectionClass::implementsInterface' => ['bool', 'interface_name'=>'string|ReflectionClass'],
'ReflectionClass::inNamespace' => ['bool'],
'ReflectionClass::isAbstract' => ['bool'],
'ReflectionClass::isAnonymous' => ['bool'],
@@ -9685,13 +9679,13 @@
'ReflectionClass::isInternal' => ['bool'],
'ReflectionClass::isIterable' => ['bool'],
'ReflectionClass::isIterateable' => ['bool'],
-'ReflectionClass::isSubclassOf' => ['bool', 'class'=>'string|reflectionclass'],
+'ReflectionClass::isSubclassOf' => ['bool', 'class'=>'string|ReflectionClass'],
'ReflectionClass::isTrait' => ['bool'],
'ReflectionClass::isUserDefined' => ['bool'],
'ReflectionClass::newInstance' => ['object', 'args='=>'mixed', '...args='=>'mixed'],
'ReflectionClass::newInstanceArgs' => ['object', 'args='=>'array'],
'ReflectionClass::newInstanceWithoutConstructor' => ['object'],
-'ReflectionClass::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'string'],
+'ReflectionClass::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'mixed'],
'ReflectionClassConstant::__construct' => ['void', 'class'=>'mixed', 'name'=>'string'],
'ReflectionClassConstant::__toString' => ['string'],
'ReflectionClassConstant::export' => ['string', 'class'=>'mixed', 'name'=>'string', 'return='=>'bool'],
@@ -9782,7 +9776,7 @@
'ReflectionGenerator::getExecutingLine' => ['int'],
'ReflectionGenerator::getFunction' => ['ReflectionFunctionAbstract'],
'ReflectionGenerator::getThis' => ['object'],
-'ReflectionGenerator::getTrace' => ['array', 'options'=>'int'],
+'ReflectionGenerator::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}', 'options'=>'int'],
'ReflectionMethod::__construct' => ['void', 'class'=>'string|object', 'name'=>'string'],
'ReflectionMethod::__construct\'1' => ['void', 'class_method'=>'string'],
'ReflectionMethod::__toString' => ['string'],
@@ -9861,7 +9855,7 @@
'ReflectionZendExtension::getVersion' => ['string'],
'Reflector::__toString' => ['string'],
'Reflector::export' => ['?string'],
-'RegexIterator::__construct' => ['void', 'iterator'=>'iterator', 'regex'=>'string', 'mode='=>'int', 'flags='=>'int', 'preg_flags='=>'int'],
+'RegexIterator::__construct' => ['void', 'iterator'=>'Iterator', 'regex'=>'string', 'mode='=>'int', 'flags='=>'int', 'preg_flags='=>'int'],
'RegexIterator::accept' => ['bool'],
'RegexIterator::getFlags' => ['int'],
'RegexIterator::getMode' => ['int'],
@@ -9875,7 +9869,7 @@
'register_tick_function' => ['bool', 'function'=>'callable(): void', '...args='=>'mixed'],
'rename' => ['bool', 'old_name'=>'string', 'new_name'=>'string', 'context='=>'resource'],
'rename_function' => ['bool', 'original_name'=>'string', 'new_name'=>'string'],
-'reset' => ['mixed', '&rw_array'=>'array'],
+'reset' => ['mixed', '&rw_array'=>'array|object'],
'ResourceBundle::__construct' => ['void', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'],
'ResourceBundle::count' => ['0|positive-int'],
'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'],
@@ -9889,13 +9883,13 @@
'resourcebundle_get_error_code' => ['int', 'r'=>'resourcebundle'],
'resourcebundle_get_error_message' => ['string', 'r'=>'resourcebundle'],
'resourcebundle_locales' => ['array|false', 'bundlename'=>'string'],
-'restore_error_handler' => ['bool'],
-'restore_exception_handler' => ['bool'],
+'restore_error_handler' => ['true'],
+'restore_exception_handler' => ['true'],
'restore_include_path' => ['void'],
'rewind' => ['bool', 'fp'=>'resource'],
'rewinddir' => ['null|false', 'dir_handle='=>'resource'],
'rmdir' => ['bool', 'dirname'=>'string', 'context='=>'resource'],
-'round' => ['float', 'number'=>'float', 'precision='=>'int', 'mode='=>'int'],
+'round' => ['float|false', 'number'=>'float', 'precision='=>'int', 'mode='=>'int'],
'rpm_close' => ['bool', 'rpmr'=>'resource'],
'rpm_get_tag' => ['mixed', 'rpmr'=>'resource', 'tagnum'=>'int'],
'rpm_is_valid' => ['bool', 'filename'=>'string'],
@@ -9963,7 +9957,7 @@
'RuntimeException::getLine' => ['int'],
'RuntimeException::getMessage' => ['string'],
'RuntimeException::getPrevious' => ['Throwable|RuntimeException|null'],
-'RuntimeException::getTrace' => ['array'],
+'RuntimeException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'RuntimeException::getTraceAsString' => ['string'],
'SAMConnection::commit' => ['bool'],
'SAMConnection::connect' => ['bool', 'protocol'=>'string', 'properties='=>'array'],
@@ -10124,7 +10118,7 @@
'SessionHandler::close' => ['bool'],
'SessionHandler::create_sid' => ['char'],
'SessionHandler::destroy' => ['bool', 'id'=>'string'],
-'SessionHandler::gc' => ['bool', 'maxlifetime'=>'int'],
+'SessionHandler::gc' => ['int|false', 'maxlifetime'=>'int'],
'SessionHandler::open' => ['bool', 'save_path'=>'string', 'session_name'=>'string'],
'SessionHandler::read' => ['string', 'id'=>'string'],
'SessionHandler::updateTimestamp' => ['bool', 'session_id'=>'string', 'session_data'=>'string'],
@@ -10132,7 +10126,7 @@
'SessionHandler::write' => ['bool', 'id'=>'string', 'data'=>'string'],
'SessionHandlerInterface::close' => ['bool'],
'SessionHandlerInterface::destroy' => ['bool', 'session_id'=>'string'],
-'SessionHandlerInterface::gc' => ['bool', 'maxlifetime'=>'int'],
+'SessionHandlerInterface::gc' => ['int|false', 'maxlifetime'=>'int'],
'SessionHandlerInterface::open' => ['bool', 'save_path'=>'string', 'name'=>'string'],
'SessionHandlerInterface::read' => ['string', 'session_id'=>'string'],
'SessionHandlerInterface::write' => ['bool', 'session_id'=>'string', 'session_data'=>'string'],
@@ -10141,7 +10135,7 @@
'SessionUpdateTimestampHandler::validateId' => ['char', 'id'=>'string'],
'SessionUpdateTimestampHandlerInterface::updateTimestamp' => ['bool', 'key'=>'string', 'val'=>'string'],
'SessionUpdateTimestampHandlerInterface::validateId' => ['bool', 'key'=>'string'],
-'set_error_handler' => ['?callable', 'callback'=>'null|callable(int,string,string,int,array):bool|callable(int,string,string,int):bool|callable(int,string,string):bool|callable(int,string):bool', 'error_types='=>'int'],
+'set_error_handler' => ['?callable', 'callback'=>'null|callable(int,string,string,int,array):bool', 'error_types='=>'int'],
'set_exception_handler' => ['null|callable(Throwable):void', 'exception_handler'=>'null|callable(Throwable):void'],
'set_file_buffer' => ['int', 'fp'=>'resource', 'buffer'=>'int'],
'set_include_path' => ['string|false', 'new_include_path'=>'string'],
@@ -10284,20 +10278,20 @@
'snmpset' => ['bool', 'host'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'mixed', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalk' => ['array|false', 'host'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
-'SoapClient::__call' => ['', 'function_name'=>'string', 'arguments'=>'array'],
+'SoapClient::__call' => ['mixed', 'function_name'=>'string', 'arguments'=>'array'],
'SoapClient::__construct' => ['void', 'wsdl'=>'mixed', 'options='=>'array|null'],
-'SoapClient::__doRequest' => ['string', 'request'=>'string', 'location'=>'string', 'action'=>'string', 'version'=>'int', 'one_way='=>'int'],
+'SoapClient::__doRequest' => ['string|null', 'request'=>'string', 'location'=>'string', 'action'=>'string', 'version'=>'int', 'one_way='=>'int'],
'SoapClient::__getCookies' => ['array'],
-'SoapClient::__getFunctions' => ['array'],
-'SoapClient::__getLastRequest' => ['string'],
-'SoapClient::__getLastRequestHeaders' => ['string'],
-'SoapClient::__getLastResponse' => ['string'],
-'SoapClient::__getLastResponseHeaders' => ['string'],
-'SoapClient::__getTypes' => ['array'],
+'SoapClient::__getFunctions' => ['array|null'],
+'SoapClient::__getLastRequest' => ['string|null'],
+'SoapClient::__getLastRequestHeaders' => ['string|null'],
+'SoapClient::__getLastResponse' => ['string|null'],
+'SoapClient::__getLastResponseHeaders' => ['string|null'],
+'SoapClient::__getTypes' => ['array|null'],
'SoapClient::__setCookie' => ['', 'name'=>'string', 'value='=>'string'],
-'SoapClient::__setLocation' => ['string', 'new_location='=>'string'],
+'SoapClient::__setLocation' => ['string|null', 'new_location='=>'string'],
'SoapClient::__setSoapHeaders' => ['bool', 'soapheaders='=>''],
-'SoapClient::__soapCall' => ['', 'function_name'=>'string', 'arguments'=>'array', 'options='=>'array', 'input_headers='=>'SoapHeader|array', '&w_output_headers='=>'array'],
+'SoapClient::__soapCall' => ['mixed', 'function_name'=>'string', 'arguments'=>'array', 'options='=>'array', 'input_headers='=>'SoapHeader|array', '&w_output_headers='=>'array'],
'SoapClient::SoapClient' => ['object', 'wsdl'=>'mixed', 'options='=>'array|null'],
'SoapFault::__construct' => ['void', 'faultcode'=>'string', 'string'=>'string', 'faultactor='=>'string', 'detail='=>'string', 'faultname='=>'string', 'headerfault='=>'string'],
'SoapFault::__toString' => ['string'],
@@ -10417,7 +10411,7 @@
'Sodium\randombytes_uniform' => ['int', 'upperBoundNonInclusive'=>'int'],
'Sodium\version_string' => ['string'],
'sodium_add' => ['string', 'string_1'=>'string', 'string_2'=>'string'],
-'sodium_base642bin' => ['string', 'base64'=>'string', 'variant'=>'int', 'ignore'=>'string'],
+'sodium_base642bin' => ['string', 'base64'=>'string', 'variant'=>'int', 'ignore='=>'string'],
'sodium_bin2base64' => ['string', 'binary'=>'string', 'variant'=>'int'],
'sodium_bin2hex' => ['string', 'binary'=>'string'],
'sodium_compare' => ['int', 'string_1'=>'string', 'string_2'=>'string'],
@@ -10820,7 +10814,7 @@
'SolrException::getLine' => ['int'],
'SolrException::getMessage' => ['string'],
'SolrException::getPrevious' => ['Exception|Throwable'],
-'SolrException::getTrace' => ['array'],
+'SolrException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'SolrException::getTraceAsString' => ['string'],
'SolrGenericResponse::__construct' => ['void'],
'SolrGenericResponse::__destruct' => [''],
@@ -10845,7 +10839,7 @@
'SolrIllegalArgumentException::getLine' => ['int'],
'SolrIllegalArgumentException::getMessage' => ['string'],
'SolrIllegalArgumentException::getPrevious' => ['Exception|Throwable'],
-'SolrIllegalArgumentException::getTrace' => ['array'],
+'SolrIllegalArgumentException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'SolrIllegalArgumentException::getTraceAsString' => ['string'],
'SolrIllegalOperationException::__clone' => ['void'],
'SolrIllegalOperationException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'(?Exception)|(?Throwable)'],
@@ -10857,7 +10851,7 @@
'SolrIllegalOperationException::getLine' => ['int'],
'SolrIllegalOperationException::getMessage' => ['string'],
'SolrIllegalOperationException::getPrevious' => ['Exception|Throwable'],
-'SolrIllegalOperationException::getTrace' => ['array'],
+'SolrIllegalOperationException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'SolrIllegalOperationException::getTraceAsString' => ['string'],
'SolrInputDocument::__clone' => ['void'],
'SolrInputDocument::__construct' => ['void'],
@@ -11165,7 +11159,7 @@
'SolrServerException::getLine' => ['int'],
'SolrServerException::getMessage' => ['string'],
'SolrServerException::getPrevious' => ['Exception|Throwable'],
-'SolrServerException::getTrace' => ['array'],
+'SolrServerException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'SolrServerException::getTraceAsString' => ['string'],
'SolrUpdateResponse::__construct' => ['void'],
'SolrUpdateResponse::__destruct' => [''],
@@ -11257,24 +11251,24 @@
'SplEnum::getConstList' => ['array', 'include_default='=>'bool'],
'SplFileInfo::__construct' => ['void', 'file_name'=>'string'],
'SplFileInfo::__toString' => ['string'],
-'SplFileInfo::getATime' => ['int'],
+'SplFileInfo::getATime' => ['__benevolent'],
'SplFileInfo::getBasename' => ['string', 'suffix='=>'string'],
'SplFileInfo::getCTime' => ['int'],
'SplFileInfo::getExtension' => ['string'],
'SplFileInfo::getFileInfo' => ['SplFileInfo', 'class_name='=>'string'],
'SplFileInfo::getFilename' => ['string'],
-'SplFileInfo::getGroup' => ['int'],
-'SplFileInfo::getInode' => ['int'],
-'SplFileInfo::getLinkTarget' => ['string'],
-'SplFileInfo::getMTime' => ['int'],
-'SplFileInfo::getOwner' => ['int'],
+'SplFileInfo::getGroup' => ['__benevolent'],
+'SplFileInfo::getInode' => ['__benevolent'],
+'SplFileInfo::getLinkTarget' => ['__benevolent'],
+'SplFileInfo::getMTime' => ['__benevolent'],
+'SplFileInfo::getOwner' => ['__benevolent'],
'SplFileInfo::getPath' => ['string'],
'SplFileInfo::getPathInfo' => ['SplFileInfo', 'class_name='=>'string'],
'SplFileInfo::getPathname' => ['string'],
-'SplFileInfo::getPerms' => ['int'],
-'SplFileInfo::getRealPath' => ['string|false'],
-'SplFileInfo::getSize' => ['int'],
-'SplFileInfo::getType' => ['string'],
+'SplFileInfo::getPerms' => ['__benevolent'],
+'SplFileInfo::getRealPath' => ['__benevolent'],
+'SplFileInfo::getSize' => ['__benevolent'],
+'SplFileInfo::getType' => ['__benevolent'],
'SplFileInfo::isDir' => ['bool'],
'SplFileInfo::isExecutable' => ['bool'],
'SplFileInfo::isFile' => ['bool'],
@@ -11300,7 +11294,7 @@
'SplFileObject::fread' => ['string|false', 'length'=>'int'],
'SplFileObject::fscanf' => ['bool', 'format'=>'string', '&...w_vars='=>'string|int|float'],
'SplFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'],
-'SplFileObject::fstat' => ['array|false'],
+'SplFileObject::fstat' => ['array'],
'SplFileObject::ftell' => ['int|false'],
'SplFileObject::ftruncate' => ['bool', 'size'=>'int'],
'SplFileObject::fwrite' => ['int', 'str'=>'string', 'length='=>'int'],
@@ -11351,7 +11345,7 @@
'spliti' => ['array', 'pattern'=>'string', 'string'=>'string', 'limit='=>'int'],
'SplMaxHeap::compare' => ['int', 'a'=>'mixed', 'b'=>'mixed'],
'SplMinHeap::compare' => ['int', 'a'=>'mixed', 'b'=>'mixed'],
-'SplObjectStorage::addAll' => ['void', 'os'=>'splobjectstorage'],
+'SplObjectStorage::addAll' => ['void', 'os'=>'SplObjectStorage'],
'SplObjectStorage::attach' => ['void', 'obj'=>'object', 'inf='=>'mixed'],
'SplObjectStorage::contains' => ['bool', 'obj'=>'object'],
'SplObjectStorage::count' => ['0|positive-int'],
@@ -11365,14 +11359,14 @@
'SplObjectStorage::offsetGet' => ['mixed', 'obj'=>'object'],
'SplObjectStorage::offsetSet' => ['object', 'object'=>'object', 'data='=>'mixed'],
'SplObjectStorage::offsetUnset' => ['object', 'object'=>'object'],
-'SplObjectStorage::removeAll' => ['void', 'os'=>'splobjectstorage'],
-'SplObjectStorage::removeAllExcept' => ['void', 'os'=>'splobjectstorage'],
+'SplObjectStorage::removeAll' => ['void', 'os'=>'SplObjectStorage'],
+'SplObjectStorage::removeAllExcept' => ['void', 'os'=>'SplObjectStorage'],
'SplObjectStorage::rewind' => ['void'],
'SplObjectStorage::serialize' => ['string'],
'SplObjectStorage::setInfo' => ['void', 'inf'=>'mixed'],
'SplObjectStorage::unserialize' => ['void', 'serialized'=>'string'],
'SplObjectStorage::valid' => ['bool'],
-'SplObserver::update' => ['void', 'subject'=>'splsubject'],
+'SplObserver::update' => ['void', 'subject'=>'SplSubject'],
'SplPriorityQueue::compare' => ['int', 'a'=>'mixed', 'b'=>'mixed'],
'SplPriorityQueue::count' => ['0|positive-int'],
'SplPriorityQueue::current' => ['mixed'],
@@ -11391,8 +11385,8 @@
'SplQueue::enqueue' => ['void', 'value'=>'mixed'],
'SplQueue::setIteratorMode' => ['void', 'mode'=>'int'],
'SplStack::setIteratorMode' => ['void', 'mode'=>'int'],
-'SplSubject::attach' => ['void', 'observer'=>'splobserver'],
-'SplSubject::detach' => ['void', 'observer'=>'splobserver'],
+'SplSubject::attach' => ['void', 'observer'=>'SplObserver'],
+'SplSubject::detach' => ['void', 'observer'=>'SplObserver'],
'SplSubject::notify' => ['void'],
'SplTempFileObject::__construct' => ['void', 'max_memory='=>'int'],
'SplType::__construct' => ['void', 'initial_value='=>'mixed', 'strict='=>'bool'],
@@ -11546,7 +11540,8 @@
'sqlsrv_server_info' => ['array', 'conn'=>'resource'],
'sqrt' => ['float', 'number'=>'float'],
'srand' => ['void', 'seed='=>'int', 'mode='=>'int'],
-'sscanf' => ['mixed', 'str'=>'string', 'format'=>'string', '&...w_vars='=>'string|int|float|null'],
+'sscanf' => ['int|null', 'str'=>'string', 'format'=>'string', '&w_war'=>'string|int|float|null', '&...w_vars='=>'string|int|float|null'],
+'sscanf\'1' => ['array|null', 'str'=>'string', 'format'=>'string'],
'ssdeep_fuzzy_compare' => ['int', 'signature1'=>'string', 'signature2'=>'string'],
'ssdeep_fuzzy_hash' => ['string', 'to_hash'=>'string'],
'ssdeep_fuzzy_hash_filename' => ['string', 'file_name'=>'string'],
@@ -11696,7 +11691,7 @@
'str_replace' => ['string|array', 'search'=>'string|array', 'replace'=>'string|array', 'subject'=>'string|array', '&w_replace_count='=>'int'],
'str_rot13' => ['string', 'str'=>'string'],
'str_shuffle' => ['string', 'str'=>'string'],
-'str_split' => ['array|false', 'str'=>'string', 'split_length='=>'int'],
+'str_split' => ['non-empty-array|false', 'str'=>'string', 'split_length='=>'positive-int'],
'str_word_count' => ['array|int|false', 'string'=>'string', 'format='=>'int', 'charlist='=>'string'],
'strcasecmp' => ['int', 'str1'=>'string', 'str2'=>'string'],
'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'],
@@ -11705,7 +11700,7 @@
'strcspn' => ['int', 'str'=>'string', 'mask'=>'string', 'start='=>'int', 'length='=>'int'],
'stream_bucket_append' => ['void', 'brigade'=>'resource', 'bucket'=>'object'],
'stream_bucket_make_writeable' => ['object|null', 'brigade'=>'resource'],
-'stream_bucket_new' => ['resource', 'stream'=>'resource', 'buffer'=>'string'],
+'stream_bucket_new' => ['object', 'stream'=>'resource', 'buffer'=>'string'],
'stream_bucket_prepend' => ['void', 'brigade'=>'resource', 'bucket'=>'object'],
'stream_context_create' => ['resource', 'options='=>'array', 'params='=>'array'],
'stream_context_get_default' => ['resource', 'options='=>'array'],
@@ -11731,7 +11726,7 @@
'stream_isatty' => ['bool', 'stream'=>'resource'],
'stream_notification_callback' => ['callback', 'notification_code'=>'int', 'severity'=>'int', 'message'=>'string', 'message_code'=>'int', 'bytes_transferred'=>'int', 'bytes_max'=>'int'],
'stream_resolve_include_path' => ['string|false', 'filename'=>'string'],
-'stream_select' => ['int|false', '&rw_read_streams'=>'resource[]', '&rw_write_streams'=>'resource[]|null', '&rw_except_streams'=>'resource[]|null', 'tv_sec'=>'?int', 'tv_usec='=>'?int'],
+'stream_select' => ['int|false', '&rw_read_streams'=>'resource[]|null', '&rw_write_streams'=>'resource[]|null', '&rw_except_streams'=>'resource[]|null', 'tv_sec'=>'?int', 'tv_usec='=>'?int'],
'stream_set_blocking' => ['bool', 'socket'=>'resource', 'mode'=>'bool'],
'stream_set_chunk_size' => ['int|false', 'fp'=>'resource', 'chunk_size'=>'int'],
'stream_set_read_buffer' => ['int', 'fp'=>'resource', 'buffer'=>'int'],
@@ -11803,9 +11798,9 @@
'strtr' => ['string', 'str'=>'string', 'from'=>'string', 'to'=>'string'],
'strtr\'1' => ['string', 'str'=>'string', 'replace_pairs'=>'array'],
'strval' => ['string', 'var'=>'mixed'],
-'substr' => ['string', 'string'=>'string', 'start'=>'int', 'length='=>'int'],
+'substr' => ['__benevolent', 'string'=>'string', 'start'=>'int', 'length='=>'int'],
'substr_compare' => ['int|false', 'main_str'=>'string', 'str'=>'string', 'offset'=>'int', 'length='=>'int', 'case_sensitivity='=>'bool'],
-'substr_count' => ['int', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'length='=>'int'],
+'substr_count' => ['0|positive-int', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'length='=>'int'],
'substr_replace' => ['string|array', 'str'=>'string|array', 'repl'=>'mixed', 'start'=>'mixed', 'length='=>'mixed'],
'suhosin_encrypt_cookie' => ['string', 'name'=>'string', 'value'=>'string'],
'suhosin_get_raw_cookies' => ['array'],
@@ -12243,7 +12238,7 @@
'Throwable::getLine' => ['int'],
'Throwable::getMessage' => ['string'],
'Throwable::getPrevious' => ['Throwable|null'],
-'Throwable::getTrace' => ['array'],
+'Throwable::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'Throwable::getTraceAsString' => ['string'],
'tidy::__construct' => ['void', 'filename='=>'string', 'config='=>'', 'encoding='=>'string', 'use_include_path='=>'bool'],
'tidy::body' => ['tidyNode'],
@@ -12304,12 +12299,12 @@
'tidyNode::isJste' => ['bool'],
'tidyNode::isPhp' => ['bool'],
'tidyNode::isText' => ['bool'],
-'time' => ['int'],
-'time_nanosleep' => ['array{0:int,1:int}|bool', 'seconds'=>'int', 'nanoseconds'=>'int'],
+'time' => ['positive-int'],
+'time_nanosleep' => ['array{0:0|positive-int,1:0|positive-int}|bool', 'seconds'=>'int', 'nanoseconds'=>'int'],
'time_sleep_until' => ['bool', 'timestamp'=>'float'],
'timezone_abbreviations_list' => ['array'],
-'timezone_identifiers_list' => ['array', 'what='=>'int', 'country='=>'?string'],
-'timezone_location_get' => ['array|false', 'object'=>'DateTimeZone'],
+'timezone_identifiers_list' => ['array', 'what='=>'int', 'country='=>'?string'],
+'timezone_location_get' => ['array{country_code: string, latitude: float, longitude: float, comments: string}|false', 'object'=>'DateTimeZone'],
'timezone_name_from_abbr' => ['string|false', 'abbr'=>'string', 'gmtoffset='=>'int', 'isdst='=>'int'],
'timezone_name_get' => ['string', 'object'=>'DateTimeZone'],
'timezone_offset_get' => ['int', 'object'=>'DateTimeZone', 'datetime'=>'DateTime'],
@@ -12563,9 +12558,9 @@
'TypeError::getLine' => ['int'],
'TypeError::getMessage' => ['string'],
'TypeError::getPrevious' => ['Throwable|TypeError|null'],
-'TypeError::getTrace' => ['array'],
+'TypeError::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'TypeError::getTraceAsString' => ['string'],
-'uasort' => ['bool', '&rw_array_arg'=>'array', 'cmp_function'=>'callable(mixed,mixed):int'],
+'uasort' => ['bool', '&rw_array_arg'=>'array', 'callback'=>'callable(mixed,mixed):int'],
'ucfirst' => ['string', 'str'=>'string'],
'UConverter::__construct' => ['void', 'destination_encoding'=>'string', 'source_encoding='=>'string'],
'UConverter::convert' => ['string', 'str'=>'string', 'reverse='=>'bool'],
@@ -12614,7 +12609,7 @@
'ui\draw\text\font\fontfamilies' => ['array'],
'ui\quit' => ['void'],
'ui\run' => ['void', 'flags='=>'int'],
-'uksort' => ['bool', '&rw_array_arg'=>'array', 'cmp_function'=>'callable(array-key,array-key):int'],
+'uksort' => ['bool', '&rw_array_arg'=>'array', 'callback'=>'callable(array-key,array-key):int'],
'umask' => ['int', 'mask='=>'int'],
'UnderflowException::__clone' => ['void'],
'UnderflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'(?Throwable)|(?UnderflowException)'],
@@ -12624,7 +12619,7 @@
'UnderflowException::getLine' => ['int'],
'UnderflowException::getMessage' => ['string'],
'UnderflowException::getPrevious' => ['Throwable|UnderflowException|null'],
-'UnderflowException::getTrace' => ['array'],
+'UnderflowException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'UnderflowException::getTraceAsString' => ['string'],
'UnexpectedValueException::__clone' => ['void'],
'UnexpectedValueException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'(?Throwable)|(?UnexpectedValueException)'],
@@ -12634,15 +12629,14 @@
'UnexpectedValueException::getLine' => ['int'],
'UnexpectedValueException::getMessage' => ['string'],
'UnexpectedValueException::getPrevious' => ['Throwable|UnexpectedValueException|null'],
-'UnexpectedValueException::getTrace' => ['array'],
+'UnexpectedValueException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'UnexpectedValueException::getTraceAsString' => ['string'],
-'uniqid' => ['string', 'prefix='=>'string', 'more_entropy='=>'bool'],
+'uniqid' => ['non-empty-string', 'prefix='=>'string', 'more_entropy='=>'bool'],
'unixtojd' => ['int|false', 'timestamp='=>'int'],
'unlink' => ['bool', 'filename'=>'string', 'context='=>'resource'],
'unpack' => ['array|false', 'format'=>'string', 'data'=>'string', 'offset='=>'int'],
'unregister_tick_function' => ['void', 'function_name'=>'callable'],
'unserialize' => ['mixed', 'variable_representation'=>'string', 'allowed_classes='=>'array{allowed_classes?:string[]|bool}'],
-'unset' => ['void', 'var='=>'mixed', '...args='=>'mixed'],
'untaint' => ['bool', '&rw_string'=>'string', '&...rw_strings='=>'string'],
'uopz_add_function' => ['bool', 'class'=>'string', 'function'=>'string', 'handler'=>'Closure', '$flags'=>'bool', '$all'=>'bool'],
'uopz_add_function\1' => ['bool', 'function'=>'string', 'handler'=>'Closure', '$flags'=>'bool'],
@@ -12694,7 +12688,7 @@
'urlencode' => ['string', 'str'=>'string'],
'use_soap_error_handler' => ['bool', 'handler='=>'bool'],
'usleep' => ['void', 'micro_seconds'=>'int'],
-'usort' => ['bool', '&rw_array_arg'=>'array', 'cmp_function'=>'callable(mixed,mixed):int'],
+'usort' => ['bool', '&rw_array_arg'=>'array', 'callback'=>'callable(mixed,mixed):int'],
'utf8_decode' => ['string', 'data'=>'string'],
'utf8_encode' => ['string', 'data'=>'string'],
'V8Js::__construct' => ['void', 'object_name='=>'string', 'variables='=>'array', 'extensions='=>'array', 'report_uncaught_exceptions='=>'bool', 'snapshot_blob='=>'string'],
@@ -12730,7 +12724,7 @@
'V8JsScriptException::getLine' => ['int'],
'V8JsScriptException::getMessage' => ['string'],
'V8JsScriptException::getPrevious' => ['Exception|Throwable'],
-'V8JsScriptException::getTrace' => ['array'],
+'V8JsScriptException::getTrace' => ['array{function:string,line?:int,file?:string,class?:class-string,type?:string,args?:mixed[],object?:object}'],
'V8JsScriptException::getTraceAsString' => ['string'],
'var_dump' => ['void', 'var'=>'mixed', '...args='=>'mixed'],
'var_export' => ['string|null', 'var'=>'mixed', 'return='=>'bool'],
@@ -12973,6 +12967,7 @@
'xdebug_call_line' => ['int', 'depth=' => 'int'],
'xdebug_clear_aggr_profiling_data' => ['bool'],
'xdebug_code_coverage_started' => ['bool'],
+'xdebug_connect_to_client' => ['bool'],
'xdebug_debug_zval' => ['void', '...varName'=>'string'],
'xdebug_debug_zval_stdout' => ['void', '...varName'=>'string'],
'xdebug_disable' => ['void'],
@@ -12993,6 +12988,7 @@
'xdebug_is_debugger_active' => ['bool'],
'xdebug_is_enabled' => ['bool'],
'xdebug_memory_usage' => ['int'],
+'xdebug_notify' => ['bool', 'data' => 'mixed'],
'xdebug_peak_memory_usage' => ['int'],
'xdebug_print_function_stack' => ['array', 'message='=>'string', 'options=' => 'int'],
'xdebug_set_filter' => ['void', 'group' => 'int', 'list_type' => 'int', 'configuration' => 'array'],
@@ -13215,7 +13211,7 @@
'xslt_set_scheme_handler' => ['', 'xh'=>'', 'handlers'=>'array'],
'xslt_set_scheme_handlers' => ['', 'xh'=>'', 'handlers'=>'array'],
'xslt_setopt' => ['', 'processor'=>'', 'newmask'=>'int'],
-'XSLTProcessor::getParameter' => ['string', 'namespaceuri'=>'string', 'localname'=>'string'],
+'XSLTProcessor::getParameter' => ['string|false', 'namespaceuri'=>'string', 'localname'=>'string'],
'XsltProcessor::getSecurityPrefs' => ['int'],
'XSLTProcessor::hasExsltSupport' => ['bool'],
'XSLTProcessor::importStylesheet' => ['bool', 'stylesheet'=>'object'],
@@ -13225,9 +13221,9 @@
'XSLTProcessor::setParameter\'1' => ['bool', 'namespace'=>'string', 'options'=>'array'],
'XSLTProcessor::setProfiling' => ['bool', 'filename'=>'string'],
'XsltProcessor::setSecurityPrefs' => ['int', 'securityPrefs'=>'int'],
-'XSLTProcessor::transformToDoc' => ['DOMDocument', 'doc'=>'DOMNode'],
+'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'doc'=>'DOMNode'],
'XSLTProcessor::transformToURI' => ['int', 'doc'=>'DOMDocument', 'uri'=>'string'],
-'XSLTProcessor::transformToXML' => ['string|false', 'doc'=>'DOMDocument|SimpleXMLElement'],
+'XSLTProcessor::transformToXML' => ['string|false|null', 'doc'=>'DOMDocument|SimpleXMLElement'],
'Yaconf::get' => ['mixed', 'name'=>'string', 'default_value='=>'mixed'],
'Yaconf::has' => ['bool', 'name'=>'string'],
'Yaf_Action_Abstract::__construct' => ['void', 'request'=>'Yaf_Request_Abstract', 'response'=>'Yaf_Response_Abstract', 'view'=>'Yaf_View_Interface', 'invokeArgs='=>'?array'],
diff --git a/resources/functionMap_php74delta.php b/resources/functionMap_php74delta.php
index 656d998997..ff3f99ba0d 100644
--- a/resources/functionMap_php74delta.php
+++ b/resources/functionMap_php74delta.php
@@ -39,13 +39,13 @@
'FFI::typeof' => ['FFI\CType', '&ptr'=>'FFI\CData'],
'FFI::type' => ['FFI\CType', 'type'=>'string'],
'get_mangled_object_vars' => ['array', 'obj'=>'object'],
- 'mb_str_split' => ['array|false', 'str'=>'string', 'split_length='=>'int', 'encoding='=>'string'],
+ 'mb_str_split' => ['non-empty-array|false', 'str'=>'string', 'split_length='=>'int', 'encoding='=>'string'],
'password_algos' => ['array'],
'password_hash' => ['string|false', 'password'=>'string', 'algo'=>'string|null', 'options='=>'array'],
'password_needs_rehash' => ['bool', 'hash'=>'string', 'algo'=>'string|null', 'options='=>'array'],
- 'preg_replace_callback' => ['string|array|null', 'regex'=>'string|array', 'callback'=>'callable', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int', 'flags='=>'int'],
+ 'preg_replace_callback' => ['string|array|null', 'regex'=>'string|array', 'callback'=>'callable(array):string', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int', 'flags='=>'int'],
'preg_replace_callback_array' => ['string|array|null', 'pattern'=>'array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int', 'flags='=>'int'],
- 'sapi_windows_set_ctrl_handler' => ['bool', 'callable'=>'callable', 'add='=>'bool'],
+ 'sapi_windows_set_ctrl_handler' => ['bool', 'callable'=>'callable(int):void', 'add='=>'bool'],
'ReflectionProperty::getType' => ['?ReflectionType'],
'ReflectionProperty::hasType' => ['bool'],
'ReflectionProperty::isInitialized' => ['bool', 'object='=>'?object'],
@@ -55,6 +55,7 @@
'strip_tags' => ['string', 'str'=>'string', 'allowable_tags='=>'string|array'],
'WeakReference::create' => ['WeakReference', 'referent'=>'object'],
'WeakReference::get' => ['?object'],
+ 'proc_open' => ['resource|false', 'command'=>'string|list', 'descriptorspec'=>'array', '&w_pipes'=>'resource[]', 'cwd='=>'?string', 'env='=>'?array', 'other_options='=>'array'],
],
'old' => [
'implode\'2' => ['string', 'pieces'=>'array', 'glue'=>'string'],
diff --git a/resources/functionMap_php80delta.php b/resources/functionMap_php80delta.php
index c42dbbcd3b..fb557075d8 100644
--- a/resources/functionMap_php80delta.php
+++ b/resources/functionMap_php80delta.php
@@ -25,6 +25,7 @@
'bcdiv' => ['string', 'dividend'=>'string', 'divisor'=>'string', 'scale='=>'int'],
'bcmod' => ['string', 'dividend'=>'string', 'divisor'=>'string', 'scale='=>'int'],
'bcpowmod' => ['string', 'base'=>'string', 'exponent'=>'string', 'modulus'=>'string', 'scale='=>'int'],
+ 'call_user_func_array' => ['mixed', 'function'=>'callable', 'parameters'=>'array'],
'com_load_typelib' => ['bool', 'typelib_name'=>'string', 'case_insensitive='=>'true'],
'count_chars' => ['array|string', 'input'=>'string', 'mode='=>'int'],
'date_add' => ['DateTime', 'object'=>'DateTime', 'interval'=>'DateInterval'],
@@ -38,13 +39,16 @@
'date_time_set' => ['DateTime', 'object'=>'DateTime', 'hour'=>'int', 'minute'=>'int', 'second='=>'int', 'microseconds='=>'int'],
'date_timestamp_set' => ['DateTime', 'object'=>'DateTime', 'unixtimestamp'=>'int'],
'date_timezone_set' => ['DateTime', 'object'=>'DateTime', 'timezone'=>'DateTimeZone'],
- 'explode' => ['array', 'separator'=>'string', 'str'=>'string', 'limit='=>'int'],
+ 'explode' => ['non-empty-array', 'separator'=>'non-empty-string', 'str'=>'string', 'limit='=>'int'],
'fdiv' => ['float', 'dividend'=>'float', 'divisor'=>'float'],
'get_debug_type' => ['string', 'var'=>'mixed'],
'get_resource_id' => ['int', 'res'=>'resource'],
'gmdate' => ['string', 'format'=>'string', 'timestamp='=>'int'],
'gmmktime' => ['int|false', 'hour'=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'],
- 'hash_hkdf' => ['string', 'algo'=>'string', 'ikm'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
+ 'hash' => ['non-empty-string', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
+ 'hash_hkdf' => ['non-empty-string', 'algo'=>'string', 'key'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
+ 'hash_hmac' => ['non-empty-string', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
+ 'hash_pbkdf2' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
'imageaffine' => ['false|object', 'src'=>'resource', 'affine'=>'array', 'clip='=>'array'],
'imagecreate' => ['false|object', 'x_size'=>'int', 'y_size'=>'int'],
'imagecreatefrombmp' => ['false|object', 'filename'=>'string'],
@@ -66,24 +70,29 @@
'imagegrabscreen' => ['false|object'],
'imagegrabwindow' => ['false|object', 'window_handle'=>'int', 'client_area='=>'int'],
'imagejpeg' => ['bool', 'im'=>'GdImage', 'filename='=>'string|resource|null', 'quality='=>'int'],
- 'imagejpeg\'1' => ['string|false', 'im'=>'GdImage', 'filename='=>'null', 'quality='=>'int'],
'imagerotate' => ['false|object', 'src_im'=>'resource', 'angle'=>'float', 'bgdcolor'=>'int', 'ignoretransparent='=>'int'],
'imagescale' => ['false|object', 'im'=>'resource', 'new_width'=>'int', 'new_height='=>'int', 'method='=>'int'],
+ 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'resource', 'callback'=>'?callable'],
'mb_decode_numericentity' => ['string|false', 'string'=>'string', 'convmap'=>'array', 'encoding='=>'string'],
- 'mb_str_split' => ['array', 'str'=>'string', 'split_length='=>'int', 'encoding='=>'string'],
+ 'mb_str_split' => ['non-empty-array', 'str'=>'string', 'split_length='=>'positive-int', 'encoding='=>'string'],
+ 'mb_strlen' => ['0|positive-int', 'str'=>'string', 'encoding='=>'string'],
'mktime' => ['int|false', 'hour'=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'],
'odbc_exec' => ['resource|false', 'connection_id'=>'resource', 'query'=>'string'],
'parse_str' => ['void', 'encoded_string'=>'string', '&w_result'=>'array'],
'password_hash' => ['string', 'password'=>'string', 'algo'=>'string|int|null', 'options='=>'array'],
+ 'PDOStatement::fetchAll' => ['array', 'how='=>'int', 'fetch_argument='=>'int|string|callable', 'ctor_args='=>'?array'],
'PhpToken::tokenize' => ['list', 'code'=>'string', 'flags='=>'int'],
'PhpToken::is' => ['bool', 'kind'=>'string|int|string[]|int[]'],
'PhpToken::isIgnorable' => ['bool'],
'PhpToken::getTokenName' => ['string'],
+ 'preg_match_all' => ['0|positive-int|false', 'pattern'=>'string', 'subject'=>'string', '&w_subpatterns='=>'array', 'flags='=>'int', 'offset='=>'int'],
'proc_get_status' => ['array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}', 'process'=>'resource'],
+ 'set_error_handler' => ['?callable', 'callback'=>'null|callable(int,string,string,int):bool', 'error_types='=>'int'],
'socket_addrinfo_lookup' => ['AddressInfo[]', 'node'=>'string', 'service='=>'mixed', 'hints='=>'array'],
+ 'socket_select' => ['int|false', '&rw_read'=>'Socket[]|null', '&rw_write'=>'Socket[]|null', '&rw_except'=>'Socket[]|null', 'seconds'=>'int|null', 'microseconds='=>'int'],
'sodium_crypto_aead_chacha20poly1305_ietf_decrypt' => ['string|false', 'confidential_message'=>'string', 'public_message'=>'string', 'nonce'=>'string', 'key'=>'string'],
'str_contains' => ['bool', 'haystack'=>'string', 'needle'=>'string'],
- 'str_split' => ['array', 'str'=>'string', 'split_length='=>'int'],
+ 'str_split' => ['non-empty-array', 'str'=>'string', 'split_length='=>'positive-int'],
'str_ends_with' => ['bool', 'haystack'=>'string', 'needle'=>'string'],
'str_starts_with' => ['bool', 'haystack'=>'string', 'needle'=>'string'],
'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'],
@@ -94,6 +103,7 @@
'strripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
'strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
'strstr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'],
+ 'substr' => ['string', 'string'=>'string', 'start'=>'int', 'length='=>'int'],
'version_compare' => ['int|bool', 'version1'=>'string', 'version2'=>'string', 'operator='=>'string'],
'xml_parser_create' => ['XMLParser', 'encoding='=>'string'],
'xml_parser_create_ns' => ['XMLParser', 'encoding='=>'string', 'sep='=>'string'],
@@ -151,7 +161,6 @@
'bcpowmod' => ['?string', 'base'=>'string', 'exponent'=>'string', 'modulus'=>'string', 'scale='=>'int'],
'com_load_typelib' => ['bool', 'typelib_name'=>'string', 'case_insensitive='=>'bool'],
'count_chars' => ['array|false|string', 'input'=>'string', 'mode='=>'int'],
- 'create_function' => ['string', 'args'=>'string', 'code'=>'string'],
'date_add' => ['DateTime|false', 'object'=>'DateTime', 'interval'=>'DateInterval'],
'date_date_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'month'=>'int', 'day'=>'int'],
'date_diff' => ['DateInterval|false', 'obj1'=>'DateTimeInterface', 'obj2'=>'DateTimeInterface', 'absolute='=>'bool'],
@@ -168,7 +177,10 @@
'gmmktime' => ['int|false', 'hour='=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'],
'gmp_random' => ['GMP', 'limiter='=>'int'],
'gzgetss' => ['string|false', 'zp'=>'resource', 'length'=>'int', 'allowable_tags='=>'string'],
- 'hash_hkdf' => ['string|false', 'algo'=>'string', 'ikm'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
+ 'hash' => ['non-empty-string|false', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
+ 'hash_hkdf' => ['non-empty-string|false', 'algo'=>'string', 'key'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
+ 'hash_hmac' => ['non-empty-string|false', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
+ 'hash_pbkdf2' => ['non-empty-string|false', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
'image2wbmp' => ['bool', 'im'=>'resource', 'filename='=>'?string', 'threshold='=>'int'],
'imageaffine' => ['resource|false', 'src'=>'resource', 'affine'=>'array', 'clip='=>'array'],
'imagecreate' => ['resource|false', 'x_size'=>'int', 'y_size'=>'int'],
@@ -191,20 +203,22 @@
'imagegrabscreen' => ['false|resource'],
'imagegrabwindow' => ['false|resource', 'window_handle'=>'int', 'client_area='=>'int'],
'imagejpeg' => ['bool', 'im'=>'resource', 'filename='=>'string|resource|null', 'quality='=>'int'],
- 'imagejpeg\'1' => ['string|false', 'im'=>'resource', 'filename='=>'null', 'quality='=>'int'],
'imagerotate' => ['resource|false', 'src_im'=>'resource', 'angle'=>'float', 'bgdcolor'=>'int', 'ignoretransparent='=>'int'],
'imagescale' => ['resource|false', 'im'=>'resource', 'new_width'=>'int', 'new_height='=>'int', 'method='=>'int'],
'implode\'1' => ['string', 'pieces'=>'array'],
'jpeg2wbmp' => ['bool', 'jpegname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'],
+ 'ldap_set_rebind_proc' => ['bool', 'link_identifier'=>'resource', 'callback'=>'callable'],
'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'],
'mb_decode_numericentity' => ['string|false', 'string'=>'string', 'convmap'=>'array', 'encoding='=>'string', 'is_hex='=>'bool'],
'mktime' => ['int|false', 'hour='=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'],
+ 'mb_strlen' => ['0|positive-int', 'str'=>'string', 'encoding='=>'string'],
'odbc_exec' => ['resource|false', 'connection_id'=>'resource', 'query'=>'string', 'flags='=>'int'],
'parse_str' => ['void', 'encoded_string'=>'string', '&w_result='=>'array'],
'password_hash' => ['string|false|null', 'password'=>'string', 'algo'=>'?string|?int', 'options='=>'array'],
'png2wbmp' => ['bool', 'pngname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'],
'proc_get_status' => ['array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}|false', 'process'=>'resource'],
'read_exif_data' => ['array', 'filename'=>'string', 'sections_needed='=>'string', 'sub_arrays='=>'bool', 'read_thumbnail='=>'bool'],
+ 'socket_select' => ['int|false', '&rw_read_fds'=>'resource[]|null', '&rw_write_fds'=>'resource[]|null', '&rw_except_fds'=>'resource[]|null', 'tv_sec'=>'int|null', 'tv_usec='=>'int|null'],
'sodium_crypto_aead_chacha20poly1305_ietf_decrypt' => ['?string|?false', 'confidential_message'=>'string', 'public_message'=>'string', 'nonce'=>'string', 'key'=>'string'],
'SplFileObject::fgetss' => ['string|false', 'allowable_tags='=>'string'],
'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string|int', 'before_needle='=>'bool'],
@@ -215,6 +229,7 @@
'strripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string|int', 'offset='=>'int'],
'strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string|int', 'offset='=>'int'],
'strstr' => ['string|false', 'haystack'=>'string', 'needle'=>'string|int', 'before_needle='=>'bool'],
+ 'substr' => ['__benevolent', 'string'=>'string', 'start'=>'int', 'length='=>'int'],
'version_compare' => ['int|bool', 'version1'=>'string', 'version2'=>'string', 'operator='=>'string'],
'xml_parser_create' => ['resource', 'encoding='=>'string'],
'xml_parser_create_ns' => ['resource', 'encoding='=>'string', 'sep='=>'string'],
diff --git a/resources/functionMetadata.php b/resources/functionMetadata.php
index f136e361e4..fb6a45857c 100644
--- a/resources/functionMetadata.php
+++ b/resources/functionMetadata.php
@@ -68,6 +68,7 @@
'DateTimeImmutable::setTimestamp' => ['hasSideEffects' => false],
'DateTimeImmutable::setTimezone' => ['hasSideEffects' => false],
'DateTimeImmutable::sub' => ['hasSideEffects' => false],
+ 'Error::__construct' => ['hasSideEffects' => false],
'ErrorException::__construct' => ['hasSideEffects' => false],
'Event::__construct' => ['hasSideEffects' => false],
'EventBase::getFeatures' => ['hasSideEffects' => false],
@@ -86,6 +87,7 @@
'EventHttpConnection::__construct' => ['hasSideEffects' => false],
'EventHttpRequest::__construct' => ['hasSideEffects' => false],
'EventHttpRequest::getCommand' => ['hasSideEffects' => false],
+ 'EventHttpRequest::getConnection' => ['hasSideEffects' => false],
'EventHttpRequest::getHost' => ['hasSideEffects' => false],
'EventHttpRequest::getInputBuffer' => ['hasSideEffects' => false],
'EventHttpRequest::getInputHeaders' => ['hasSideEffects' => false],
@@ -495,6 +497,7 @@
'ReflectionFunctionAbstract::getAttributes' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::getClosureScopeClass' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::getClosureThis' => ['hasSideEffects' => false],
+ 'ReflectionFunctionAbstract::getClosureUsedVariables' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::getDocComment' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::getEndLine' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::getExtension' => ['hasSideEffects' => false],
@@ -509,10 +512,13 @@
'ReflectionFunctionAbstract::getShortName' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::getStartLine' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::getStaticVariables' => ['hasSideEffects' => false],
+ 'ReflectionFunctionAbstract::getTentativeReturnType' => ['hasSideEffects' => false],
+ 'ReflectionFunctionAbstract::hasTentativeReturnType' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::isClosure' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::isDeprecated' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::isGenerator' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::isInternal' => ['hasSideEffects' => false],
+ 'ReflectionFunctionAbstract::isStatic' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::isUserDefined' => ['hasSideEffects' => false],
'ReflectionFunctionAbstract::isVariadic' => ['hasSideEffects' => false],
'ReflectionGenerator::getExecutingFile' => ['hasSideEffects' => false],
@@ -521,6 +527,7 @@
'ReflectionGenerator::getFunction' => ['hasSideEffects' => false],
'ReflectionGenerator::getThis' => ['hasSideEffects' => false],
'ReflectionGenerator::getTrace' => ['hasSideEffects' => false],
+ 'ReflectionIntersectionType::getTypes' => ['hasSideEffects' => false],
'ReflectionMethod::getClosure' => ['hasSideEffects' => false],
'ReflectionMethod::getDeclaringClass' => ['hasSideEffects' => false],
'ReflectionMethod::getModifiers' => ['hasSideEffects' => false],
@@ -533,6 +540,7 @@
'ReflectionMethod::isProtected' => ['hasSideEffects' => false],
'ReflectionMethod::isPublic' => ['hasSideEffects' => false],
'ReflectionMethod::isStatic' => ['hasSideEffects' => false],
+ 'ReflectionMethod::setAccessible' => ['hasSideEffects' => false],
'ReflectionNamedType::getName' => ['hasSideEffects' => false],
'ReflectionNamedType::isBuiltin' => ['hasSideEffects' => false],
'ReflectionParameter::getAttributes' => ['hasSideEffects' => false],
@@ -567,6 +575,7 @@
'ReflectionProperty::isProtected' => ['hasSideEffects' => false],
'ReflectionProperty::isPublic' => ['hasSideEffects' => false],
'ReflectionProperty::isStatic' => ['hasSideEffects' => false],
+ 'ReflectionProperty::setAccessible' => ['hasSideEffects' => false],
'ReflectionReference::getId' => ['hasSideEffects' => false],
'ReflectionType::isBuiltin' => ['hasSideEffects' => false],
'ReflectionUnionType::getTypes' => ['hasSideEffects' => false],
@@ -601,15 +610,11 @@
'SimpleXMLIterator::valid' => ['hasSideEffects' => false],
'SoapFault::__construct' => ['hasSideEffects' => false],
'Spoofchecker::__construct' => ['hasSideEffects' => false],
- 'StubTests\\Model\\BasePHPElement::getFQN' => ['hasSideEffects' => false],
- 'StubTests\\Model\\BasePHPElement::getTypeNameFromNode' => ['hasSideEffects' => false],
- 'StubTests\\Model\\BasePHPElement::hasMutedProblem' => ['hasSideEffects' => false],
- 'StubTests\\Model\\StubsContainer::getClass' => ['hasSideEffects' => false],
- 'StubTests\\Model\\StubsContainer::getInterface' => ['hasSideEffects' => false],
+ 'StubTests\\CodeStyle\\BracesOneLineFixer::getDefinition' => ['hasSideEffects' => false],
'StubTests\\Parsers\\ExpectedFunctionArgumentsInfo::__toString' => ['hasSideEffects' => false],
'StubTests\\Parsers\\Visitors\\CoreStubASTVisitor::__construct' => ['hasSideEffects' => false],
+ 'StubTests\\StubsMetaExpectedArgumentsTest::getClassMemberFqn' => ['hasSideEffects' => false],
'StubTests\\StubsParameterNamesTest::printParameters' => ['hasSideEffects' => false],
- 'StubTests\\StubsTest::getParameterRepresentation' => ['hasSideEffects' => false],
'Transliterator::createInverse' => ['hasSideEffects' => false],
'Transliterator::getErrorCode' => ['hasSideEffects' => false],
'Transliterator::getErrorMessage' => ['hasSideEffects' => false],
@@ -658,6 +663,7 @@
'array_intersect_key' => ['hasSideEffects' => false],
'array_intersect_uassoc' => ['hasSideEffects' => false],
'array_intersect_ukey' => ['hasSideEffects' => false],
+ 'array_is_list' => ['hasSideEffects' => false],
'array_key_exists' => ['hasSideEffects' => false],
'array_key_first' => ['hasSideEffects' => false],
'array_key_last' => ['hasSideEffects' => false],
@@ -711,7 +717,10 @@
'ceil' => ['hasSideEffects' => false],
'checkdate' => ['hasSideEffects' => false],
'checkdnsrr' => ['hasSideEffects' => false],
+ 'chgrp' => ['hasSideEffects' => true],
+ 'chmod' => ['hasSideEffects' => true],
'chop' => ['hasSideEffects' => false],
+ 'chown' => ['hasSideEffects' => true],
'chr' => ['hasSideEffects' => false],
'chunk_split' => ['hasSideEffects' => false],
'class_implements' => ['hasSideEffects' => false],
@@ -732,6 +741,7 @@
'convert_cyr_string' => ['hasSideEffects' => false],
'convert_uudecode' => ['hasSideEffects' => false],
'convert_uuencode' => ['hasSideEffects' => false],
+ 'copy' => ['hasSideEffects' => true],
'cos' => ['hasSideEffects' => false],
'cosh' => ['hasSideEffects' => false],
'count' => ['hasSideEffects' => false],
@@ -806,6 +816,7 @@
'deg2rad' => ['hasSideEffects' => false],
'dirname' => ['hasSideEffects' => false],
'disk_free_space' => ['hasSideEffects' => false],
+ 'disk_total_space' => ['hasSideEffects' => false],
'diskfreespace' => ['hasSideEffects' => false],
'dngettext' => ['hasSideEffects' => false],
'doubleval' => ['hasSideEffects' => false],
@@ -816,8 +827,18 @@
'explode' => ['hasSideEffects' => false],
'expm1' => ['hasSideEffects' => false],
'extension_loaded' => ['hasSideEffects' => false],
+ 'fclose' => ['hasSideEffects' => true],
'fdiv' => ['hasSideEffects' => false],
+ 'feof' => ['hasSideEffects' => false],
+ 'fflush' => ['hasSideEffects' => true],
+ 'fgetc' => ['hasSideEffects' => true],
+ 'fgetcsv' => ['hasSideEffects' => true],
+ 'fgets' => ['hasSideEffects' => true],
+ 'fgetss' => ['hasSideEffects' => true],
+ 'file' => ['hasSideEffects' => false],
'file_exists' => ['hasSideEffects' => false],
+ 'file_get_contents' => ['hasSideEffects' => false],
+ 'file_put_contents' => ['hasSideEffects' => true],
'fileatime' => ['hasSideEffects' => false],
'filectime' => ['hasSideEffects' => false],
'filegroup' => ['hasSideEffects' => false],
@@ -837,13 +858,26 @@
'finfo::buffer' => ['hasSideEffects' => false],
'finfo::file' => ['hasSideEffects' => false],
'floatval' => ['hasSideEffects' => false],
+ 'flock' => ['hasSideEffects' => true],
'floor' => ['hasSideEffects' => false],
'fmod' => ['hasSideEffects' => false],
+ 'fnmatch' => ['hasSideEffects' => false],
+ 'fopen' => ['hasSideEffects' => true],
+ 'fpassthru' => ['hasSideEffects' => true],
+ 'fputcsv' => ['hasSideEffects' => true],
+ 'fputs' => ['hasSideEffects' => true],
+ 'fread' => ['hasSideEffects' => true],
+ 'fscanf' => ['hasSideEffects' => true],
+ 'fseek' => ['hasSideEffects' => true],
+ 'fstat' => ['hasSideEffects' => false],
+ 'ftell' => ['hasSideEffects' => false],
'ftok' => ['hasSideEffects' => false],
+ 'ftruncate' => ['hasSideEffects' => true],
'func_get_arg' => ['hasSideEffects' => false],
'func_get_args' => ['hasSideEffects' => false],
'func_num_args' => ['hasSideEffects' => false],
'function_exists' => ['hasSideEffects' => false],
+ 'fwrite' => ['hasSideEffects' => true],
'gc_enabled' => ['hasSideEffects' => false],
'gc_status' => ['hasSideEffects' => false],
'gd_info' => ['hasSideEffects' => false],
@@ -948,9 +982,6 @@
'gmp_pow' => ['hasSideEffects' => false],
'gmp_powm' => ['hasSideEffects' => false],
'gmp_prob_prime' => ['hasSideEffects' => false],
- 'gmp_random' => ['hasSideEffects' => false],
- 'gmp_random_bits' => ['hasSideEffects' => false],
- 'gmp_random_range' => ['hasSideEffects' => false],
'gmp_root' => ['hasSideEffects' => false],
'gmp_rootrem' => ['hasSideEffects' => false],
'gmp_scan0' => ['hasSideEffects' => false],
@@ -1172,8 +1203,11 @@
'key' => ['hasSideEffects' => false],
'key_exists' => ['hasSideEffects' => false],
'lcfirst' => ['hasSideEffects' => false],
+ 'lchgrp' => ['hasSideEffects' => true],
+ 'lchown' => ['hasSideEffects' => true],
'libxml_get_errors' => ['hasSideEffects' => false],
'libxml_get_last_error' => ['hasSideEffects' => false],
+ 'link' => ['hasSideEffects' => true],
'linkinfo' => ['hasSideEffects' => false],
'locale_accept_from_http' => ['hasSideEffects' => false],
'locale_canonicalize' => ['hasSideEffects' => false],
@@ -1260,7 +1294,9 @@
'mhash_keygen_s2k' => ['hasSideEffects' => false],
'microtime' => ['hasSideEffects' => false],
'min' => ['hasSideEffects' => false],
+ 'mkdir' => ['hasSideEffects' => true],
'mktime' => ['hasSideEffects' => false],
+ 'move_uploaded_file' => ['hasSideEffects' => true],
'msgfmt_create' => ['hasSideEffects' => false],
'msgfmt_format' => ['hasSideEffects' => false],
'msgfmt_format_message' => ['hasSideEffects' => false],
@@ -1292,13 +1328,16 @@
'numfmt_get_text_attribute' => ['hasSideEffects' => false],
'numfmt_parse' => ['hasSideEffects' => false],
'ob_etaghandler' => ['hasSideEffects' => false],
+ 'ob_get_contents' => ['hasSideEffects' => false],
'ob_iconv_handler' => ['hasSideEffects' => false],
'octdec' => ['hasSideEffects' => false],
'ord' => ['hasSideEffects' => false],
'pack' => ['hasSideEffects' => false],
+ 'parse_ini_file' => ['hasSideEffects' => false],
'parse_ini_string' => ['hasSideEffects' => false],
'parse_url' => ['hasSideEffects' => false],
'pathinfo' => ['hasSideEffects' => false],
+ 'pclose' => ['hasSideEffects' => true],
'pcntl_errno' => ['hasSideEffects' => false],
'pcntl_get_last_error' => ['hasSideEffects' => false],
'pcntl_getpriority' => ['hasSideEffects' => false],
@@ -1319,6 +1358,7 @@
'php_uname' => ['hasSideEffects' => false],
'phpversion' => ['hasSideEffects' => false],
'pi' => ['hasSideEffects' => false],
+ 'popen' => ['hasSideEffects' => true],
'pos' => ['hasSideEffects' => false],
'posix_ctermid' => ['hasSideEffects' => false],
'posix_errno' => ['hasSideEffects' => false],
@@ -1363,15 +1403,20 @@
'range' => ['hasSideEffects' => false],
'rawurldecode' => ['hasSideEffects' => false],
'rawurlencode' => ['hasSideEffects' => false],
+ 'readfile' => ['hasSideEffects' => true],
+ 'readlink' => ['hasSideEffects' => false],
'realpath' => ['hasSideEffects' => false],
'realpath_cache_get' => ['hasSideEffects' => false],
'realpath_cache_size' => ['hasSideEffects' => false],
+ 'rename' => ['hasSideEffects' => true],
'resourcebundle_count' => ['hasSideEffects' => false],
'resourcebundle_create' => ['hasSideEffects' => false],
'resourcebundle_get' => ['hasSideEffects' => false],
'resourcebundle_get_error_code' => ['hasSideEffects' => false],
'resourcebundle_get_error_message' => ['hasSideEffects' => false],
'resourcebundle_locales' => ['hasSideEffects' => false],
+ 'rewind' => ['hasSideEffects' => true],
+ 'rmdir' => ['hasSideEffects' => true],
'round' => ['hasSideEffects' => false],
'rtrim' => ['hasSideEffects' => false],
'sha1' => ['hasSideEffects' => false],
@@ -1391,7 +1436,6 @@
'str_pad' => ['hasSideEffects' => false],
'str_repeat' => ['hasSideEffects' => false],
'str_rot13' => ['hasSideEffects' => false],
- 'str_shuffle' => ['hasSideEffects' => false],
'str_split' => ['hasSideEffects' => false],
'str_starts_with' => ['hasSideEffects' => false],
'str_word_count' => ['hasSideEffects' => false],
@@ -1433,9 +1477,11 @@
'substr_compare' => ['hasSideEffects' => false],
'substr_count' => ['hasSideEffects' => false],
'substr_replace' => ['hasSideEffects' => false],
+ 'symlink' => ['hasSideEffects' => true],
'sys_getloadavg' => ['hasSideEffects' => false],
'tan' => ['hasSideEffects' => false],
'tanh' => ['hasSideEffects' => false],
+ 'tempnam' => ['hasSideEffects' => true],
'timezone_abbreviations_list' => ['hasSideEffects' => false],
'timezone_identifiers_list' => ['hasSideEffects' => false],
'timezone_location_get' => ['hasSideEffects' => false],
@@ -1445,8 +1491,10 @@
'timezone_open' => ['hasSideEffects' => false],
'timezone_transitions_get' => ['hasSideEffects' => false],
'timezone_version_get' => ['hasSideEffects' => false],
+ 'tmpfile' => ['hasSideEffects' => true],
'token_get_all' => ['hasSideEffects' => false],
'token_name' => ['hasSideEffects' => false],
+ 'touch' => ['hasSideEffects' => true],
'transliterator_create' => ['hasSideEffects' => false],
'transliterator_create_from_rules' => ['hasSideEffects' => false],
'transliterator_create_inverse' => ['hasSideEffects' => false],
@@ -1457,7 +1505,8 @@
'trim' => ['hasSideEffects' => false],
'ucfirst' => ['hasSideEffects' => false],
'ucwords' => ['hasSideEffects' => false],
- 'uniqid' => ['hasSideEffects' => false],
+ 'umask' => ['hasSideEffects' => true],
+ 'unlink' => ['hasSideEffects' => true],
'unpack' => ['hasSideEffects' => false],
'urldecode' => ['hasSideEffects' => false],
'urlencode' => ['hasSideEffects' => false],
diff --git a/src/AnalysedCodeException.php b/src/AnalysedCodeException.php
index d4e7558622..6d78350e50 100644
--- a/src/AnalysedCodeException.php
+++ b/src/AnalysedCodeException.php
@@ -2,7 +2,9 @@
namespace PHPStan;
-abstract class AnalysedCodeException extends \Exception
+use Exception;
+
+abstract class AnalysedCodeException extends Exception
{
abstract public function getTip(): ?string;
diff --git a/src/Analyser/Analyser.php b/src/Analyser/Analyser.php
index 6941c4a0d6..0ddf4b727c 100644
--- a/src/Analyser/Analyser.php
+++ b/src/Analyser/Analyser.php
@@ -2,49 +2,38 @@
namespace PHPStan\Analyser;
+use Closure;
use PHPStan\Rules\Registry;
+use Throwable;
+use function array_fill_keys;
+use function array_merge;
+use function count;
+use function sprintf;
class Analyser
{
- private \PHPStan\Analyser\FileAnalyser $fileAnalyser;
-
- private Registry $registry;
-
- private \PHPStan\Analyser\NodeScopeResolver $nodeScopeResolver;
-
- private int $internalErrorsCountLimit;
-
- /** @var \PHPStan\Analyser\Error[] */
- private array $collectedErrors = [];
-
public function __construct(
- FileAnalyser $fileAnalyser,
- Registry $registry,
- NodeScopeResolver $nodeScopeResolver,
- int $internalErrorsCountLimit
+ private FileAnalyser $fileAnalyser,
+ private Registry $registry,
+ private NodeScopeResolver $nodeScopeResolver,
+ private int $internalErrorsCountLimit,
)
{
- $this->fileAnalyser = $fileAnalyser;
- $this->registry = $registry;
- $this->nodeScopeResolver = $nodeScopeResolver;
- $this->internalErrorsCountLimit = $internalErrorsCountLimit;
}
/**
* @param string[] $files
- * @param \Closure(string $file): void|null $preFileCallback
- * @param \Closure(int): void|null $postFileCallback
- * @param bool $debug
+ * @param Closure(string $file): void|null $preFileCallback
+ * @param Closure(int ): void|null $postFileCallback
* @param string[]|null $allAnalysedFiles
- * @return AnalyserResult
*/
public function analyse(
array $files,
- ?\Closure $preFileCallback = null,
- ?\Closure $postFileCallback = null,
+ ?Closure $preFileCallback = null,
+ ?Closure $postFileCallback = null,
bool $debug = false,
- ?array $allAnalysedFiles = null
+ ?array $allAnalysedFiles = null,
): AnalyserResult
{
if ($allAnalysedFiles === null) {
@@ -54,8 +43,6 @@ public function analyse(
$this->nodeScopeResolver->setAnalysedFiles($allAnalysedFiles);
$allAnalysedFiles = array_fill_keys($allAnalysedFiles, true);
- $this->collectErrors($files);
-
$errors = [];
$internalErrorsCount = 0;
$reachedInternalErrorsCountLimit = false;
@@ -71,7 +58,7 @@ public function analyse(
$file,
$allAnalysedFiles,
$this->registry,
- null
+ null,
);
$errors = array_merge($errors, $fileAnalyserResult->getErrors());
$dependencies[$file] = $fileAnalyserResult->getDependencies();
@@ -80,7 +67,7 @@ public function analyse(
if (count($fileExportedNodes) > 0) {
$exportedNodes[$file] = $fileExportedNodes;
}
- } catch (\Throwable $t) {
+ } catch (Throwable $t) {
if ($debug) {
throw $t;
}
@@ -90,7 +77,7 @@ public function analyse(
'%sRun PHPStan with --debug option and post the stack trace to:%s%s',
"\n",
"\n",
- 'https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md'
+ 'https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md',
);
$errors[] = new Error($internalErrorMessage, $file, null, $t);
if ($internalErrorsCount >= $this->internalErrorsCountLimit) {
@@ -106,44 +93,13 @@ public function analyse(
$postFileCallback(1);
}
- $this->restoreCollectErrorsHandler();
-
- $errors = array_merge($errors, $this->collectedErrors);
-
return new AnalyserResult(
$errors,
[],
$internalErrorsCount === 0 ? $dependencies : null,
$exportedNodes,
- $reachedInternalErrorsCountLimit
+ $reachedInternalErrorsCountLimit,
);
}
- /**
- * @param string[] $analysedFiles
- */
- private function collectErrors(array $analysedFiles): void
- {
- $this->collectedErrors = [];
- set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) use ($analysedFiles): bool {
- if (error_reporting() === 0) {
- // silence @ operator
- return true;
- }
-
- if (!in_array($errfile, $analysedFiles, true)) {
- return true;
- }
-
- $this->collectedErrors[] = new Error($errstr, $errfile, $errline, true);
-
- return true;
- });
- }
-
- private function restoreCollectErrorsHandler(): void
- {
- restore_error_handler();
- }
-
}
diff --git a/src/Analyser/AnalyserResult.php b/src/Analyser/AnalyserResult.php
index d045f84749..3966d008b3 100644
--- a/src/Analyser/AnalyserResult.php
+++ b/src/Analyser/AnalyserResult.php
@@ -3,68 +3,46 @@
namespace PHPStan\Analyser;
use PHPStan\Dependency\ExportedNode;
+use function usort;
class AnalyserResult
{
- /** @var \PHPStan\Analyser\Error[] */
+ /** @var Error[] */
private array $unorderedErrors;
- /** @var \PHPStan\Analyser\Error[] */
- private array $errors;
-
- /** @var string[] */
- private array $internalErrors;
-
- /** @var array>|null */
- private ?array $dependencies;
-
- /** @var array> */
- private array $exportedNodes;
-
- private bool $reachedInternalErrorsCountLimit;
-
/**
- * @param \PHPStan\Analyser\Error[] $errors
+ * @param Error[] $errors
* @param string[] $internalErrors
* @param array>|null $dependencies
* @param array> $exportedNodes
- * @param bool $reachedInternalErrorsCountLimit
*/
public function __construct(
- array $errors,
- array $internalErrors,
- ?array $dependencies,
- array $exportedNodes,
- bool $reachedInternalErrorsCountLimit
+ private array $errors,
+ private array $internalErrors,
+ private ?array $dependencies,
+ private array $exportedNodes,
+ private bool $reachedInternalErrorsCountLimit,
)
{
$this->unorderedErrors = $errors;
usort(
- $errors,
- static function (Error $a, Error $b): int {
- return [
- $a->getFile(),
- $a->getLine(),
- $a->getMessage(),
- ] <=> [
- $b->getFile(),
- $b->getLine(),
- $b->getMessage(),
- ];
- }
+ $this->errors,
+ static fn (Error $a, Error $b): int => [
+ $a->getFile(),
+ $a->getLine(),
+ $a->getMessage(),
+ ] <=> [
+ $b->getFile(),
+ $b->getLine(),
+ $b->getMessage(),
+ ],
);
-
- $this->errors = $errors;
- $this->internalErrors = $internalErrors;
- $this->dependencies = $dependencies;
- $this->exportedNodes = $exportedNodes;
- $this->reachedInternalErrorsCountLimit = $reachedInternalErrorsCountLimit;
}
/**
- * @return \PHPStan\Analyser\Error[]
+ * @return Error[]
*/
public function getUnorderedErrors(): array
{
@@ -72,7 +50,7 @@ public function getUnorderedErrors(): array
}
/**
- * @return \PHPStan\Analyser\Error[]
+ * @return Error[]
*/
public function getErrors(): array
{
diff --git a/src/Analyser/ConditionalExpressionHolder.php b/src/Analyser/ConditionalExpressionHolder.php
index 834dd49d03..b36e35bc26 100644
--- a/src/Analyser/ConditionalExpressionHolder.php
+++ b/src/Analyser/ConditionalExpressionHolder.php
@@ -2,31 +2,27 @@
namespace PHPStan\Analyser;
+use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
+use function count;
+use function implode;
+use function sprintf;
class ConditionalExpressionHolder
{
- /** @var array */
- private array $conditionExpressionTypes;
-
- private VariableTypeHolder $typeHolder;
-
/**
* @param array $conditionExpressionTypes
- * @param VariableTypeHolder $typeHolder
*/
public function __construct(
- array $conditionExpressionTypes,
- VariableTypeHolder $typeHolder
+ private array $conditionExpressionTypes,
+ private VariableTypeHolder $typeHolder,
)
{
if (count($conditionExpressionTypes) === 0) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
- $this->conditionExpressionTypes = $conditionExpressionTypes;
- $this->typeHolder = $typeHolder;
}
/**
@@ -53,7 +49,7 @@ public function getKey(): string
'%s => %s (%s)',
implode(' && ', $parts),
$this->typeHolder->getType()->describe(VerbosityLevel::precise()),
- $this->typeHolder->getCertainty()->describe()
+ $this->typeHolder->getCertainty()->describe(),
);
}
diff --git a/src/Analyser/DirectScopeFactory.php b/src/Analyser/DirectScopeFactory.php
index f8e7e43553..2299cd7dd2 100644
--- a/src/Analyser/DirectScopeFactory.php
+++ b/src/Analyser/DirectScopeFactory.php
@@ -2,13 +2,20 @@
namespace PHPStan\Analyser;
+use PhpParser\PrettyPrinter\Standard;
use PHPStan\DependencyInjection\Container;
use PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider;
use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
+use PHPStan\Parser\Parser;
+use PHPStan\Php\PhpVersion;
+use PHPStan\Reflection\FunctionReflection;
+use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Properties\PropertyReflectionFinder;
+use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Type;
+use function is_a;
/**
* @internal
@@ -16,85 +23,43 @@
class DirectScopeFactory implements ScopeFactory
{
- private string $scopeClass;
-
- private \PHPStan\Reflection\ReflectionProvider $reflectionProvider;
-
- private \PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider $dynamicReturnTypeExtensionRegistryProvider;
-
- private OperatorTypeSpecifyingExtensionRegistryProvider $operatorTypeSpecifyingExtensionRegistryProvider;
-
- private \PhpParser\PrettyPrinter\Standard $printer;
-
- private \PHPStan\Analyser\TypeSpecifier $typeSpecifier;
-
- private \PHPStan\Rules\Properties\PropertyReflectionFinder $propertyReflectionFinder;
-
- private \PHPStan\Parser\Parser $parser;
-
- private NodeScopeResolver $nodeScopeResolver;
-
- private bool $treatPhpDocTypesAsCertain;
-
- private bool $objectFromNewClass;
-
/** @var string[] */
private array $dynamicConstantNames;
public function __construct(
- string $scopeClass,
- ReflectionProvider $reflectionProvider,
- DynamicReturnTypeExtensionRegistryProvider $dynamicReturnTypeExtensionRegistryProvider,
- OperatorTypeSpecifyingExtensionRegistryProvider $operatorTypeSpecifyingExtensionRegistryProvider,
- \PhpParser\PrettyPrinter\Standard $printer,
- TypeSpecifier $typeSpecifier,
- PropertyReflectionFinder $propertyReflectionFinder,
- \PHPStan\Parser\Parser $parser,
- NodeScopeResolver $nodeScopeResolver,
- bool $treatPhpDocTypesAsCertain,
- bool $objectFromNewClass,
- Container $container
+ private string $scopeClass,
+ private ReflectionProvider $reflectionProvider,
+ private DynamicReturnTypeExtensionRegistryProvider $dynamicReturnTypeExtensionRegistryProvider,
+ private OperatorTypeSpecifyingExtensionRegistryProvider $operatorTypeSpecifyingExtensionRegistryProvider,
+ private Standard $printer,
+ private TypeSpecifier $typeSpecifier,
+ private PropertyReflectionFinder $propertyReflectionFinder,
+ private Parser $parser,
+ private NodeScopeResolver $nodeScopeResolver,
+ private bool $treatPhpDocTypesAsCertain,
+ Container $container,
+ private PhpVersion $phpVersion,
+ private bool $explicitMixedInUnknownGenericNew,
)
{
- $this->scopeClass = $scopeClass;
- $this->reflectionProvider = $reflectionProvider;
- $this->dynamicReturnTypeExtensionRegistryProvider = $dynamicReturnTypeExtensionRegistryProvider;
- $this->operatorTypeSpecifyingExtensionRegistryProvider = $operatorTypeSpecifyingExtensionRegistryProvider;
- $this->printer = $printer;
- $this->typeSpecifier = $typeSpecifier;
- $this->propertyReflectionFinder = $propertyReflectionFinder;
- $this->parser = $parser;
- $this->nodeScopeResolver = $nodeScopeResolver;
- $this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain;
- $this->objectFromNewClass = $objectFromNewClass;
$this->dynamicConstantNames = $container->getParameter('dynamicConstantNames');
}
/**
- * @param \PHPStan\Analyser\ScopeContext $context
- * @param bool $declareStrictTypes
- * @param array $constantTypes
- * @param \PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection|null $function
- * @param string|null $namespace
- * @param \PHPStan\Analyser\VariableTypeHolder[] $variablesTypes
- * @param \PHPStan\Analyser\VariableTypeHolder[] $moreSpecificTypes
+ * @param array $constantTypes
+ * @param VariableTypeHolder[] $variablesTypes
+ * @param VariableTypeHolder[] $moreSpecificTypes
* @param array $conditionalExpressions
- * @param string|null $inClosureBindScopeClass
- * @param \PHPStan\Reflection\ParametersAcceptor|null $anonymousFunctionReflection
- * @param bool $inFirstLevelStatement
* @param array $currentlyAssignedExpressions
* @param array $nativeExpressionTypes
- * @param array<\PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection> $inFunctionCallsStack
- * @param bool $afterExtractCall
- * @param Scope|null $parentScope
+ * @param array<(FunctionReflection|MethodReflection)> $inFunctionCallsStack
*
- * @return MutatingScope
*/
public function create(
ScopeContext $context,
bool $declareStrictTypes = false,
array $constantTypes = [],
- $function = null,
+ FunctionReflection|MethodReflection|null $function = null,
?string $namespace = null,
array $variablesTypes = [],
array $moreSpecificTypes = [],
@@ -106,12 +71,12 @@ public function create(
array $nativeExpressionTypes = [],
array $inFunctionCallsStack = [],
bool $afterExtractCall = false,
- ?Scope $parentScope = null
+ ?Scope $parentScope = null,
): MutatingScope
{
$scopeClass = $this->scopeClass;
if (!is_a($scopeClass, MutatingScope::class, true)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return new $scopeClass(
@@ -125,6 +90,7 @@ public function create(
$this->parser,
$this->nodeScopeResolver,
$context,
+ $this->phpVersion,
$declareStrictTypes,
$constantTypes,
$function,
@@ -140,9 +106,9 @@ public function create(
$inFunctionCallsStack,
$this->dynamicConstantNames,
$this->treatPhpDocTypesAsCertain,
- $this->objectFromNewClass,
$afterExtractCall,
- $parentScope
+ $parentScope,
+ $this->explicitMixedInUnknownGenericNew,
);
}
diff --git a/src/Analyser/EnsuredNonNullabilityResult.php b/src/Analyser/EnsuredNonNullabilityResult.php
index a949f46976..258a16b18b 100644
--- a/src/Analyser/EnsuredNonNullabilityResult.php
+++ b/src/Analyser/EnsuredNonNullabilityResult.php
@@ -5,19 +5,11 @@
class EnsuredNonNullabilityResult
{
- private MutatingScope $scope;
-
- /** @var EnsuredNonNullabilityResultExpression[] */
- private array $specifiedExpressions;
-
/**
- * @param MutatingScope $scope
* @param EnsuredNonNullabilityResultExpression[] $specifiedExpressions
*/
- public function __construct(MutatingScope $scope, array $specifiedExpressions)
+ public function __construct(private MutatingScope $scope, private array $specifiedExpressions)
{
- $this->scope = $scope;
- $this->specifiedExpressions = $specifiedExpressions;
}
public function getScope(): MutatingScope
diff --git a/src/Analyser/EnsuredNonNullabilityResultExpression.php b/src/Analyser/EnsuredNonNullabilityResultExpression.php
index adc3ebfb22..a7ed1572f8 100644
--- a/src/Analyser/EnsuredNonNullabilityResultExpression.php
+++ b/src/Analyser/EnsuredNonNullabilityResultExpression.php
@@ -8,21 +8,12 @@
class EnsuredNonNullabilityResultExpression
{
- private Expr $expression;
-
- private Type $originalType;
-
- private Type $originalNativeType;
-
public function __construct(
- Expr $expression,
- Type $originalType,
- Type $originalNativeType
+ private Expr $expression,
+ private Type $originalType,
+ private Type $originalNativeType,
)
{
- $this->expression = $expression;
- $this->originalType = $originalType;
- $this->originalNativeType = $originalNativeType;
}
public function getExpression(): Expr
diff --git a/src/Analyser/Error.php b/src/Analyser/Error.php
index 7a80edbf8b..f36ba89284 100644
--- a/src/Analyser/Error.php
+++ b/src/Analyser/Error.php
@@ -2,74 +2,38 @@
namespace PHPStan\Analyser;
-class Error implements \JsonSerializable
+use Exception;
+use JsonSerializable;
+use PhpParser\Node;
+use PHPStan\ShouldNotHappenException;
+use ReturnTypeWillChange;
+use Throwable;
+use function is_bool;
+
+/** @api */
+class Error implements JsonSerializable
{
- private string $message;
-
- private string $file;
-
- private ?int $line;
-
- /** @var bool|\Throwable */
- private $canBeIgnored;
-
- private ?string $filePath;
-
- private ?string $traitFilePath;
-
- private ?string $tip;
-
- private ?int $nodeLine;
-
- /** @phpstan-var class-string<\PhpParser\Node>|null */
- private ?string $nodeType;
-
- private ?string $identifier;
-
- /** @var mixed[] */
- private array $metadata;
-
/**
* Error constructor.
*
- * @param string $message
- * @param string $file
- * @param int|null $line
- * @param bool|\Throwable $canBeIgnored
- * @param string|null $filePath
- * @param string|null $traitFilePath
- * @param string|null $tip
- * @param int|null $nodeLine
- * @param class-string<\PhpParser\Node>|null $nodeType
- * @param string|null $identifier
+ * @param class-string|null $nodeType
* @param mixed[] $metadata
*/
public function __construct(
- string $message,
- string $file,
- ?int $line = null,
- $canBeIgnored = true,
- ?string $filePath = null,
- ?string $traitFilePath = null,
- ?string $tip = null,
- ?int $nodeLine = null,
- ?string $nodeType = null,
- ?string $identifier = null,
- array $metadata = []
+ private string $message,
+ private string $file,
+ private ?int $line = null,
+ private bool|Throwable $canBeIgnored = true,
+ private ?string $filePath = null,
+ private ?string $traitFilePath = null,
+ private ?string $tip = null,
+ private ?int $nodeLine = null,
+ private ?string $nodeType = null,
+ private ?string $identifier = null,
+ private array $metadata = [],
)
{
- $this->message = $message;
- $this->file = $file;
- $this->line = $line;
- $this->canBeIgnored = $canBeIgnored;
- $this->filePath = $filePath;
- $this->traitFilePath = $traitFilePath;
- $this->tip = $tip;
- $this->nodeLine = $nodeLine;
- $this->nodeType = $nodeType;
- $this->identifier = $identifier;
- $this->metadata = $metadata;
}
public function getMessage(): string
@@ -94,7 +58,7 @@ public function getFilePath(): string
public function changeFilePath(string $newFilePath): self
{
if ($this->traitFilePath !== null) {
- throw new \PHPStan\ShouldNotHappenException('Errors in traits not yet supported');
+ throw new ShouldNotHappenException('Errors in traits not yet supported');
}
return new self(
@@ -108,7 +72,7 @@ public function changeFilePath(string $newFilePath): self
$this->nodeLine,
$this->nodeType,
$this->identifier,
- $this->metadata
+ $this->metadata,
);
}
@@ -125,7 +89,7 @@ public function changeTraitFilePath(string $newFilePath): self
$this->nodeLine,
$this->nodeType,
$this->identifier,
- $this->metadata
+ $this->metadata,
);
}
@@ -146,7 +110,7 @@ public function canBeIgnored(): bool
public function hasNonIgnorableException(): bool
{
- return $this->canBeIgnored instanceof \Throwable;
+ return $this->canBeIgnored instanceof Throwable;
}
public function getTip(): ?string
@@ -169,7 +133,26 @@ public function withoutTip(): self
$this->traitFilePath,
null,
$this->nodeLine,
- $this->nodeType
+ $this->nodeType,
+ );
+ }
+
+ public function doNotIgnore(): self
+ {
+ if (!$this->canBeIgnored()) {
+ return $this;
+ }
+
+ return new self(
+ $this->message,
+ $this->file,
+ $this->line,
+ false,
+ $this->filePath,
+ $this->traitFilePath,
+ $this->tip,
+ $this->nodeLine,
+ $this->nodeType,
);
}
@@ -179,7 +162,7 @@ public function getNodeLine(): ?int
}
/**
- * @return class-string<\PhpParser\Node>|null
+ * @return class-string|null
*/
public function getNodeType(): ?string
{
@@ -202,6 +185,7 @@ public function getMetadata(): array
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
@@ -221,7 +205,6 @@ public function jsonSerialize()
/**
* @param mixed[] $json
- * @return self
*/
public static function decode(array $json): self
{
@@ -229,20 +212,19 @@ public static function decode(array $json): self
$json['message'],
$json['file'],
$json['line'],
- $json['canBeIgnored'] === 'exception' ? new \Exception() : $json['canBeIgnored'],
+ $json['canBeIgnored'] === 'exception' ? new Exception() : $json['canBeIgnored'],
$json['filePath'],
$json['traitFilePath'],
$json['tip'],
$json['nodeLine'] ?? null,
$json['nodeType'] ?? null,
$json['identifier'] ?? null,
- $json['metadata'] ?? []
+ $json['metadata'] ?? [],
);
}
/**
* @param mixed[] $properties
- * @return self
*/
public static function __set_state(array $properties): self
{
@@ -257,7 +239,7 @@ public static function __set_state(array $properties): self
$properties['nodeLine'] ?? null,
$properties['nodeType'] ?? null,
$properties['identifier'] ?? null,
- $properties['metadata'] ?? []
+ $properties['metadata'] ?? [],
);
}
diff --git a/src/Analyser/ExpressionContext.php b/src/Analyser/ExpressionContext.php
index 373ea50d0a..df1f55d284 100644
--- a/src/Analyser/ExpressionContext.php
+++ b/src/Analyser/ExpressionContext.php
@@ -7,21 +7,12 @@
class ExpressionContext
{
- private bool $isDeep;
-
- private ?string $inAssignRightSideVariableName;
-
- private ?Type $inAssignRightSideType;
-
private function __construct(
- bool $isDeep,
- ?string $inAssignRightSideVariableName,
- ?Type $inAssignRightSideType
+ private bool $isDeep,
+ private ?string $inAssignRightSideVariableName,
+ private ?Type $inAssignRightSideType,
)
{
- $this->isDeep = $isDeep;
- $this->inAssignRightSideVariableName = $inAssignRightSideVariableName;
- $this->inAssignRightSideType = $inAssignRightSideType;
}
public static function createTopLevel(): self
diff --git a/src/Analyser/ExpressionResult.php b/src/Analyser/ExpressionResult.php
index 4cec309c24..93a3bdca25 100644
--- a/src/Analyser/ExpressionResult.php
+++ b/src/Analyser/ExpressionResult.php
@@ -5,13 +5,6 @@
class ExpressionResult
{
- private MutatingScope $scope;
-
- private bool $hasYield;
-
- /** @var ThrowPoint[] $throwPoints */
- private array $throwPoints;
-
/** @var (callable(): MutatingScope)|null */
private $truthyScopeCallback;
@@ -23,23 +16,18 @@ class ExpressionResult
private ?MutatingScope $falseyScope = null;
/**
- * @param MutatingScope $scope
- * @param bool $hasYield
* @param ThrowPoint[] $throwPoints
* @param (callable(): MutatingScope)|null $truthyScopeCallback
* @param (callable(): MutatingScope)|null $falseyScopeCallback
*/
public function __construct(
- MutatingScope $scope,
- bool $hasYield,
- array $throwPoints,
+ private MutatingScope $scope,
+ private bool $hasYield,
+ private array $throwPoints,
?callable $truthyScopeCallback = null,
- ?callable $falseyScopeCallback = null
+ ?callable $falseyScopeCallback = null,
)
{
- $this->scope = $scope;
- $this->hasYield = $hasYield;
- $this->throwPoints = $throwPoints;
$this->truthyScopeCallback = $truthyScopeCallback;
$this->falseyScopeCallback = $falseyScopeCallback;
}
diff --git a/src/Analyser/FileAnalyser.php b/src/Analyser/FileAnalyser.php
index af21ba3f71..4d9cdb9297 100644
--- a/src/Analyser/FileAnalyser.php
+++ b/src/Analyser/FileAnalyser.php
@@ -4,6 +4,7 @@
use PhpParser\Comment;
use PhpParser\Node;
+use PHPStan\AnalysedCodeException;
use PHPStan\BetterReflection\NodeCompiler\Exception\UnableToCompileNode;
use PHPStan\BetterReflection\Reflection\Exception\NotAClassReflection;
use PHPStan\BetterReflection\Reflection\Exception\NotAnInterfaceReflection;
@@ -11,6 +12,7 @@
use PHPStan\Dependency\DependencyResolver;
use PHPStan\Node\FileNode;
use PHPStan\Parser\Parser;
+use PHPStan\Parser\ParserErrorsException;
use PHPStan\Rules\FileRuleError;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\LineRuleError;
@@ -19,48 +21,46 @@
use PHPStan\Rules\Registry;
use PHPStan\Rules\TipRuleError;
use function array_key_exists;
+use function array_keys;
+use function array_merge;
use function array_unique;
+use function array_values;
+use function error_reporting;
+use function get_class;
+use function is_dir;
+use function is_file;
+use function is_string;
+use function restore_error_handler;
+use function set_error_handler;
+use function sprintf;
+use function strpos;
+use const E_DEPRECATED;
class FileAnalyser
{
- private \PHPStan\Analyser\ScopeFactory $scopeFactory;
-
- private \PHPStan\Analyser\NodeScopeResolver $nodeScopeResolver;
-
- private \PHPStan\Parser\Parser $parser;
-
- private DependencyResolver $dependencyResolver;
-
- private bool $reportUnmatchedIgnoredErrors;
+ /** @var Error[] */
+ private array $collectedErrors = [];
public function __construct(
- ScopeFactory $scopeFactory,
- NodeScopeResolver $nodeScopeResolver,
- Parser $parser,
- DependencyResolver $dependencyResolver,
- bool $reportUnmatchedIgnoredErrors
+ private ScopeFactory $scopeFactory,
+ private NodeScopeResolver $nodeScopeResolver,
+ private Parser $parser,
+ private DependencyResolver $dependencyResolver,
+ private bool $reportUnmatchedIgnoredErrors,
)
{
- $this->scopeFactory = $scopeFactory;
- $this->nodeScopeResolver = $nodeScopeResolver;
- $this->parser = $parser;
- $this->dependencyResolver = $dependencyResolver;
- $this->reportUnmatchedIgnoredErrors = $reportUnmatchedIgnoredErrors;
}
/**
- * @param string $file
* @param array $analysedFiles
- * @param Registry $registry
- * @param callable(\PhpParser\Node $node, Scope $scope): void|null $outerNodeCallback
- * @return FileAnalyserResult
+ * @param callable(Node $node, Scope $scope): void|null $outerNodeCallback
*/
public function analyseFile(
string $file,
array $analysedFiles,
Registry $registry,
- ?callable $outerNodeCallback
+ ?callable $outerNodeCallback,
): FileAnalyserResult
{
$fileErrors = [];
@@ -68,10 +68,20 @@ public function analyseFile(
$exportedNodes = [];
if (is_file($file)) {
try {
+ $this->collectErrors($analysedFiles);
$parserNodes = $this->parser->parseFile($file);
- $linesToIgnore = [];
+ $linesToIgnore = $this->getLinesToIgnoreFromTokens($file, $parserNodes);
$temporaryFileErrors = [];
- $nodeCallback = function (\PhpParser\Node $node, Scope $scope) use (&$fileErrors, &$fileDependencies, &$exportedNodes, $file, $registry, $outerNodeCallback, $analysedFiles, &$linesToIgnore, &$temporaryFileErrors): void {
+ $nodeCallback = function (Node $node, Scope $scope) use (&$fileErrors, &$fileDependencies, &$exportedNodes, $file, $registry, $outerNodeCallback, $analysedFiles, &$linesToIgnore, &$temporaryFileErrors): void {
+ if ($node instanceof Node\Stmt\Trait_) {
+ foreach (array_keys($linesToIgnore[$file] ?? []) as $lineToIgnore) {
+ if ($lineToIgnore < $node->getStartLine() || $lineToIgnore > $node->getEndLine()) {
+ continue;
+ }
+
+ unset($linesToIgnore[$file][$lineToIgnore]);
+ }
+ }
if ($outerNodeCallback !== null) {
$outerNodeCallback($node, $scope);
}
@@ -80,7 +90,7 @@ public function analyseFile(
foreach ($registry->getRules($nodeType) as $rule) {
try {
$ruleErrors = $rule->processNode($node, $scope);
- } catch (\PHPStan\AnalysedCodeException $e) {
+ } catch (AnalysedCodeException $e) {
if (isset($uniquedAnalysedCodeExceptionMessages[$e->getMessage()])) {
continue;
}
@@ -108,7 +118,7 @@ public function analyseFile(
$metadata = [];
if ($scope->isInTrait()) {
$traitReflection = $scope->getTraitReflection();
- if ($traitReflection->getFileName() !== false) {
+ if ($traitReflection->getFileName() !== null) {
$traitFilePath = $traitReflection->getFileName();
}
}
@@ -158,13 +168,21 @@ public function analyseFile(
$nodeLine,
$nodeType,
$identifier,
- $metadata
+ $metadata,
);
}
}
- foreach ($this->getLinesToIgnore($node) as $lineToIgnore) {
- $linesToIgnore[$scope->getFileDescription()][$lineToIgnore] = true;
+ if ($scope->isInTrait()) {
+ $sameTraitFile = $file === $scope->getTraitReflection()->getFileName();
+ foreach ($this->getLinesToIgnore($node) as $lineToIgnore) {
+ $linesToIgnore[$scope->getFileDescription()][$lineToIgnore] = true;
+ if (!$sameTraitFile) {
+ continue;
+ }
+
+ unset($linesToIgnore[$file][$lineToIgnore]);
+ }
}
try {
@@ -175,11 +193,11 @@ public function analyseFile(
if ($dependencies->getExportedNode() !== null) {
$exportedNodes[] = $dependencies->getExportedNode();
}
- } catch (\PHPStan\AnalysedCodeException $e) {
+ } catch (AnalysedCodeException) {
// pass
- } catch (IdentifierNotFound $e) {
+ } catch (IdentifierNotFound) {
// pass
- } catch (UnableToCompileNode | NotAClassReflection | NotAnInterfaceReflection $e) {
+ } catch (UnableToCompileNode | NotAClassReflection | NotAnInterfaceReflection) {
// pass
}
};
@@ -189,7 +207,7 @@ public function analyseFile(
$this->nodeScopeResolver->processNodes(
$parserNodes,
$scope,
- $nodeCallback
+ $nodeCallback,
);
$unmatchedLineIgnores = $linesToIgnore;
foreach ($temporaryFileErrors as $tmpFileError) {
@@ -224,18 +242,18 @@ public function analyseFile(
null,
null,
null,
- 'ignoredError.unmatchedOnLine'
+ 'ignoredError.unmatchedOnLine',
);
}
}
}
} catch (\PhpParser\Error $e) {
$fileErrors[] = new Error($e->getMessage(), $file, $e->getStartLine() !== -1 ? $e->getStartLine() : null, $e);
- } catch (\PHPStan\Parser\ParserErrorsException $e) {
+ } catch (ParserErrorsException $e) {
foreach ($e->getErrors() as $error) {
$fileErrors[] = new Error($error->getMessage(), $e->getParsedFile() ?? $file, $error->getStartLine() !== -1 ? $error->getStartLine() : null, $e);
}
- } catch (\PHPStan\AnalysedCodeException $e) {
+ } catch (AnalysedCodeException $e) {
$fileErrors[] = new Error($e->getMessage(), $file, null, $e, null, null, $e->getTip());
} catch (IdentifierNotFound $e) {
$fileErrors[] = new Error(sprintf('Reflection error: %s not found.', $e->getIdentifier()->getName()), $file, null, $e, null, null, 'Learn more at https://phpstan.org/user-guide/discovering-symbols');
@@ -248,11 +266,14 @@ public function analyseFile(
$fileErrors[] = new Error(sprintf('File %s does not exist.', $file), $file, null, false);
}
+ $this->restoreCollectErrorsHandler();
+
+ $fileErrors = array_merge($fileErrors, $this->collectedErrors);
+
return new FileAnalyserResult($fileErrors, array_values(array_unique($fileDependencies)), $exportedNodes);
}
/**
- * @param Node $node
* @return int[]
*/
private function getLinesToIgnore(Node $node): array
@@ -277,6 +298,26 @@ private function getLinesToIgnore(Node $node): array
return $lines;
}
+ /**
+ * @param Node[] $nodes
+ * @return array>
+ */
+ private function getLinesToIgnoreFromTokens(string $file, array $nodes): array
+ {
+ if (!isset($nodes[0])) {
+ return [];
+ }
+
+ /** @var int[] $tokenLines */
+ $tokenLines = $nodes[0]->getAttribute('linesToIgnore', []);
+ $lines = [];
+ foreach ($tokenLines as $tokenLine) {
+ $lines[$file][$tokenLine] = true;
+ }
+
+ return $lines;
+ }
+
private function findLineToIgnoreComment(Comment $comment): ?int
{
$text = $comment->getText();
@@ -300,4 +341,35 @@ private function findLineToIgnoreComment(Comment $comment): ?int
return null;
}
+ /**
+ * @param array $analysedFiles
+ */
+ private function collectErrors(array $analysedFiles): void
+ {
+ $this->collectedErrors = [];
+ set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) use ($analysedFiles): bool {
+ if ((error_reporting() & $errno) === 0) {
+ // silence @ operator
+ return true;
+ }
+
+ if ($errno === E_DEPRECATED) {
+ return true;
+ }
+
+ if (!isset($analysedFiles[$errfile])) {
+ return true;
+ }
+
+ $this->collectedErrors[] = new Error($errstr, $errfile, $errline, true);
+
+ return true;
+ });
+ }
+
+ private function restoreCollectErrorsHandler(): void
+ {
+ restore_error_handler();
+ }
+
}
diff --git a/src/Analyser/FileAnalyserResult.php b/src/Analyser/FileAnalyserResult.php
index 024a953026..9669866ef8 100644
--- a/src/Analyser/FileAnalyserResult.php
+++ b/src/Analyser/FileAnalyserResult.php
@@ -7,25 +7,13 @@
class FileAnalyserResult
{
- /** @var Error[] */
- private array $errors;
-
- /** @var array */
- private array $dependencies;
-
- /** @var array */
- private array $exportedNodes;
-
/**
* @param Error[] $errors
* @param array $dependencies
* @param array $exportedNodes
*/
- public function __construct(array $errors, array $dependencies, array $exportedNodes)
+ public function __construct(private array $errors, private array $dependencies, private array $exportedNodes)
{
- $this->errors = $errors;
- $this->dependencies = $dependencies;
- $this->exportedNodes = $exportedNodes;
}
/**
diff --git a/src/Analyser/IgnoredError.php b/src/Analyser/IgnoredError.php
index f683a95ee1..ab3c1faf6c 100644
--- a/src/Analyser/IgnoredError.php
+++ b/src/Analyser/IgnoredError.php
@@ -2,14 +2,20 @@
namespace PHPStan\Analyser;
+use Nette\Utils\Strings;
use PHPStan\File\FileExcluder;
use PHPStan\File\FileHelper;
+use function count;
+use function implode;
+use function is_array;
+use function preg_quote;
+use function sprintf;
+use function str_replace;
class IgnoredError
{
/**
- * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param mixed[]|string $ignoredError
* @return string Representation of the ignored error
*/
@@ -34,18 +40,13 @@ public static function stringifyPattern($ignoredError): string
}
/**
- * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
- * @param FileHelper $fileHelper
- * @param Error $error
- * @param string $ignoredErrorPattern
- * @param string|null $path
* @return bool To ignore or not to ignore?
*/
public static function shouldIgnore(
FileHelper $fileHelper,
Error $error,
string $ignoredErrorPattern,
- ?string $path
+ ?string $path,
): bool
{
// normalize newlines to allow working with ignore-patterns independent of used OS newline-format
@@ -54,12 +55,11 @@ public static function shouldIgnore(
$ignoredErrorPattern = str_replace([preg_quote('\r\n'), preg_quote('\r')], preg_quote('\n'), $ignoredErrorPattern);
if ($path !== null) {
- $fileExcluder = new FileExcluder($fileHelper, [$path], []);
-
- if (\Nette\Utils\Strings::match($errorMessage, $ignoredErrorPattern) === null) {
+ if (Strings::match($errorMessage, $ignoredErrorPattern) === null) {
return false;
}
+ $fileExcluder = new FileExcluder($fileHelper, [$path], []);
$isExcluded = $fileExcluder->isExcludedFromAnalysing($error->getFilePath());
if (!$isExcluded && $error->getTraitFilePath() !== null) {
return $fileExcluder->isExcludedFromAnalysing($error->getTraitFilePath());
@@ -68,7 +68,7 @@ public static function shouldIgnore(
return $isExcluded;
}
- return \Nette\Utils\Strings::match($errorMessage, $ignoredErrorPattern) !== null;
+ return Strings::match($errorMessage, $ignoredErrorPattern) !== null;
}
}
diff --git a/src/Analyser/IgnoredErrorHelper.php b/src/Analyser/IgnoredErrorHelper.php
index cf57521f3b..b683886b97 100644
--- a/src/Analyser/IgnoredErrorHelper.php
+++ b/src/Analyser/IgnoredErrorHelper.php
@@ -3,45 +3,30 @@
namespace PHPStan\Analyser;
use Nette\Utils\Json;
-use PHPStan\Command\IgnoredRegexValidator;
+use Nette\Utils\JsonException;
use PHPStan\File\FileHelper;
+use function is_array;
+use function is_file;
+use function sprintf;
class IgnoredErrorHelper
{
- private IgnoredRegexValidator $ignoredRegexValidator;
-
- private \PHPStan\File\FileHelper $fileHelper;
-
- /** @var (string|mixed[])[] */
- private array $ignoreErrors;
-
- private bool $reportUnmatchedIgnoredErrors;
-
/**
- * @param IgnoredRegexValidator $ignoredRegexValidator
- * @param FileHelper $fileHelper
* @param (string|mixed[])[] $ignoreErrors
- * @param bool $reportUnmatchedIgnoredErrors
*/
public function __construct(
- IgnoredRegexValidator $ignoredRegexValidator,
- FileHelper $fileHelper,
- array $ignoreErrors,
- bool $reportUnmatchedIgnoredErrors
+ private FileHelper $fileHelper,
+ private array $ignoreErrors,
+ private bool $reportUnmatchedIgnoredErrors,
)
{
- $this->ignoredRegexValidator = $ignoredRegexValidator;
- $this->fileHelper = $fileHelper;
- $this->ignoreErrors = $ignoreErrors;
- $this->reportUnmatchedIgnoredErrors = $reportUnmatchedIgnoredErrors;
}
public function initialize(): IgnoredErrorHelperResult
{
$otherIgnoreErrors = [];
$ignoreErrorsByFile = [];
- $warnings = [];
$errors = [];
foreach ($this->ignoreErrors as $i => $ignoreError) {
try {
@@ -49,7 +34,7 @@ public function initialize(): IgnoredErrorHelperResult
if (!isset($ignoreError['message'])) {
$errors[] = sprintf(
'Ignored error %s is missing a message.',
- Json::encode($ignoreError)
+ Json::encode($ignoreError),
);
continue;
}
@@ -57,7 +42,7 @@ public function initialize(): IgnoredErrorHelperResult
if (!isset($ignoreError['paths'])) {
$errors[] = sprintf(
'Ignored error %s is missing a path.',
- Json::encode($ignoreError)
+ Json::encode($ignoreError),
);
}
@@ -80,78 +65,18 @@ public function initialize(): IgnoredErrorHelperResult
'ignoreError' => $ignoreError,
];
}
-
- $ignoreMessage = $ignoreError['message'];
- \Nette\Utils\Strings::match('', $ignoreMessage);
- if (isset($ignoreError['count'])) {
- continue; // ignoreError coming from baseline will be correct
- }
- $validationResult = $this->ignoredRegexValidator->validate($ignoreMessage);
- $ignoredTypes = $validationResult->getIgnoredTypes();
- if (count($ignoredTypes) > 0) {
- $warnings[] = $this->createIgnoredTypesWarning($ignoreMessage, $ignoredTypes);
- }
-
- if ($validationResult->hasAnchorsInTheMiddle()) {
- $warnings[] = $this->createAnchorInTheMiddleWarning($ignoreMessage);
- }
-
- if ($validationResult->areAllErrorsIgnored()) {
- $errors[] = sprintf("Ignored error %s has an unescaped '%s' which leads to ignoring all errors. Use '%s' instead.", $ignoreMessage, $validationResult->getWrongSequence(), $validationResult->getEscapedWrongSequence());
- }
} else {
$otherIgnoreErrors[] = [
'index' => $i,
'ignoreError' => $ignoreError,
];
- $ignoreMessage = $ignoreError;
- \Nette\Utils\Strings::match('', $ignoreMessage);
- $validationResult = $this->ignoredRegexValidator->validate($ignoreMessage);
- $ignoredTypes = $validationResult->getIgnoredTypes();
- if (count($ignoredTypes) > 0) {
- $warnings[] = $this->createIgnoredTypesWarning($ignoreMessage, $ignoredTypes);
- }
-
- if ($validationResult->hasAnchorsInTheMiddle()) {
- $warnings[] = $this->createAnchorInTheMiddleWarning($ignoreMessage);
- }
-
- if ($validationResult->areAllErrorsIgnored()) {
- $errors[] = sprintf("Ignored error %s has an unescaped '%s' which leads to ignoring all errors. Use '%s' instead.", $ignoreMessage, $validationResult->getWrongSequence(), $validationResult->getEscapedWrongSequence());
- }
}
- } catch (\Nette\Utils\RegexpException $e) {
- $errors[] = $e->getMessage();
- } catch (\Nette\Utils\JsonException $e) {
+ } catch (JsonException $e) {
$errors[] = $e->getMessage();
}
}
- return new IgnoredErrorHelperResult($this->fileHelper, $errors, $warnings, $otherIgnoreErrors, $ignoreErrorsByFile, $this->ignoreErrors, $this->reportUnmatchedIgnoredErrors);
- }
-
- /**
- * @param string $regex
- * @param array $ignoredTypes
- * @return string
- */
- private function createIgnoredTypesWarning(string $regex, array $ignoredTypes): string
- {
- return sprintf(
- "Ignored error %s has an unescaped '|' which leads to ignoring more errors than intended. Use '\\|' instead.\n%s",
- $regex,
- sprintf(
- "It ignores all errors containing the following types:\n%s",
- implode("\n", array_map(static function (string $typeDescription): string {
- return sprintf('* %s', $typeDescription);
- }, array_keys($ignoredTypes)))
- )
- );
- }
-
- private function createAnchorInTheMiddleWarning(string $regex): string
- {
- return sprintf("Ignored error %s has an unescaped anchor '$' in the middle. This leads to unintended behavior. Use '\\$' instead.", $regex);
+ return new IgnoredErrorHelperResult($this->fileHelper, $errors, $otherIgnoreErrors, $ignoreErrorsByFile, $this->ignoreErrors, $this->reportUnmatchedIgnoredErrors);
}
}
diff --git a/src/Analyser/IgnoredErrorHelperResult.php b/src/Analyser/IgnoredErrorHelperResult.php
index 4aa7b42bf1..e0dc5de4f6 100644
--- a/src/Analyser/IgnoredErrorHelperResult.php
+++ b/src/Analyser/IgnoredErrorHelperResult.php
@@ -3,55 +3,35 @@
namespace PHPStan\Analyser;
use PHPStan\File\FileHelper;
+use PHPStan\ShouldNotHappenException;
+use function array_fill_keys;
+use function array_filter;
+use function array_key_exists;
+use function array_merge;
+use function array_values;
+use function count;
+use function is_array;
+use function is_string;
+use function sprintf;
class IgnoredErrorHelperResult
{
- private FileHelper $fileHelper;
-
- /** @var string[] */
- private array $errors;
-
- /** @var string[] */
- private array $warnings;
-
- /** @var array> */
- private array $otherIgnoreErrors;
-
- /** @var array>> */
- private array $ignoreErrorsByFile;
-
- /** @var (string|mixed[])[] */
- private array $ignoreErrors;
-
- private bool $reportUnmatchedIgnoredErrors;
-
/**
- * @param FileHelper $fileHelper
* @param string[] $errors
- * @param string[] $warnings
* @param array> $otherIgnoreErrors
* @param array>> $ignoreErrorsByFile
* @param (string|mixed[])[] $ignoreErrors
- * @param bool $reportUnmatchedIgnoredErrors
*/
public function __construct(
- FileHelper $fileHelper,
- array $errors,
- array $warnings,
- array $otherIgnoreErrors,
- array $ignoreErrorsByFile,
- array $ignoreErrors,
- bool $reportUnmatchedIgnoredErrors
+ private FileHelper $fileHelper,
+ private array $errors,
+ private array $otherIgnoreErrors,
+ private array $ignoreErrorsByFile,
+ private array $ignoreErrors,
+ private bool $reportUnmatchedIgnoredErrors,
)
{
- $this->fileHelper = $fileHelper;
- $this->errors = $errors;
- $this->warnings = $warnings;
- $this->otherIgnoreErrors = $otherIgnoreErrors;
- $this->ignoreErrorsByFile = $ignoreErrorsByFile;
- $this->ignoreErrors = $ignoreErrors;
- $this->reportUnmatchedIgnoredErrors = $reportUnmatchedIgnoredErrors;
}
/**
@@ -62,14 +42,6 @@ public function getErrors(): array
return $this->errors;
}
- /**
- * @return string[]
- */
- public function getWarnings(): array
- {
- return $this->warnings;
- }
-
/**
* @param Error[] $errors
* @param string[] $analysedFiles
@@ -79,7 +51,7 @@ public function process(
array $errors,
bool $onlyFiles,
array $analysedFiles,
- bool $hasInternalErrors
+ bool $hasInternalErrors,
): array
{
$unmatchedIgnoredErrors = $this->ignoreErrors;
@@ -122,7 +94,7 @@ public function process(
if (isset($unmatchedIgnoredErrors[$i])) {
if (!is_array($unmatchedIgnoredErrors[$i])) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
unset($unmatchedIgnoredErrors[$i]['paths'][$j]);
if (isset($unmatchedIgnoredErrors[$i]['paths']) && count($unmatchedIgnoredErrors[$i]['paths']) === 0) {
@@ -132,7 +104,7 @@ public function process(
break;
}
} else {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
}
@@ -140,7 +112,7 @@ public function process(
if (!$error->canBeIgnored()) {
$addErrors[] = sprintf(
'Error message "%s" cannot be ignored, use excludePaths instead.',
- $error->getMessage()
+ $error->getMessage(),
);
return true;
}
@@ -206,7 +178,7 @@ public function process(
$unmatchedIgnoredError['count'],
$unmatchedIgnoredError['count'] === 1 ? 'time' : 'times',
$unmatchedIgnoredError['realCount'],
- $unmatchedIgnoredError['realCount'] === 1 ? 'time' : 'times'
+ $unmatchedIgnoredError['realCount'] === 1 ? 'time' : 'times',
), $unmatchedIgnoredError['file'], $unmatchedIgnoredError['line'], false);
}
@@ -228,7 +200,7 @@ public function process(
$unmatchedIgnoredError['count'],
$unmatchedIgnoredError['count'] === 1 ? 'time' : 'times',
$unmatchedIgnoredError['realCount'],
- $unmatchedIgnoredError['realCount'] === 1 ? 'time' : 'times'
+ $unmatchedIgnoredError['realCount'] === 1 ? 'time' : 'times',
), $unmatchedIgnoredError['file'], $unmatchedIgnoredError['line'], false);
}
} elseif (isset($unmatchedIgnoredError['realPath'])) {
@@ -239,16 +211,16 @@ public function process(
$errors[] = new Error(
sprintf(
'Ignored error pattern %s was not matched in reported errors.',
- IgnoredError::stringifyPattern($unmatchedIgnoredError)
+ IgnoredError::stringifyPattern($unmatchedIgnoredError),
),
$unmatchedIgnoredError['realPath'],
null,
- false
+ false,
);
} elseif (!$onlyFiles) {
$errors[] = sprintf(
'Ignored error pattern %s was not matched in reported errors.',
- IgnoredError::stringifyPattern($unmatchedIgnoredError)
+ IgnoredError::stringifyPattern($unmatchedIgnoredError),
);
}
}
diff --git a/src/Analyser/LazyScopeFactory.php b/src/Analyser/LazyScopeFactory.php
index d9ac4ca85b..e704d36419 100644
--- a/src/Analyser/LazyScopeFactory.php
+++ b/src/Analyser/LazyScopeFactory.php
@@ -6,62 +6,51 @@
use PHPStan\DependencyInjection\Container;
use PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider;
use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
+use PHPStan\Php\PhpVersion;
+use PHPStan\Reflection\FunctionReflection;
+use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Properties\PropertyReflectionFinder;
+use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Type;
+use function is_a;
class LazyScopeFactory implements ScopeFactory
{
- private string $scopeClass;
-
- private Container $container;
-
/** @var string[] */
private array $dynamicConstantNames;
private bool $treatPhpDocTypesAsCertain;
- private bool $objectFromNewClass;
+ private bool $explicitMixedInUnknownGenericNew;
public function __construct(
- string $scopeClass,
- Container $container
+ private string $scopeClass,
+ private Container $container,
)
{
- $this->scopeClass = $scopeClass;
- $this->container = $container;
$this->dynamicConstantNames = $container->getParameter('dynamicConstantNames');
$this->treatPhpDocTypesAsCertain = $container->getParameter('treatPhpDocTypesAsCertain');
- $this->objectFromNewClass = $container->getParameter('featureToggles')['objectFromNewClass'];
+ $this->explicitMixedInUnknownGenericNew = $this->container->getParameter('featureToggles')['explicitMixedInUnknownGenericNew'];
}
/**
- * @param \PHPStan\Analyser\ScopeContext $context
- * @param bool $declareStrictTypes
* @param array $constantTypes
- * @param \PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection|null $function
- * @param string|null $namespace
- * @param \PHPStan\Analyser\VariableTypeHolder[] $variablesTypes
- * @param \PHPStan\Analyser\VariableTypeHolder[] $moreSpecificTypes
+ * @param VariableTypeHolder[] $variablesTypes
+ * @param VariableTypeHolder[] $moreSpecificTypes
* @param array $conditionalExpressions
- * @param string|null $inClosureBindScopeClass
- * @param \PHPStan\Reflection\ParametersAcceptor|null $anonymousFunctionReflection
- * @param bool $inFirstLevelStatement
* @param array $currentlyAssignedExpressions
* @param array $nativeExpressionTypes
- * @param array<\PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection> $inFunctionCallsStack
- * @param bool $afterExtractCall
- * @param Scope|null $parentScope
+ * @param array<(FunctionReflection|MethodReflection)> $inFunctionCallsStack
*
- * @return MutatingScope
*/
public function create(
ScopeContext $context,
bool $declareStrictTypes = false,
array $constantTypes = [],
- $function = null,
+ FunctionReflection|MethodReflection|null $function = null,
?string $namespace = null,
array $variablesTypes = [],
array $moreSpecificTypes = [],
@@ -73,12 +62,12 @@ public function create(
array $nativeExpressionTypes = [],
array $inFunctionCallsStack = [],
bool $afterExtractCall = false,
- ?Scope $parentScope = null
+ ?Scope $parentScope = null,
): MutatingScope
{
$scopeClass = $this->scopeClass;
if (!is_a($scopeClass, MutatingScope::class, true)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return new $scopeClass(
@@ -89,9 +78,10 @@ public function create(
$this->container->getByType(Standard::class),
$this->container->getByType(TypeSpecifier::class),
$this->container->getByType(PropertyReflectionFinder::class),
- $this->container->getByType(\PHPStan\Parser\Parser::class),
+ $this->container->getService('currentPhpVersionSimpleParser'),
$this->container->getByType(NodeScopeResolver::class),
$context,
+ $this->container->getByType(PhpVersion::class),
$declareStrictTypes,
$constantTypes,
$function,
@@ -107,9 +97,9 @@ public function create(
$inFunctionCallsStack,
$this->dynamicConstantNames,
$this->treatPhpDocTypesAsCertain,
- $this->objectFromNewClass,
$afterExtractCall,
- $parentScope
+ $parentScope,
+ $this->explicitMixedInUnknownGenericNew,
);
}
diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php
index c0c03073e0..acb13d5b92 100644
--- a/src/Analyser/MutatingScope.php
+++ b/src/Analyser/MutatingScope.php
@@ -2,6 +2,9 @@
namespace PHPStan\Analyser;
+use ArrayAccess;
+use Closure;
+use Generator;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Arg;
@@ -25,8 +28,17 @@
use PhpParser\Node\Scalar\EncapsedStringPart;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
+use PhpParser\NodeFinder;
+use PhpParser\PrettyPrinter\Standard;
use PHPStan\Node\ExecutionEndNode;
+use PHPStan\Node\Expr\GetIterableValueTypeExpr;
+use PHPStan\Node\Expr\GetOffsetValueTypeExpr;
+use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
+use PHPStan\Node\Expr\SetOffsetValueTypeExpr;
use PHPStan\Parser\Parser;
+use PHPStan\Parser\ParserErrorsException;
+use PHPStan\Php\PhpVersion;
+use PHPStan\Reflection\ClassConstantReflection;
use PHPStan\Reflection\ClassMemberReflection;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ConstantReflection;
@@ -34,6 +46,7 @@
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\Native\NativeParameterReflection;
+use PHPStan\Reflection\ParameterReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\PassedByReference;
@@ -44,7 +57,11 @@
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Reflection\TrivialParametersAcceptor;
use PHPStan\Rules\Properties\PropertyReflectionFinder;
+use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
+use PHPStan\Type\Accessory\AccessoryLiteralStringType;
+use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
+use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BenevolentUnionType;
use PHPStan\Type\BooleanType;
@@ -60,8 +77,10 @@
use PHPStan\Type\ConstantType;
use PHPStan\Type\ConstantTypeHelper;
use PHPStan\Type\DynamicReturnTypeExtensionRegistry;
+use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\FloatType;
+use PHPStan\Type\GeneralizePrecision;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Generic\TemplateType;
@@ -92,11 +111,37 @@
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use PHPStan\Type\VoidType;
+use Throwable;
+use function abs;
+use function array_column;
+use function array_filter;
use function array_key_exists;
+use function array_keys;
+use function array_map;
+use function array_pop;
+use function count;
+use function dirname;
+use function get_class;
+use function in_array;
+use function is_float;
+use function is_string;
+use function ltrim;
+use function max;
+use function sprintf;
+use function str_starts_with;
+use function strlen;
+use function strtolower;
+use function substr;
+use function usort;
+use const PHP_INT_MAX;
+use const PHP_INT_MIN;
+use const PHP_INT_SIZE;
class MutatingScope implements Scope
{
+ public const CALCULATE_SCALARS_LIMIT = 128;
+
private const OPERATOR_SIGIL_MAP = [
Node\Expr\AssignOp\Plus::class => '+',
Node\Expr\AssignOp\Minus::class => '-',
@@ -105,167 +150,64 @@ class MutatingScope implements Scope
Node\Expr\AssignOp\Div::class => '/',
];
- private \PHPStan\Analyser\ScopeFactory $scopeFactory;
-
- private \PHPStan\Reflection\ReflectionProvider $reflectionProvider;
-
- private \PHPStan\Type\DynamicReturnTypeExtensionRegistry $dynamicReturnTypeExtensionRegistry;
-
- private OperatorTypeSpecifyingExtensionRegistry $operatorTypeSpecifyingExtensionRegistry;
-
- private \PhpParser\PrettyPrinter\Standard $printer;
-
- private \PHPStan\Analyser\TypeSpecifier $typeSpecifier;
-
- private \PHPStan\Rules\Properties\PropertyReflectionFinder $propertyReflectionFinder;
-
- private Parser $parser;
-
- private NodeScopeResolver $nodeScopeResolver;
-
- private \PHPStan\Analyser\ScopeContext $context;
-
- /** @var \PHPStan\Type\Type[] */
+ /** @var Type[] */
private array $resolvedTypes = [];
- private bool $declareStrictTypes;
-
- /** @var array */
- private array $constantTypes;
+ /** @var array */
+ private array $truthyScopes = [];
- /** @var \PHPStan\Reflection\FunctionReflection|MethodReflection|null */
- private $function;
+ /** @var array */
+ private array $falseyScopes = [];
private ?string $namespace;
- /** @var \PHPStan\Analyser\VariableTypeHolder[] */
- private array $variableTypes;
-
- /** @var \PHPStan\Analyser\VariableTypeHolder[] */
- private array $moreSpecificTypes;
-
- /** @var array */
- private array $conditionalExpressions;
-
- private ?string $inClosureBindScopeClass;
-
- private ?ParametersAcceptor $anonymousFunctionReflection;
-
- private bool $inFirstLevelStatement;
-
- /** @var array */
- private array $currentlyAssignedExpressions;
-
- /** @var array */
- private array $inFunctionCallsStack;
-
- /** @var array */
- private array $nativeExpressionTypes;
-
- /** @var string[] */
- private array $dynamicConstantNames;
-
- private bool $treatPhpDocTypesAsCertain;
-
- private bool $objectFromNewClass;
-
- private bool $afterExtractCall;
-
- private ?Scope $parentScope;
-
/**
- * @param \PHPStan\Analyser\ScopeFactory $scopeFactory
- * @param ReflectionProvider $reflectionProvider
- * @param \PHPStan\Type\DynamicReturnTypeExtensionRegistry $dynamicReturnTypeExtensionRegistry
- * @param \PHPStan\Type\OperatorTypeSpecifyingExtensionRegistry $operatorTypeSpecifyingExtensionRegistry
- * @param \PhpParser\PrettyPrinter\Standard $printer
- * @param \PHPStan\Analyser\TypeSpecifier $typeSpecifier
- * @param \PHPStan\Rules\Properties\PropertyReflectionFinder $propertyReflectionFinder
- * @param Parser $parser
- * @param NodeScopeResolver $nodeScopeResolver
- * @param \PHPStan\Analyser\ScopeContext $context
- * @param bool $declareStrictTypes
* @param array $constantTypes
- * @param \PHPStan\Reflection\FunctionReflection|MethodReflection|null $function
- * @param string|null $namespace
- * @param \PHPStan\Analyser\VariableTypeHolder[] $variablesTypes
- * @param \PHPStan\Analyser\VariableTypeHolder[] $moreSpecificTypes
+ * @param VariableTypeHolder[] $variableTypes
+ * @param VariableTypeHolder[] $moreSpecificTypes
* @param array $conditionalExpressions
- * @param string|null $inClosureBindScopeClass
- * @param \PHPStan\Reflection\ParametersAcceptor|null $anonymousFunctionReflection
- * @param bool $inFirstLevelStatement
* @param array $currentlyAssignedExpressions
* @param array $nativeExpressionTypes
* @param array $inFunctionCallsStack
* @param string[] $dynamicConstantNames
- * @param bool $treatPhpDocTypesAsCertain
- * @param bool $objectFromNewClass
- * @param bool $afterExtractCall
- * @param Scope|null $parentScope
*/
public function __construct(
- ScopeFactory $scopeFactory,
- ReflectionProvider $reflectionProvider,
- DynamicReturnTypeExtensionRegistry $dynamicReturnTypeExtensionRegistry,
- OperatorTypeSpecifyingExtensionRegistry $operatorTypeSpecifyingExtensionRegistry,
- \PhpParser\PrettyPrinter\Standard $printer,
- TypeSpecifier $typeSpecifier,
- PropertyReflectionFinder $propertyReflectionFinder,
- Parser $parser,
- NodeScopeResolver $nodeScopeResolver,
- ScopeContext $context,
- bool $declareStrictTypes = false,
- array $constantTypes = [],
- $function = null,
+ private ScopeFactory $scopeFactory,
+ private ReflectionProvider $reflectionProvider,
+ private DynamicReturnTypeExtensionRegistry $dynamicReturnTypeExtensionRegistry,
+ private OperatorTypeSpecifyingExtensionRegistry $operatorTypeSpecifyingExtensionRegistry,
+ private Standard $printer,
+ private TypeSpecifier $typeSpecifier,
+ private PropertyReflectionFinder $propertyReflectionFinder,
+ private Parser $parser,
+ private NodeScopeResolver $nodeScopeResolver,
+ private ScopeContext $context,
+ private PhpVersion $phpVersion,
+ private bool $declareStrictTypes = false,
+ private array $constantTypes = [],
+ private FunctionReflection|MethodReflection|null $function = null,
?string $namespace = null,
- array $variablesTypes = [],
- array $moreSpecificTypes = [],
- array $conditionalExpressions = [],
- ?string $inClosureBindScopeClass = null,
- ?ParametersAcceptor $anonymousFunctionReflection = null,
- bool $inFirstLevelStatement = true,
- array $currentlyAssignedExpressions = [],
- array $nativeExpressionTypes = [],
- array $inFunctionCallsStack = [],
- array $dynamicConstantNames = [],
- bool $treatPhpDocTypesAsCertain = true,
- bool $objectFromNewClass = false,
- bool $afterExtractCall = false,
- ?Scope $parentScope = null
+ private array $variableTypes = [],
+ private array $moreSpecificTypes = [],
+ private array $conditionalExpressions = [],
+ private ?string $inClosureBindScopeClass = null,
+ private ?ParametersAcceptor $anonymousFunctionReflection = null,
+ private bool $inFirstLevelStatement = true,
+ private array $currentlyAssignedExpressions = [],
+ private array $nativeExpressionTypes = [],
+ private array $inFunctionCallsStack = [],
+ private array $dynamicConstantNames = [],
+ private bool $treatPhpDocTypesAsCertain = true,
+ private bool $afterExtractCall = false,
+ private ?Scope $parentScope = null,
+ private bool $explicitMixedInUnknownGenericNew = false,
)
{
if ($namespace === '') {
$namespace = null;
}
- $this->scopeFactory = $scopeFactory;
- $this->reflectionProvider = $reflectionProvider;
- $this->dynamicReturnTypeExtensionRegistry = $dynamicReturnTypeExtensionRegistry;
- $this->operatorTypeSpecifyingExtensionRegistry = $operatorTypeSpecifyingExtensionRegistry;
- $this->printer = $printer;
- $this->typeSpecifier = $typeSpecifier;
- $this->propertyReflectionFinder = $propertyReflectionFinder;
- $this->parser = $parser;
- $this->nodeScopeResolver = $nodeScopeResolver;
- $this->context = $context;
- $this->declareStrictTypes = $declareStrictTypes;
- $this->constantTypes = $constantTypes;
- $this->function = $function;
$this->namespace = $namespace;
- $this->variableTypes = $variablesTypes;
- $this->moreSpecificTypes = $moreSpecificTypes;
- $this->conditionalExpressions = $conditionalExpressions;
- $this->inClosureBindScopeClass = $inClosureBindScopeClass;
- $this->anonymousFunctionReflection = $anonymousFunctionReflection;
- $this->inFirstLevelStatement = $inFirstLevelStatement;
- $this->currentlyAssignedExpressions = $currentlyAssignedExpressions;
- $this->nativeExpressionTypes = $nativeExpressionTypes;
- $this->inFunctionCallsStack = $inFunctionCallsStack;
- $this->dynamicConstantNames = $dynamicConstantNames;
- $this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain;
- $this->objectFromNewClass = $objectFromNewClass;
- $this->afterExtractCall = $afterExtractCall;
- $this->parentScope = $parentScope;
}
/** @api */
@@ -284,20 +226,20 @@ public function getFileDescription(): string
/** @var ClassReflection $classReflection */
$classReflection = $this->context->getClassReflection();
- $className = sprintf('class %s', $classReflection->getDisplayName());
- if ($classReflection->isAnonymous()) {
- $className = 'anonymous class';
+ $className = $classReflection->getDisplayName();
+ if (!$classReflection->isAnonymous()) {
+ $className = sprintf('class %s', $className);
}
$traitReflection = $this->context->getTraitReflection();
- if ($traitReflection->getFileName() === false) {
- throw new \PHPStan\ShouldNotHappenException();
+ if ($traitReflection->getFileName() === null) {
+ throw new ShouldNotHappenException();
}
return sprintf(
'%s (in context of %s)',
$traitReflection->getFileName(),
- $className
+ $className,
);
}
@@ -315,7 +257,7 @@ public function enterDeclareStrictTypes(): self
[],
null,
null,
- $this->variableTypes
+ $this->variableTypes,
);
}
@@ -345,7 +287,7 @@ public function getTraitReflection(): ?ClassReflection
/**
* @api
- * @return \PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection|null
+ * @return FunctionReflection|MethodReflection|null
*/
public function getFunction()
{
@@ -371,7 +313,7 @@ public function getParentScope(): ?Scope
}
/**
- * @return array
+ * @return array
*/
private function getVariableTypes(): array
{
@@ -402,7 +344,7 @@ public function afterExtractCall(): self
$this->nativeExpressionTypes,
$this->inFunctionCallsStack,
true,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -433,7 +375,7 @@ public function afterClearstatcacheCall(): self
'filetype',
'fileperms',
] as $functionName) {
- if (!Strings::startsWith((string) $exprString, $functionName . '(') && !Strings::startsWith((string) $exprString, '\\' . $functionName . '(')) {
+ if (!str_starts_with((string) $exprString, $functionName . '(') && !str_starts_with((string) $exprString, '\\' . $functionName . '(')) {
continue;
}
@@ -457,7 +399,7 @@ public function afterClearstatcacheCall(): self
$this->nativeExpressionTypes,
$this->inFunctionCallsStack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -487,7 +429,7 @@ public function getVariableType(string $variableName): Type
}
if ($this->hasVariableType($variableName)->no()) {
- throw new \PHPStan\Analyser\UndefinedVariableException($this, $variableName);
+ throw new UndefinedVariableException($this, $variableName);
}
if (!array_key_exists($variableName, $this->variableTypes)) {
@@ -568,7 +510,7 @@ private function fileHasCompilerHaltStatementCalls(): bool
{
$nodes = $this->parser->parseFile($this->getFile());
foreach ($nodes as $node) {
- if ($node instanceof \PhpParser\Node\Stmt\HaltCompiler) {
+ if ($node instanceof Node\Stmt\HaltCompiler) {
return true;
}
}
@@ -589,7 +531,7 @@ public function getAnonymousFunctionReflection(): ?ParametersAcceptor
}
/** @api */
- public function getAnonymousFunctionReturnType(): ?\PHPStan\Type\Type
+ public function getAnonymousFunctionReturnType(): ?Type
{
if ($this->anonymousFunctionReflection === null) {
return null;
@@ -601,6 +543,27 @@ public function getAnonymousFunctionReturnType(): ?\PHPStan\Type\Type
/** @api */
public function getType(Expr $node): Type
{
+ 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 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)) {
@@ -646,54 +609,123 @@ private function resolveType(Expr $node): Type
if (
$node instanceof Expr\BinaryOp\Equal
|| $node instanceof Expr\BinaryOp\NotEqual
- || $node instanceof Expr\Empty_
) {
+ 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);
+
+ $stringType = new StringType();
+ $integerType = new IntegerType();
+ $floatType = new FloatType();
+ if (
+ ($stringType->isSuperTypeOf($leftType)->yes() && $stringType->isSuperTypeOf($rightType)->yes())
+ || ($integerType->isSuperTypeOf($leftType)->yes() && $integerType->isSuperTypeOf($rightType)->yes())
+ || ($floatType->isSuperTypeOf($leftType)->yes() && $floatType->isSuperTypeOf($rightType)->yes())
+ ) {
+ return $this->getType(new Expr\BinaryOp\Identical($node->left, $node->right));
+ }
+ }
+
+ if ($node instanceof Expr\BinaryOp\NotEqual) {
+ 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(false);
+ }
+
+ $leftType = $this->getType($node->left);
+ $rightType = $this->getType($node->right);
+
+ $stringType = new StringType();
+ $integerType = new IntegerType();
+ $floatType = new FloatType();
+ if (
+ ($stringType->isSuperTypeOf($leftType)->yes() && $stringType->isSuperTypeOf($rightType)->yes())
+ || ($integerType->isSuperTypeOf($leftType)->yes() && $integerType->isSuperTypeOf($rightType)->yes())
+ || ($floatType->isSuperTypeOf($leftType)->yes() && $floatType->isSuperTypeOf($rightType)->yes())
+ ) {
+ return $this->getType(new Expr\BinaryOp\NotIdentical($node->left, $node->right));
+ }
+ }
+
return new BooleanType();
}
- if ($node instanceof Expr\Isset_) {
- $result = new ConstantBooleanType(true);
- foreach ($node->vars as $var) {
- if ($var instanceof Expr\ArrayDimFetch && $var->dim !== null) {
- $variableType = $this->getType($var->var);
- $dimType = $this->getType($var->dim);
- $hasOffset = $variableType->hasOffsetValueType($dimType);
- $offsetValueType = $variableType->getOffsetValueType($dimType);
- $offsetValueIsNotNull = (new NullType())->isSuperTypeOf($offsetValueType)->negate();
- $isset = $hasOffset->and($offsetValueIsNotNull)->toBooleanType();
- if ($isset instanceof ConstantBooleanType) {
- if (!$isset->getValue()) {
- return $isset;
- }
+ 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;
+ }
- continue;
+ if ($isNull->yes()) {
+ if ($isFalsey->yes()) {
+ return false;
+ }
+ if ($isFalsey->no()) {
+ return true;
}
- $result = $isset;
- continue;
+ return false;
}
- if ($var instanceof Expr\Variable && is_string($var->name)) {
- $variableType = $this->getType($var);
- $isNullSuperType = (new NullType())->isSuperTypeOf($variableType);
- $has = $this->hasVariableType($var->name);
- if ($has->no() || $isNullSuperType->yes()) {
- return new ConstantBooleanType(false);
+ return !$isFalsey->yes();
+ });
+ if ($result === null) {
+ return new BooleanType();
+ }
+
+ return new ConstantBooleanType(!$result);
+ }
+
+ 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;
}
- if ($has->maybe() || !$isNullSuperType->no()) {
- $result = new BooleanType();
+ return !$isNull->yes();
+ });
+ if ($result !== null) {
+ if (!$result) {
+ return new ConstantBooleanType($result);
}
+
continue;
}
+ $issetResult = $result;
+ }
+
+ if ($issetResult === null) {
return new BooleanType();
}
- return $result;
+ return new ConstantBooleanType($issetResult);
}
- if ($node instanceof \PhpParser\Node\Expr\BooleanNot) {
+ if ($node instanceof Node\Expr\BooleanNot) {
if ($this->treatPhpDocTypesAsCertain) {
$exprBooleanType = $this->getType($node->expr)->toBoolean();
} else {
@@ -706,7 +738,7 @@ private function resolveType(Expr $node): Type
return new BooleanType();
}
- if ($node instanceof \PhpParser\Node\Expr\BitwiseNot) {
+ if ($node instanceof Node\Expr\BitwiseNot) {
$exprType = $this->getType($node->expr);
return TypeTraverser::map($exprType, static function (Type $type, callable $traverse): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
@@ -726,8 +758,8 @@ private function resolveType(Expr $node): Type
}
if (
- $node instanceof \PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $node instanceof \PhpParser\Node\Expr\BinaryOp\LogicalAnd
+ $node instanceof Node\Expr\BinaryOp\BooleanAnd
+ || $node instanceof Node\Expr\BinaryOp\LogicalAnd
) {
if ($this->treatPhpDocTypesAsCertain) {
$leftBooleanType = $this->getType($node->left)->toBoolean();
@@ -768,8 +800,8 @@ private function resolveType(Expr $node): Type
}
if (
- $node instanceof \PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $node instanceof \PhpParser\Node\Expr\BinaryOp\LogicalOr
+ $node instanceof Node\Expr\BinaryOp\BooleanOr
+ || $node instanceof Node\Expr\BinaryOp\LogicalOr
) {
if ($this->treatPhpDocTypesAsCertain) {
$leftBooleanType = $this->getType($node->left)->toBoolean();
@@ -808,7 +840,7 @@ private function resolveType(Expr $node): Type
return new BooleanType();
}
- if ($node instanceof \PhpParser\Node\Expr\BinaryOp\LogicalXor) {
+ if ($node instanceof Node\Expr\BinaryOp\LogicalXor) {
if ($this->treatPhpDocTypesAsCertain) {
$leftBooleanType = $this->getType($node->left)->toBoolean();
$rightBooleanType = $this->getType($node->right)->toBoolean();
@@ -822,7 +854,7 @@ private function resolveType(Expr $node): Type
&& $rightBooleanType instanceof ConstantBooleanType
) {
return new ConstantBooleanType(
- $leftBooleanType->getValue() xor $rightBooleanType->getValue()
+ $leftBooleanType->getValue() xor $rightBooleanType->getValue(),
);
}
@@ -830,6 +862,16 @@ private function resolveType(Expr $node): Type
}
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);
+ }
+
if ($this->treatPhpDocTypesAsCertain) {
$leftType = $this->getType($node->left);
$rightType = $this->getType($node->right);
@@ -876,6 +918,15 @@ private function resolveType(Expr $node): Type
}
if ($node instanceof Expr\BinaryOp\NotIdentical) {
+ 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(false);
+ }
if ($this->treatPhpDocTypesAsCertain) {
$leftType = $this->getType($node->left);
$rightType = $this->getType($node->right);
@@ -999,6 +1050,7 @@ private function resolveType(Expr $node): Type
if ($node instanceof Node\Expr\UnaryMinus) {
$type = $this->getType($node->expr)->toNumber();
$scalarValues = TypeUtils::getConstantScalars($type);
+
if (count($scalarValues) > 0) {
$newTypes = [];
foreach ($scalarValues as $scalarValue) {
@@ -1012,40 +1064,43 @@ private function resolveType(Expr $node): Type
return TypeCombinator::union(...$newTypes);
}
+ if ($type instanceof IntegerRangeType) {
+ return $this->resolveType(new Node\Expr\BinaryOp\Mul($node->expr, new LNumber(-1)));
+ }
+
return $type;
}
if ($node instanceof Expr\BinaryOp\Concat || $node instanceof Expr\AssignOp\Concat) {
+ return $this->resolveConcatType($node);
+ }
+
+ if (
+ $node instanceof Node\Expr\BinaryOp\Mul
+ || $node instanceof Node\Expr\AssignOp\Mul
+ ) {
if ($node instanceof Node\Expr\AssignOp) {
- $left = $node->var;
- $right = $node->expr;
+ $leftType = $this->getType($node->var)->toNumber();
+ $rightType = $this->getType($node->expr)->toNumber();
} else {
- $left = $node->left;
- $right = $node->right;
+ $leftType = $this->getType($node->left)->toNumber();
+ $rightType = $this->getType($node->right)->toNumber();
}
- $leftStringType = $this->getType($left)->toString();
- $rightStringType = $this->getType($right)->toString();
- if (TypeCombinator::union(
- $leftStringType,
- $rightStringType
- ) instanceof ErrorType) {
- return new ErrorType();
- }
+ $floatType = new FloatType();
- if ($leftStringType instanceof ConstantStringType && $leftStringType->getValue() === '') {
- return $rightStringType;
- }
-
- if ($rightStringType instanceof ConstantStringType && $rightStringType->getValue() === '') {
- return $leftStringType;
+ if ($leftType instanceof ConstantIntegerType && $leftType->getValue() === 0) {
+ if ($floatType->isSuperTypeOf($rightType)->yes()) {
+ return new ConstantFloatType(0.0);
+ }
+ return new ConstantIntegerType(0);
}
-
- if ($leftStringType instanceof ConstantStringType && $rightStringType instanceof ConstantStringType) {
- return $leftStringType->append($rightStringType);
+ if ($rightType instanceof ConstantIntegerType && $rightType->getValue() === 0) {
+ if ($floatType->isSuperTypeOf($leftType)->yes()) {
+ return new ConstantFloatType(0.0);
+ }
+ return new ConstantIntegerType(0);
}
-
- return new StringType();
}
if (
@@ -1059,12 +1114,24 @@ private function resolveType(Expr $node): Type
} else {
$right = $node->right;
}
+ $rightType = $this->getType($right);
+
+ $integerType = $rightType->toInteger();
+ if (
+ $node instanceof Node\Expr\BinaryOp\Mod
+ || $node instanceof Node\Expr\AssignOp\Mod
+ ) {
+ if ($integerType instanceof ConstantIntegerType && $integerType->getValue() === 1) {
+ return new ConstantIntegerType(0);
+ }
+ }
+
+ $rightScalarTypes = TypeUtils::getConstantScalars($rightType->toNumber());
+ foreach ($rightScalarTypes as $scalarType) {
- $rightTypes = TypeUtils::getConstantScalars($this->getType($right)->toNumber());
- foreach ($rightTypes as $rightType) {
if (
- $rightType->getValue() === 0
- || $rightType->getValue() === 0.0
+ $scalarType->getValue() === 0
+ || $scalarType->getValue() === 0.0
) {
return new ErrorType();
}
@@ -1088,11 +1155,18 @@ private function resolveType(Expr $node): Type
$leftTypes = TypeUtils::getConstantScalars($this->getType($left));
$rightTypes = TypeUtils::getConstantScalars($this->getType($right));
- if (count($leftTypes) > 0 && count($rightTypes) > 0) {
+ $leftTypesCount = count($leftTypes);
+ $rightTypesCount = count($rightTypes);
+ if ($leftTypesCount > 0 && $rightTypesCount > 0) {
$resultTypes = [];
+ $generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT;
foreach ($leftTypes as $leftType) {
foreach ($rightTypes as $rightType) {
- $resultTypes[] = $this->calculateFromScalars($node, $leftType, $rightType);
+ $resultType = $this->calculateFromScalars($node, $leftType, $rightType);
+ if ($generalize) {
+ $resultType = $resultType->generalize(GeneralizePrecision::lessSpecific());
+ }
+ $resultTypes[] = $resultType;
}
}
return TypeCombinator::union(...$resultTypes);
@@ -1100,6 +1174,52 @@ private function resolveType(Expr $node): Type
}
if ($node instanceof Node\Expr\BinaryOp\Mod || $node instanceof Expr\AssignOp\Mod) {
+ if ($node instanceof Node\Expr\AssignOp) {
+ $left = $node->var;
+ $right = $node->expr;
+ } else {
+ $left = $node->left;
+ $right = $node->right;
+ }
+
+ $leftType = $this->getType($left);
+ $rightType = $this->getType($right);
+
+ $integer = new IntegerType();
+ $positiveInt = IntegerRangeType::fromInterval(0, null);
+ if ($integer->isSuperTypeOf($rightType)->yes()) {
+ $rangeMin = null;
+ $rangeMax = null;
+
+ if ($rightType instanceof IntegerRangeType) {
+ $rangeMax = $rightType->getMax() !== null ? $rightType->getMax() - 1 : null;
+ } elseif ($rightType instanceof ConstantIntegerType) {
+ $rangeMax = $rightType->getValue() - 1;
+ } elseif ($rightType instanceof UnionType) {
+ foreach ($rightType->getTypes() as $type) {
+ if ($type instanceof IntegerRangeType) {
+ if ($type->getMax() === null) {
+ $rangeMax = null;
+ } else {
+ $rangeMax = max($rangeMax, $type->getMax());
+ }
+ } elseif ($type instanceof ConstantIntegerType) {
+ $rangeMax = max($rangeMax, $type->getValue() - 1);
+ }
+ }
+ }
+
+ if ($positiveInt->isSuperTypeOf($leftType)->yes()) {
+ $rangeMin = 0;
+ } elseif ($rangeMax !== null) {
+ $rangeMin = $rangeMax * -1;
+ }
+
+ return IntegerRangeType::fromInterval($rangeMin, $rangeMax);
+ } elseif ($positiveInt->isSuperTypeOf($leftType)->yes()) {
+ return IntegerRangeType::fromInterval(0, null);
+ }
+
return new IntegerType();
}
@@ -1127,7 +1247,7 @@ private function resolveType(Expr $node): Type
if (TypeCombinator::union(
$this->getType($left)->toNumber(),
- $this->getType($right)->toNumber()
+ $this->getType($right)->toNumber(),
) instanceof ErrorType) {
return new ErrorType();
}
@@ -1189,31 +1309,6 @@ private function resolveType(Expr $node): Type
$leftType = $this->getType($left);
$rightType = $this->getType($right);
- $operatorSigil = null;
-
- if ($node instanceof BinaryOp) {
- $operatorSigil = $node->getOperatorSigil();
- }
-
- if ($operatorSigil === null) {
- $operatorSigil = self::OPERATOR_SIGIL_MAP[get_class($node)] ?? null;
- }
-
- if ($operatorSigil !== null) {
- $operatorTypeSpecifyingExtensions = $this->operatorTypeSpecifyingExtensionRegistry->getOperatorTypeSpecifyingExtensions($operatorSigil, $leftType, $rightType);
-
- /** @var Type[] $extensionTypes */
- $extensionTypes = [];
-
- foreach ($operatorTypeSpecifyingExtensions as $extension) {
- $extensionTypes[] = $extension->specifyType($operatorSigil, $leftType, $rightType);
- }
-
- if (count($extensionTypes) > 0) {
- return TypeCombinator::union(...$extensionTypes);
- }
- }
-
if ($node instanceof Expr\AssignOp\Plus || $node instanceof Expr\BinaryOp\Plus) {
$leftConstantArrays = TypeUtils::getConstantArrays($leftType);
$rightConstantArrays = TypeUtils::getConstantArrays($rightType);
@@ -1226,7 +1321,7 @@ private function resolveType(Expr $node): Type
foreach ($leftConstantArray->getKeyTypes() as $leftKeyType) {
$newArrayBuilder->setOffsetValueType(
$leftKeyType,
- $leftConstantArray->getOffsetValueType($leftKeyType)
+ $leftConstantArray->getOffsetValueType($leftKeyType),
);
}
$resultTypes[] = $newArrayBuilder->getArray();
@@ -1235,6 +1330,7 @@ private function resolveType(Expr $node): Type
return TypeCombinator::union(...$resultTypes);
}
+
$arrayType = new ArrayType(new MixedType(), new MixedType());
if ($arrayType->isSuperTypeOf($leftType)->yes() && $arrayType->isSuperTypeOf($rightType)->yes()) {
@@ -1251,10 +1347,16 @@ private function resolveType(Expr $node): Type
}
$keyType = TypeCombinator::union(...$keyTypes);
}
- return new ArrayType(
+
+ $arrayType = new ArrayType(
$keyType,
- TypeCombinator::union($leftType->getIterableValueType(), $rightType->getIterableValueType())
+ TypeCombinator::union($leftType->getIterableValueType(), $rightType->getIterableValueType()),
);
+
+ if ($leftType->isIterableAtLeastOnce()->yes() || $rightType->isIterableAtLeastOnce()->yes()) {
+ return TypeCombinator::intersect($arrayType, new NonEmptyArrayType());
+ }
+ return $arrayType;
}
if ($leftType instanceof MixedType && $rightType instanceof MixedType) {
@@ -1266,6 +1368,64 @@ private function resolveType(Expr $node): Type
}
}
+ if (($leftType instanceof IntegerRangeType || $leftType instanceof ConstantIntegerType || $leftType instanceof UnionType) &&
+ ($rightType instanceof IntegerRangeType || $rightType instanceof ConstantIntegerType || $rightType instanceof UnionType) &&
+ !($node instanceof Node\Expr\BinaryOp\Pow || $node instanceof Node\Expr\AssignOp\Pow)) {
+
+ if ($leftType instanceof ConstantIntegerType) {
+ return $this->integerRangeMath(
+ $leftType,
+ $node,
+ $rightType,
+ );
+ } elseif ($leftType instanceof UnionType) {
+
+ $unionParts = [];
+
+ foreach ($leftType->getTypes() as $type) {
+ if ($type instanceof IntegerRangeType || $type instanceof ConstantIntegerType) {
+ $unionParts[] = $this->integerRangeMath($type, $node, $rightType);
+ } else {
+ $unionParts[] = $type;
+ }
+ }
+
+ $union = TypeCombinator::union(...$unionParts);
+ if ($leftType instanceof BenevolentUnionType) {
+ return TypeUtils::toBenevolentUnion($union)->toNumber();
+ }
+
+ return $union->toNumber();
+ }
+
+ return $this->integerRangeMath($leftType, $node, $rightType);
+ }
+
+ $operatorSigil = null;
+
+ if ($node instanceof BinaryOp) {
+ $operatorSigil = $node->getOperatorSigil();
+ }
+
+ if ($operatorSigil === null) {
+ $operatorSigil = self::OPERATOR_SIGIL_MAP[get_class($node)] ?? null;
+ }
+
+ if ($operatorSigil !== null) {
+ $operatorTypeSpecifyingExtensions = $this->operatorTypeSpecifyingExtensionRegistry->getOperatorTypeSpecifyingExtensions($operatorSigil, $leftType, $rightType);
+
+ /** @var Type[] $extensionTypes */
+ $extensionTypes = [];
+
+ foreach ($operatorTypeSpecifyingExtensions as $extension) {
+ $extensionTypes[] = $extension->specifyType($operatorSigil, $leftType, $rightType);
+ }
+
+ if (count($extensionTypes) > 0) {
+ return TypeCombinator::union(...$extensionTypes);
+ }
+ }
+
$types = TypeCombinator::union($leftType, $rightType);
if (
$leftType instanceof ArrayType
@@ -1277,6 +1437,9 @@ private function resolveType(Expr $node): Type
$leftNumberType = $leftType->toNumber();
$rightNumberType = $rightType->toNumber();
+ if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) {
+ return new ErrorType();
+ }
if (
(new FloatType())->isSuperTypeOf($leftNumberType)->yes()
@@ -1316,25 +1479,117 @@ private function resolveType(Expr $node): Type
} elseif ($node instanceof String_) {
return new ConstantStringType($node->value);
} elseif ($node instanceof Node\Scalar\Encapsed) {
- $constantString = new ConstantStringType('');
+ $parts = [];
foreach ($node->parts as $part) {
if ($part instanceof EncapsedStringPart) {
- $partStringType = new ConstantStringType($part->value);
- } else {
- $partStringType = $this->getType($part)->toString();
- if ($partStringType instanceof ErrorType) {
- return new ErrorType();
+ $parts[] = new ConstantStringType($part->value);
+ continue;
+ }
+
+ $partStringType = $this->getType($part)->toString();
+ if ($partStringType instanceof ErrorType) {
+ return new ErrorType();
+ }
+
+ $parts[] = $partStringType;
+ }
+
+ $constantString = new ConstantStringType('');
+ foreach ($parts as $part) {
+ if ($part instanceof ConstantStringType) {
+ $constantString = $constantString->append($part);
+ continue;
+ }
+
+ $isNonEmpty = false;
+ $isLiteralString = true;
+ foreach ($parts as $partType) {
+ if ($partType->isNonEmptyString()->yes()) {
+ $isNonEmpty = true;
}
- if (!$partStringType instanceof ConstantStringType) {
- return new StringType();
+ if ($partType->isLiteralString()->yes()) {
+ continue;
}
+ $isLiteralString = false;
+ }
+
+ $accessoryTypes = [];
+ if ($isNonEmpty === true) {
+ $accessoryTypes[] = new AccessoryNonEmptyStringType();
+ }
+ if ($isLiteralString === true) {
+ $accessoryTypes[] = new AccessoryLiteralStringType();
+ }
+ if (count($accessoryTypes) > 0) {
+ $accessoryTypes[] = new StringType();
+ return new IntersectionType($accessoryTypes);
}
- $constantString = $constantString->append($partStringType);
+ return new StringType();
}
+
return $constantString;
} elseif ($node instanceof DNumber) {
return new ConstantFloatType($node->value);
+ } 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;
@@ -1356,17 +1611,17 @@ private function resolveType(Expr $node): Type
$isVariadic = true;
}
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$parameters[] = new NativeParameterReflection(
$param->var->name,
$firstOptionalParameterIndex !== null && $i >= $firstOptionalParameterIndex,
- $this->getFunctionType($param->type, $param->type === null, false),
+ $this->getFunctionType($param->type, $this->isParameterValueNullable($param), false),
$param->byRef
? PassedByReference::createCreatesNewVariable()
: PassedByReference::createNo(),
$param->variadic,
- $param->default !== null ? $this->getType($param->default) : null
+ $param->default !== null ? $this->getType($param->default) : null,
);
}
@@ -1380,11 +1635,22 @@ private function resolveType(Expr $node): Type
if (
$functionName === 'array_map'
&& $argOrder === 0
- && isset($funcCall->args[1])
+ && isset($funcCall->getArgs()[1])
) {
- $callableParameters = [
- new DummyParameter('item', $this->getType($funcCall->args[1]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null),
- ];
+ if (!isset($funcCall->getArgs()[2])) {
+ $callableParameters = [
+ new DummyParameter('item', $this->getType($funcCall->getArgs()[1]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null),
+ ];
+ } else {
+ $callableParameters = [];
+ foreach ($funcCall->getArgs() as $i => $funcCallArg) {
+ if ($i === 0) {
+ continue;
+ }
+
+ $callableParameters[] = new DummyParameter('item', $this->getType($funcCallArg->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null);
+ }
+ }
}
}
}
@@ -1486,7 +1752,7 @@ private function resolveType(Expr $node): Type
$valueTypes[] = $yieldFromType->getIterableValueType();
}
- $returnType = new GenericObjectType(\Generator::class, [
+ $returnType = new GenericObjectType(Generator::class, [
TypeCombinator::union(...$keyTypes),
TypeCombinator::union(...$valueTypes),
new MixedType(),
@@ -1500,7 +1766,7 @@ private function resolveType(Expr $node): Type
return new ClosureType(
$parameters,
$returnType,
- $isVariadic
+ $isVariadic,
);
} elseif ($node instanceof New_) {
if ($node->class instanceof Name) {
@@ -1534,7 +1800,7 @@ private function resolveType(Expr $node): Type
} elseif ($node instanceof Array_) {
$arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
- if (count($node->items) > 256) {
+ if (count($node->items) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
$arrayBuilder->degradeToGeneralArray();
}
foreach ($node->items as $arrayItem) {
@@ -1545,17 +1811,34 @@ private function resolveType(Expr $node): Type
$valueType = $this->getType($arrayItem->value);
if ($arrayItem->unpack) {
if ($valueType instanceof ConstantArrayType) {
- foreach ($valueType->getValueTypes() as $innerValueType) {
- $arrayBuilder->setOffsetValueType(null, $innerValueType);
+ $hasStringKey = false;
+ foreach ($valueType->getKeyTypes() as $keyType) {
+ if ($keyType instanceof ConstantStringType) {
+ $hasStringKey = true;
+ break;
+ }
+ }
+
+ foreach ($valueType->getValueTypes() as $i => $innerValueType) {
+ if ($hasStringKey && $this->phpVersion->supportsArrayUnpackingWithStringKeys()) {
+ $arrayBuilder->setOffsetValueType($valueType->getKeyTypes()[$i], $innerValueType);
+ } else {
+ $arrayBuilder->setOffsetValueType(null, $innerValueType);
+ }
}
} else {
$arrayBuilder->degradeToGeneralArray();
- $arrayBuilder->setOffsetValueType(new IntegerType(), $valueType->getIterableValueType());
+
+ if (! (new StringType())->isSuperTypeOf($valueType->getIterableKeyType())->no() && $this->phpVersion->supportsArrayUnpackingWithStringKeys()) {
+ $arrayBuilder->setOffsetValueType($valueType->getIterableKeyType(), $valueType->getIterableValueType());
+ } else {
+ $arrayBuilder->setOffsetValueType(new IntegerType(), $valueType->getIterableValueType(), !$valueType->isIterableAtLeastOnce()->yes() && !$valueType->getIterableValueType()->isIterableAtLeastOnce()->yes());
+ }
}
} else {
$arrayBuilder->setOffsetValueType(
$arrayItem->key !== null ? $this->getType($arrayItem->key) : null,
- $valueType
+ $valueType,
);
}
}
@@ -1567,9 +1850,9 @@ private function resolveType(Expr $node): Type
return $this->getType($node->expr)->toBoolean();
} elseif ($node instanceof Double) {
return $this->getType($node->expr)->toFloat();
- } elseif ($node instanceof \PhpParser\Node\Expr\Cast\String_) {
+ } elseif ($node instanceof Node\Expr\Cast\String_) {
return $this->getType($node->expr)->toString();
- } elseif ($node instanceof \PhpParser\Node\Expr\Cast\Array_) {
+ } elseif ($node instanceof Node\Expr\Cast\Array_) {
return $this->getType($node->expr)->toArray();
} elseif ($node instanceof Node\Scalar\MagicConst\Line) {
return new ConstantIntegerType($node->getLine());
@@ -1596,7 +1879,7 @@ private function resolveType(Expr $node): Type
}
if ($function instanceof MethodReflection) {
return new ConstantStringType(
- sprintf('%s::%s', $function->getDeclaringClass()->getName(), $function->getName())
+ sprintf('%s::%s', $function->getDeclaringClass()->getName(), $function->getName()),
);
}
@@ -1638,6 +1921,7 @@ private function resolveType(Expr $node): Type
} elseif ($node instanceof Expr\PreInc || $node instanceof Expr\PreDec) {
$varType = $this->getType($node->var);
$varScalars = TypeUtils::getConstantScalars($varType);
+ $stringType = new StringType();
if (count($varScalars) > 0) {
$newTypes = [];
@@ -1652,16 +1936,18 @@ private function resolveType(Expr $node): Type
$newTypes[] = $this->getTypeFromValue($varValue);
}
return TypeCombinator::union(...$newTypes);
- } elseif ($varType instanceof IntegerRangeType) {
- return $varType->shift($node instanceof Expr\PreInc ? +1 : -1);
+ } elseif ($stringType->isSuperTypeOf($varType)->yes()) {
+ if ($varType->isLiteralString()->yes()) {
+ return new IntersectionType([$stringType, new AccessoryLiteralStringType()]);
+ }
+ return $stringType;
}
- $stringType = new StringType();
- if ($stringType->isSuperTypeOf($varType)->yes()) {
- return $stringType;
+ if ($node instanceof Expr\PreInc) {
+ return $this->getType(new BinaryOp\Plus($node->var, new LNumber(1)));
}
- return $varType->toNumber();
+ return $this->getType(new BinaryOp\Minus($node->var, new LNumber(1)));
} elseif ($node instanceof Expr\Yield_) {
$functionReflection = $this->getFunction();
if ($functionReflection === null) {
@@ -1673,7 +1959,7 @@ private function resolveType(Expr $node): Type
return new MixedType();
}
- $generatorSendType = GenericTypeVariableResolver::getType($returnType, \Generator::class, 'TSend');
+ $generatorSendType = GenericTypeVariableResolver::getType($returnType, Generator::class, 'TSend');
if ($generatorSendType === null) {
return new MixedType();
}
@@ -1686,7 +1972,7 @@ private function resolveType(Expr $node): Type
return new MixedType();
}
- $generatorReturnType = GenericTypeVariableResolver::getType($yieldFromType, \Generator::class, 'TReturn');
+ $generatorReturnType = GenericTypeVariableResolver::getType($yieldFromType, Generator::class, 'TReturn');
if ($generatorReturnType === null) {
return new MixedType();
}
@@ -1704,7 +1990,7 @@ private function resolveType(Expr $node): Type
}
if (count($arm->conds) === 0) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$filteringExpr = null;
@@ -1737,62 +2023,41 @@ private function resolveType(Expr $node): Type
}
if ($node instanceof Expr\BinaryOp\Coalesce) {
- if ($node->left instanceof Expr\ArrayDimFetch && $node->left->dim !== null) {
- $dimType = $this->getType($node->left->dim);
- $varType = $this->getType($node->left->var);
- $hasOffset = $varType->hasOffsetValueType($dimType);
- $leftType = $this->getType($node->left);
- $rightType = $this->filterByFalseyValue(
- new BinaryOp\NotIdentical($node->left, new ConstFetch(new Name('null')))
- )->getType($node->right);
- if ($hasOffset->no()) {
- return $rightType;
- } elseif ($hasOffset->yes()) {
- $offsetValueType = $varType->getOffsetValueType($dimType);
- if ($offsetValueType->isSuperTypeOf(new NullType())->no()) {
- return TypeCombinator::removeNull($leftType);
- }
- }
-
- return TypeCombinator::union(
- TypeCombinator::removeNull($leftType),
- $rightType
- );
- }
-
$leftType = $this->getType($node->left);
$rightType = $this->filterByFalseyValue(
- new BinaryOp\NotIdentical($node->left, new ConstFetch(new Name('null')))
+ new BinaryOp\NotIdentical($node->left, new ConstFetch(new Name('null'))),
)->getType($node->right);
- if ($leftType instanceof ErrorType || $leftType instanceof NullType) {
- return $rightType;
- }
- if (
- TypeCombinator::containsNull($leftType)
- || $node->left instanceof PropertyFetch
- || (
- $node->left instanceof Variable
- && is_string($node->left->name)
- && !$this->hasVariableType($node->left->name)->yes()
- )
- ) {
+ $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) {
return TypeCombinator::union(
TypeCombinator::removeNull($leftType),
- $rightType
+ $rightType,
);
}
- return TypeCombinator::removeNull($leftType);
+ if ($result) {
+ return TypeCombinator::removeNull($leftType);
+ }
+
+ return $rightType;
}
if ($node instanceof ConstFetch) {
$constName = (string) $node->name;
$loweredConstName = strtolower($constName);
if ($loweredConstName === 'true') {
- return new \PHPStan\Type\Constant\ConstantBooleanType(true);
+ return new ConstantBooleanType(true);
} elseif ($loweredConstName === 'false') {
- return new \PHPStan\Type\Constant\ConstantBooleanType(false);
+ return new ConstantBooleanType(false);
} elseif ($loweredConstName === 'null') {
return new NullType();
}
@@ -1801,23 +2066,199 @@ private function resolveType(Expr $node): Type
if (array_key_exists($node->name->toCodeString(), $this->constantTypes)) {
return $this->resolveConstantType($node->name->toString(), $this->constantTypes[$node->name->toCodeString()]);
}
- }
-
- if ($this->getNamespace() !== null) {
- $constantName = new FullyQualified([$this->getNamespace(), $constName]);
- if (array_key_exists($constantName->toCodeString(), $this->constantTypes)) {
- return $this->resolveConstantType($constantName->toString(), $this->constantTypes[$constantName->toCodeString()]);
+ }
+
+ if ($this->getNamespace() !== null) {
+ $constantName = new FullyQualified([$this->getNamespace(), $constName]);
+ if (array_key_exists($constantName->toCodeString(), $this->constantTypes)) {
+ return $this->resolveConstantType($constantName->toString(), $this->constantTypes[$constantName->toCodeString()]);
+ }
+ }
+
+ $constantName = new FullyQualified($constName);
+ if (array_key_exists($constantName->toCodeString(), $this->constantTypes)) {
+ return $this->resolveConstantType($constantName->toString(), $this->constantTypes[$constantName->toCodeString()]);
+ }
+
+ if ($this->reflectionProvider->hasConstant($node->name, $this)) {
+ /** @var string $resolvedConstantName */
+ $resolvedConstantName = $this->reflectionProvider->resolveConstantName($node->name, $this);
+ // core, https://www.php.net/manual/en/reserved.constants.php
+ if ($resolvedConstantName === 'PHP_VERSION') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_MAJOR_VERSION') {
+ return IntegerRangeType::fromInterval(5, null);
+ }
+ if ($resolvedConstantName === 'PHP_MINOR_VERSION') {
+ return IntegerRangeType::fromInterval(0, null);
+ }
+ if ($resolvedConstantName === 'PHP_RELEASE_VERSION') {
+ return IntegerRangeType::fromInterval(0, null);
+ }
+ if ($resolvedConstantName === 'PHP_VERSION_ID') {
+ return IntegerRangeType::fromInterval(50207, null);
+ }
+ if ($resolvedConstantName === 'PHP_ZTS') {
+ return new UnionType([
+ new ConstantIntegerType(0),
+ new ConstantIntegerType(1),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_DEBUG') {
+ return new UnionType([
+ new ConstantIntegerType(0),
+ new ConstantIntegerType(1),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_MAXPATHLEN') {
+ return IntegerRangeType::fromInterval(1, null);
+ }
+ if ($resolvedConstantName === 'PHP_OS') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_OS_FAMILY') {
+ return new UnionType([
+ new ConstantStringType('Windows'),
+ new ConstantStringType('BSD'),
+ new ConstantStringType('Darwin'),
+ new ConstantStringType('Solaris'),
+ new ConstantStringType('Linux'),
+ new ConstantStringType('Unknown'),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_SAPI') {
+ return new UnionType([
+ new ConstantStringType('apache'),
+ new ConstantStringType('apache2handler'),
+ new ConstantStringType('cgi'),
+ new ConstantStringType('cli'),
+ new ConstantStringType('cli-server'),
+ new ConstantStringType('embed'),
+ new ConstantStringType('fpm-fcgi'),
+ new ConstantStringType('litespeed'),
+ new ConstantStringType('phpdbg'),
+ new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_EOL') {
+ return new UnionType([
+ new ConstantStringType("\n"),
+ new ConstantStringType("\r\n"),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_INT_MAX') {
+ return PHP_INT_SIZE === 8
+ ? new UnionType([new ConstantIntegerType(2147483647), new ConstantIntegerType(9223372036854775807)])
+ : new ConstantIntegerType(2147483647);
+ }
+ if ($resolvedConstantName === 'PHP_INT_MIN') {
+ // Why the -1 you might wonder, the answer is to fit it into an int :/ see https://3v4l.org/4SHIQ
+ return PHP_INT_SIZE === 8
+ ? new UnionType([new ConstantIntegerType(-9223372036854775807 - 1), new ConstantIntegerType(-2147483647 - 1)])
+ : new ConstantIntegerType(-2147483647 - 1);
+ }
+ if ($resolvedConstantName === 'PHP_INT_SIZE') {
+ return new UnionType([
+ new ConstantIntegerType(4),
+ new ConstantIntegerType(8),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_FLOAT_DIG') {
+ return IntegerRangeType::fromInterval(1, null);
+ }
+ if ($resolvedConstantName === 'PHP_EXTENSION_DIR') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
}
- }
-
- $constantName = new FullyQualified($constName);
- if (array_key_exists($constantName->toCodeString(), $this->constantTypes)) {
- return $this->resolveConstantType($constantName->toString(), $this->constantTypes[$constantName->toCodeString()]);
- }
-
- if ($this->reflectionProvider->hasConstant($node->name, $this)) {
- /** @var string $resolvedConstantName */
- $resolvedConstantName = $this->reflectionProvider->resolveConstantName($node->name, $this);
+ if ($resolvedConstantName === 'PHP_PREFIX') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_BINDIR') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_BINARY') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_MANDIR') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_LIBDIR') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_DATADIR') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_SYSCONFDIR') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_LOCALSTATEDIR') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_CONFIG_FILE_PATH') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_SHLIB_SUFFIX') {
+ return new UnionType([
+ new ConstantStringType('so'),
+ new ConstantStringType('dll'),
+ ]);
+ }
+ if ($resolvedConstantName === 'PHP_FD_SETSIZE') {
+ return IntegerRangeType::fromInterval(1, null);
+ }
+ if ($resolvedConstantName === '__COMPILER_HALT_OFFSET__') {
+ return IntegerRangeType::fromInterval(0, null);
+ }
+ // core other, https://www.php.net/manual/en/info.constants.php
+ if ($resolvedConstantName === 'PHP_WINDOWS_VERSION_MAJOR') {
+ return IntegerRangeType::fromInterval(4, null);
+ }
+ if ($resolvedConstantName === 'PHP_WINDOWS_VERSION_MINOR') {
+ return IntegerRangeType::fromInterval(0, null);
+ }
+ if ($resolvedConstantName === 'PHP_WINDOWS_VERSION_BUILD') {
+ return IntegerRangeType::fromInterval(1, null);
+ }
+ // dir, https://www.php.net/manual/en/dir.constants.php
if ($resolvedConstantName === 'DIRECTORY_SEPARATOR') {
return new UnionType([
new ConstantStringType('/'),
@@ -1830,14 +2271,26 @@ private function resolveType(Expr $node): Type
new ConstantStringType(';'),
]);
}
- if ($resolvedConstantName === 'PHP_EOL') {
- return new UnionType([
- new ConstantStringType("\n"),
- new ConstantStringType("\r\n"),
+ // iconv, https://www.php.net/manual/en/iconv.constants.php
+ if ($resolvedConstantName === 'ICONV_IMPL') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
]);
}
- if ($resolvedConstantName === '__COMPILER_HALT_OFFSET__') {
- return new IntegerType();
+ // libxml, https://www.php.net/manual/en/libxml.constants.php
+ if ($resolvedConstantName === 'LIBXML_VERSION') {
+ return IntegerRangeType::fromInterval(1, null);
+ }
+ if ($resolvedConstantName === 'LIBXML_DOTTED_VERSION') {
+ return new IntersectionType([
+ new StringType(),
+ new AccessoryNonEmptyStringType(),
+ ]);
+ }
+ // openssl, https://www.php.net/manual/en/openssl.constants.php
+ if ($resolvedConstantName === 'OPENSSL_VERSION_NUMBER') {
+ return IntegerRangeType::fromInterval(1, null);
}
$constantType = $this->reflectionProvider->getConstant($node->name, $this)->getValueType();
@@ -1848,6 +2301,7 @@ private function resolveType(Expr $node): Type
return new ErrorType();
} elseif ($node instanceof Node\Expr\ClassConstFetch && $node->name instanceof Node\Identifier) {
$constantName = $node->name->name;
+ $isObject = false;
if ($node->class instanceof Name) {
$constantClass = (string) $node->class;
$constantClassType = new ObjectType($constantClass);
@@ -1862,7 +2316,9 @@ private function resolveType(Expr $node): Type
if (strtolower($constantName) === 'class') {
return new GenericClassStringType(new StaticType($this->getClassReflection()));
}
- return new MixedType();
+
+ $namesToResolve[] = 'static';
+ $isObject = true;
}
}
if (in_array(strtolower($constantClass), $namesToResolve, true)) {
@@ -1878,11 +2334,15 @@ private function resolveType(Expr $node): Type
}
} else {
$constantClassType = $this->getType($node->class);
+ $isObject = true;
}
$referencedClasses = TypeUtils::getDirectClassNames($constantClassType);
if (strtolower($constantName) === 'class') {
if (count($referencedClasses) === 0) {
+ if ((new ObjectWithoutClassType())->isSuperTypeOf($constantClassType)->yes()) {
+ return new ClassStringType();
+ }
return new ErrorType();
}
$classTypes = [];
@@ -1898,17 +2358,43 @@ private function resolveType(Expr $node): Type
continue;
}
- $propertyClassReflection = $this->reflectionProvider->getClass($referencedClass);
- if (!$propertyClassReflection->hasConstant($constantName)) {
+ $constantClassReflection = $this->reflectionProvider->getClass($referencedClass);
+ if (!$constantClassReflection->hasConstant($constantName)) {
continue;
}
- $constantType = $propertyClassReflection->getConstant($constantName)->getValueType();
+ if ($constantClassReflection->isEnum() && $constantClassReflection->hasEnumCase($constantName)) {
+ $types[] = new EnumCaseObjectType($constantClassReflection->getName(), $constantName);
+ continue;
+ }
+
+ $constantReflection = $constantClassReflection->getConstant($constantName);
+ if (
+ $constantReflection instanceof ClassConstantReflection
+ && $isObject
+ && !$constantClassReflection->isFinal()
+ && !$constantReflection->hasPhpDocType()
+ ) {
+ return new MixedType();
+ }
+
+ if (
+ $isObject
+ && (
+ !$constantReflection instanceof ClassConstantReflection
+ || !$constantClassReflection->isFinal()
+ )
+ ) {
+ $constantType = $constantReflection->getValueType();
+ } else {
+ $constantType = ConstantTypeHelper::getTypeFromValue($constantReflection->getValue());
+ }
+
if (
$constantType instanceof ConstantType
- && in_array(sprintf('%s::%s', $propertyClassReflection->getName(), $constantName), $this->dynamicConstantNames, true)
+ && in_array(sprintf('%s::%s', $constantClassReflection->getName(), $constantName), $this->dynamicConstantNames, true)
) {
- $constantType = $constantType->generalize();
+ $constantType = $constantType->generalize(GeneralizePrecision::lessSpecific());
}
$types[] = $constantType;
}
@@ -1937,7 +2423,7 @@ private function resolveType(Expr $node): Type
}
return TypeCombinator::union(
TypeCombinator::remove($this->filterByTruthyValue($node->cond)->getType($node->cond), StaticTypeFactory::falsey()),
- $this->filterByFalseyValue($node->cond)->getType($node->else)
+ $this->filterByFalseyValue($node->cond)->getType($node->else),
);
}
@@ -1952,7 +2438,7 @@ private function resolveType(Expr $node): Type
return TypeCombinator::union(
$this->filterByTruthyValue($node->cond)->getType($node->if),
- $this->filterByFalseyValue($node->cond)->getType($node->else)
+ $this->filterByFalseyValue($node->cond)->getType($node->else),
);
}
@@ -1970,8 +2456,8 @@ private function resolveType(Expr $node): Type
$this->getTypeFromArrayDimFetch(
$node,
$this->getType($node->dim),
- $this->getType($node->var)
- )
+ $this->getType($node->var),
+ ),
);
}
@@ -1980,7 +2466,7 @@ private function resolveType(Expr $node): Type
$returnType = $this->methodCallReturnType(
$this->getType($node->var),
$node->name->name,
- $node
+ $node,
);
if ($returnType === null) {
return new ErrorType();
@@ -2000,7 +2486,7 @@ private function resolveType(Expr $node): Type
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()
+ new NullType(),
);
}
@@ -2009,16 +2495,27 @@ private function resolveType(Expr $node): Type
if ($node->class instanceof Name) {
$staticMethodCalledOnType = $this->resolveTypeByName($node->class);
} else {
- $staticMethodCalledOnType = $this->getType($node->class);
- if ($staticMethodCalledOnType instanceof GenericClassStringType) {
- $staticMethodCalledOnType = $staticMethodCalledOnType->getGenericType();
- }
+ $staticMethodCalledOnType = TypeTraverser::map($this->getType($node->class), static function (Type $type, callable $traverse): Type {
+ if ($type instanceof UnionType) {
+ return $traverse($type);
+ }
+
+ if ($type instanceof GenericClassStringType) {
+ return $type->getGenericType();
+ }
+
+ if ($type instanceof ConstantStringType && $type->isClassString()) {
+ return new ObjectType($type->getValue());
+ }
+
+ return $type;
+ });
}
$returnType = $this->methodCallReturnType(
$staticMethodCalledOnType,
$node->name->toString(),
- $node
+ $node,
);
if ($returnType === null) {
return new ErrorType();
@@ -2039,7 +2536,7 @@ private function resolveType(Expr $node): Type
$returnType = $this->propertyFetchType(
$this->getType($node->var),
$node->name->name,
- $node
+ $node,
);
if ($returnType === null) {
return new ErrorType();
@@ -2059,7 +2556,7 @@ private function resolveType(Expr $node): Type
return TypeCombinator::union(
$this->filterByTruthyValue(new BinaryOp\NotIdentical($node->var, new ConstFetch(new Name('null'))))
->getType(new PropertyFetch($node->var, $node->name)),
- new NullType()
+ new NullType(),
);
}
@@ -2080,7 +2577,7 @@ private function resolveType(Expr $node): Type
$returnType = $this->propertyFetchType(
$staticPropertyFetchedOnType,
$node->name->toString(),
- $node
+ $node,
);
if ($returnType === null) {
return new ErrorType();
@@ -2105,8 +2602,8 @@ private function resolveType(Expr $node): Type
return ParametersAcceptorSelector::selectFromArgs(
$this,
- $node->args,
- $calledOnType->getCallableParametersAcceptors($this)
+ $node->getArgs(),
+ $calledOnType->getCallableParametersAcceptors($this),
)->getReturnType();
}
@@ -2120,19 +2617,106 @@ private function resolveType(Expr $node): Type
continue;
}
- return $dynamicFunctionReturnTypeExtension->getTypeFromFunctionCall($functionReflection, $node, $this);
+ $resolvedType = $dynamicFunctionReturnTypeExtension->getTypeFromFunctionCall(
+ $functionReflection,
+ $node,
+ $this,
+ );
+ if ($resolvedType !== null) {
+ return $resolvedType;
+ }
}
return ParametersAcceptorSelector::selectFromArgs(
$this,
- $node->args,
- $functionReflection->getVariants()
+ $node->getArgs(),
+ $functionReflection->getVariants(),
)->getReturnType();
}
return new MixedType();
}
+ private function resolveConcatType(Expr\BinaryOp\Concat|Expr\AssignOp\Concat $node): Type
+ {
+ if ($node instanceof Node\Expr\AssignOp) {
+ $left = $node->var;
+ $right = $node->expr;
+ } else {
+ $left = $node->left;
+ $right = $node->right;
+ }
+
+ $leftStringType = $this->getType($left)->toString();
+ $rightStringType = $this->getType($right)->toString();
+ if (TypeCombinator::union(
+ $leftStringType,
+ $rightStringType,
+ ) instanceof ErrorType) {
+ return new ErrorType();
+ }
+
+ if ($leftStringType instanceof ConstantStringType && $leftStringType->getValue() === '') {
+ return $rightStringType;
+ }
+
+ if ($rightStringType instanceof ConstantStringType && $rightStringType->getValue() === '') {
+ return $leftStringType;
+ }
+
+ if ($leftStringType instanceof ConstantStringType && $rightStringType instanceof ConstantStringType) {
+ return $leftStringType->append($rightStringType);
+ }
+
+ // we limit the number of union-types for performance reasons
+ if ($leftStringType instanceof UnionType && count($leftStringType->getTypes()) <= 16 && $rightStringType instanceof ConstantStringType) {
+ $constantStrings = TypeUtils::getConstantStrings($leftStringType);
+ if (count($constantStrings) > 0) {
+ $strings = [];
+ foreach ($constantStrings as $constantString) {
+ if ($constantString->getValue() === '') {
+ $strings[] = $rightStringType;
+
+ continue;
+ }
+ $strings[] = $constantString->append($rightStringType);
+ }
+ return TypeCombinator::union(...$strings);
+ }
+ }
+ if ($rightStringType instanceof UnionType && count($rightStringType->getTypes()) <= 16 && $leftStringType instanceof ConstantStringType) {
+ $constantStrings = TypeUtils::getConstantStrings($rightStringType);
+ if (count($constantStrings) > 0) {
+ $strings = [];
+ foreach ($constantStrings as $constantString) {
+ if ($constantString->getValue() === '') {
+ $strings[] = $leftStringType;
+
+ continue;
+ }
+ $strings[] = $leftStringType->append($constantString);
+ }
+ return TypeCombinator::union(...$strings);
+ }
+ }
+
+ $accessoryTypes = [];
+ if ($leftStringType->isNonEmptyString()->or($rightStringType->isNonEmptyString())->yes()) {
+ $accessoryTypes[] = new AccessoryNonEmptyStringType();
+ }
+
+ if ($leftStringType->isLiteralString()->and($rightStringType->isLiteralString())->yes()) {
+ $accessoryTypes[] = new AccessoryLiteralStringType();
+ }
+
+ if (count($accessoryTypes) > 0) {
+ $accessoryTypes[] = new StringType();
+ return new IntersectionType($accessoryTypes);
+ }
+
+ return new StringType();
+ }
+
private function getNullsafeShortCircuitingType(Expr $expr, Type $type): Type
{
if ($expr instanceof Expr\NullsafePropertyFetch || $expr instanceof Expr\NullsafeMethodCall) {
@@ -2167,10 +2751,201 @@ private function getNullsafeShortCircuitingType(Expr $expr, Type $type): Type
return $type;
}
+ /**
+ * @param callable(Type): ?bool $typeCallback
+ */
+ private 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->treatPhpDocTypesAsCertain
+ ? $this->getType($expr->var)
+ : $this->getNativeType($expr->var);
+ $dimType = $this->treatPhpDocTypesAsCertain
+ ? $this->getType($expr->dim)
+ : $this->getNativeType($expr->dim);
+ $hasOffsetValue = $type->hasOffsetValueType($dimType);
+ if (!$type->isOffsetAccessible()->yes()) {
+ return $result ?? $this->issetCheckUndefined($expr->var);
+ }
+
+ if ($hasOffsetValue->no()) {
+ if ($result !== null) {
+ return $result;
+ }
+
+ return false;
+ }
+
+ if ($hasOffsetValue->maybe()) {
+ return null;
+ }
+
+ // 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()) {
+ if ($result !== null) {
+ return $result;
+ }
+
+ $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->isSpecified($expr)) {
+ 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) {
+ $parameters = $variant->getParameters();
+ $closureTypes[] = new ClosureType(
+ $parameters,
+ $variant->getReturnType(),
+ $variant->isVariadic(),
+ $variant->getTemplateTypeMap(),
+ $variant->getResolvedTemplateTypeMap(),
+ );
+ }
+
+ return TypeCombinator::union(...$closureTypes);
+ }
+
private function resolveConstantType(string $constantName, Type $constantType): Type
{
if ($constantType instanceof ConstantType && in_array($constantName, $this->dynamicConstantNames, true)) {
- return $constantType->generalize();
+ return $constantType->generalize(GeneralizePrecision::lessSpecific());
}
return $constantType;
@@ -2191,8 +2966,8 @@ public function getNativeType(Expr $expr): Type
$this->getTypeFromArrayDimFetch(
$expr,
$this->getNativeType($expr->dim),
- $this->getNativeType($expr->var)
- )
+ $this->getNativeType($expr->var),
+ ),
);
}
@@ -2217,6 +2992,7 @@ public function doNotTreatPhpDocTypesAsCertain(): Scope
$this->parser,
$this->nodeScopeResolver,
$this->context,
+ $this->phpVersion,
$this->declareStrictTypes,
$this->constantTypes,
$this->function,
@@ -2232,9 +3008,9 @@ public function doNotTreatPhpDocTypesAsCertain(): Scope
$this->inFunctionCallsStack,
$this->dynamicConstantNames,
false,
- $this->objectFromNewClass,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
+ $this->explicitMixedInUnknownGenericNew,
);
}
@@ -2243,13 +3019,13 @@ private function promoteNativeTypes(): self
$variableTypes = $this->variableTypes;
foreach ($this->nativeExpressionTypes as $expressionType => $type) {
if (substr($expressionType, 0, 1) !== '$') {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$variableName = substr($expressionType, 1);
$has = $this->hasVariableType($variableName);
if ($has->no()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$variableTypes[$variableName] = new VariableTypeHolder($type, $has);
@@ -2268,13 +3044,12 @@ private function promoteNativeTypes(): self
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
$this->currentlyAssignedExpressions,
- []
+ [],
);
}
/**
- * @param \PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\StaticPropertyFetch $propertyFetch
- * @return bool
+ * @param Node\Expr\PropertyFetch|Node\Expr\StaticPropertyFetch $propertyFetch
*/
private function hasPropertyNativeType($propertyFetch): bool
{
@@ -2294,22 +3069,22 @@ private function hasPropertyNativeType($propertyFetch): bool
protected function getTypeFromArrayDimFetch(
Expr\ArrayDimFetch $arrayDimFetch,
Type $offsetType,
- Type $offsetAccessibleType
+ Type $offsetAccessibleType,
): Type
{
if ($arrayDimFetch->dim === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
- if ((new ObjectType(\ArrayAccess::class))->isSuperTypeOf($offsetAccessibleType)->yes()) {
+ if ((new ObjectType(ArrayAccess::class))->isSuperTypeOf($offsetAccessibleType)->yes()) {
return $this->getType(
new MethodCall(
$arrayDimFetch->var,
new Node\Identifier('offsetGet'),
[
new Node\Arg($arrayDimFetch->dim),
- ]
- )
+ ],
+ ),
);
}
@@ -2351,7 +3126,7 @@ private function calculateFromScalars(Expr $node, ConstantScalarType $leftType,
}
if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
/** @var float|int $leftNumberValue */
@@ -2381,7 +3156,7 @@ private function calculateFromScalars(Expr $node, ConstantScalarType $leftType,
}
if ($node instanceof Node\Expr\BinaryOp\Mod || $node instanceof Node\Expr\AssignOp\Mod) {
- return $this->getTypeFromValue($leftNumberValue % $rightNumberValue);
+ return $this->getTypeFromValue(((int) $leftNumberValue) % ((int) $rightNumberValue));
}
if ($node instanceof Expr\BinaryOp\ShiftLeft || $node instanceof Expr\AssignOp\ShiftLeft) {
@@ -2422,7 +3197,7 @@ private function resolveExactName(Name $name): ?string
return null;
}
$currentClassReflection = $this->getClassReflection();
- if ($currentClassReflection->getParentClass() !== false) {
+ if ($currentClassReflection->getParentClass() !== null) {
return $currentClassReflection->getParentClass()->getName();
}
return null;
@@ -2448,7 +3223,7 @@ public function resolveName(Name $name): string
return $this->getClassReflection()->getName();
} elseif ($originalClass === 'parent') {
$currentClassReflection = $this->getClassReflection();
- if ($currentClassReflection->getParentClass() !== false) {
+ if ($currentClassReflection->getParentClass() !== null) {
return $currentClassReflection->getParentClass()->getName();
}
}
@@ -2462,7 +3237,9 @@ public function resolveTypeByName(Name $name): TypeWithClassName
{
if ($name->toLowerString() === 'static' && $this->isInClass()) {
if ($this->inClosureBindScopeClass !== null && $this->inClosureBindScopeClass !== 'static') {
- return new StaticType($this->inClosureBindScopeClass);
+ if ($this->reflectionProvider->hasClass($this->inClosureBindScopeClass)) {
+ return new StaticType($this->reflectionProvider->getClass($this->inClosureBindScopeClass));
+ }
}
return new StaticType($this->getClassReflection());
@@ -2470,11 +3247,14 @@ public function resolveTypeByName(Name $name): TypeWithClassName
$originalClass = $this->resolveName($name);
if ($this->isInClass()) {
- if ($this->inClosureBindScopeClass !== null && $this->inClosureBindScopeClass !== 'static') {
- $thisType = new ThisType($this->inClosureBindScopeClass);
- } else {
- $thisType = new ThisType($this->getClassReflection());
+ if ($this->inClosureBindScopeClass !== null && $this->inClosureBindScopeClass !== 'static' && $originalClass === $this->getClassReflection()->getName()) {
+ if ($this->reflectionProvider->hasClass($this->inClosureBindScopeClass)) {
+ return new ThisType($this->reflectionProvider->getClass($this->inClosureBindScopeClass));
+ }
+ return new ObjectType($this->inClosureBindScopeClass);
}
+
+ $thisType = new ThisType($this->getClassReflection());
$ancestor = $thisType->getAncestorWithClassName($originalClass);
if ($ancestor !== null) {
return $ancestor;
@@ -2504,7 +3284,6 @@ public function isSpecified(Expr $node): bool
/**
* @param MethodReflection|FunctionReflection $reflection
- * @return self
*/
public function pushInFunctionCall($reflection): self
{
@@ -2527,7 +3306,7 @@ public function pushInFunctionCall($reflection): self
$this->nativeExpressionTypes,
$stack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -2552,7 +3331,7 @@ public function popInFunctionCall(): self
$this->nativeExpressionTypes,
$stack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -2579,6 +3358,16 @@ public function isInClassExists(string $className): bool
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
{
@@ -2590,7 +3379,7 @@ public function enterClass(ClassReflection $classReflection): self
$this->getNamespace(),
[
'this' => VariableTypeHolder::createYes(new ThisType($classReflection)),
- ]
+ ],
);
}
@@ -2606,23 +3395,13 @@ public function enterTrait(ClassReflection $traitReflection): self
$this->moreSpecificTypes,
[],
$this->inClosureBindScopeClass,
- $this->anonymousFunctionReflection
+ $this->anonymousFunctionReflection,
);
}
/**
* @api
- * @param Node\Stmt\ClassMethod $classMethod
- * @param TemplateTypeMap $templateTypeMap
* @param Type[] $phpDocParameterTypes
- * @param Type|null $phpDocReturnType
- * @param Type|null $throwType
- * @param string|null $deprecatedDescription
- * @param bool $isDeprecated
- * @param bool $isInternal
- * @param bool $isFinal
- * @param bool|null $isPure
- * @return self
*/
public function enterClassMethod(
Node\Stmt\ClassMethod $classMethod,
@@ -2634,33 +3413,32 @@ public function enterClassMethod(
bool $isDeprecated,
bool $isInternal,
bool $isFinal,
- ?bool $isPure = null
+ ?bool $isPure = null,
): self
{
if (!$this->isInClass()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return $this->enterFunctionLike(
new PhpMethodFromParserNodeReflection(
$this->getClassReflection(),
$classMethod,
+ $this->getFile(),
$templateTypeMap,
$this->getRealParameterTypes($classMethod),
- array_map(static function (Type $type): Type {
- return TemplateTypeHelper::toArgument($type);
- }, $phpDocParameterTypes),
+ array_map(static fn (Type $type): Type => TemplateTypeHelper::toArgument($type), $phpDocParameterTypes),
$this->getRealParameterDefaultValues($classMethod),
- $this->transformStaticType($this->getFunctionType($classMethod->returnType, $classMethod->returnType === null, false)),
+ $this->transformStaticType($this->getFunctionType($classMethod->returnType, false, false)),
$phpDocReturnType !== null ? TemplateTypeHelper::toArgument($phpDocReturnType) : null,
$throwType,
$deprecatedDescription,
$isDeprecated,
$isInternal,
$isFinal,
- $isPure
+ $isPure,
),
- !$classMethod->isStatic()
+ !$classMethod->isStatic(),
);
}
@@ -2684,7 +3462,6 @@ private function transformStaticType(Type $type): Type
}
/**
- * @param Node\FunctionLike $functionLike
* @return Type[]
*/
private function getRealParameterTypes(Node\FunctionLike $functionLike): array
@@ -2692,12 +3469,12 @@ 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 \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$realParameterTypes[$parameter->var->name] = $this->getFunctionType(
$parameter->type,
$this->isParameterValueNullable($parameter),
- false
+ false,
);
}
@@ -2705,7 +3482,6 @@ private function getRealParameterTypes(Node\FunctionLike $functionLike): array
}
/**
- * @param Node\FunctionLike $functionLike
* @return Type[]
*/
private function getRealParameterDefaultValues(Node\FunctionLike $functionLike): array
@@ -2716,7 +3492,7 @@ private function getRealParameterDefaultValues(Node\FunctionLike $functionLike):
continue;
}
if (!$parameter->var instanceof Variable || !is_string($parameter->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$realParameterDefaultValues[$parameter->var->name] = $this->getType($parameter->default);
}
@@ -2726,17 +3502,7 @@ private function getRealParameterDefaultValues(Node\FunctionLike $functionLike):
/**
* @api
- * @param Node\Stmt\Function_ $function
- * @param TemplateTypeMap $templateTypeMap
* @param Type[] $phpDocParameterTypes
- * @param Type|null $phpDocReturnType
- * @param Type|null $throwType
- * @param string|null $deprecatedDescription
- * @param bool $isDeprecated
- * @param bool $isInternal
- * @param bool $isFinal
- * @param bool|null $isPure
- * @return self
*/
public function enterFunction(
Node\Stmt\Function_ $function,
@@ -2748,17 +3514,16 @@ public function enterFunction(
bool $isDeprecated,
bool $isInternal,
bool $isFinal,
- ?bool $isPure = null
+ ?bool $isPure = null,
): self
{
return $this->enterFunctionLike(
new PhpFunctionFromParserNodeReflection(
$function,
+ $this->getFile(),
$templateTypeMap,
$this->getRealParameterTypes($function),
- array_map(static function (Type $type): Type {
- return TemplateTypeHelper::toArgument($type);
- }, $phpDocParameterTypes),
+ 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,
@@ -2767,15 +3532,15 @@ public function enterFunction(
$isDeprecated,
$isInternal,
$isFinal,
- $isPure
+ $isPure,
),
- false
+ false,
);
}
private function enterFunctionLike(
PhpFunctionFromParserNodeReflection $functionReflection,
- bool $preserveThis
+ bool $preserveThis,
): self
{
$variableTypes = [];
@@ -2783,10 +3548,23 @@ private function enterFunctionLike(
foreach (ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getParameters() as $parameter) {
$parameterType = $parameter->getType();
if ($parameter->isVariadic()) {
- $parameterType = new ArrayType(new IntegerType(), $parameterType);
+ if ($this->phpVersion->supportsNamedArguments()) {
+ $parameterType = new ArrayType(new UnionType([new IntegerType(), new StringType()]), $parameterType);
+ } else {
+ $parameterType = new ArrayType(new IntegerType(), $parameterType);
+ }
}
$variableTypes[$parameter->getName()] = VariableTypeHolder::createYes($parameterType);
- $nativeExpressionTypes[sprintf('$%s', $parameter->getName())] = $parameter->getNativeType();
+
+ $nativeParameterType = $parameter->getNativeType();
+ if ($parameter->isVariadic()) {
+ if ($this->phpVersion->supportsNamedArguments()) {
+ $nativeParameterType = new ArrayType(new UnionType([new IntegerType(), new StringType()]), $nativeParameterType);
+ } else {
+ $nativeParameterType = new ArrayType(new IntegerType(), $nativeParameterType);
+ }
+ }
+ $nativeExpressionTypes[sprintf('$%s', $parameter->getName())] = $nativeParameterType;
}
if ($preserveThis && array_key_exists('this', $this->variableTypes)) {
@@ -2806,7 +3584,7 @@ private function enterFunctionLike(
null,
true,
[],
- $nativeExpressionTypes
+ $nativeExpressionTypes,
);
}
@@ -2817,7 +3595,7 @@ public function enterNamespace(string $namespaceName): self
$this->isDeclareStrictTypes(),
$this->constantTypes,
null,
- $namespaceName
+ $namespaceName,
);
}
@@ -2845,7 +3623,7 @@ public function enterClosureBind(?Type $thisType, string $scopeClass): self
$this->moreSpecificTypes,
$this->conditionalExpressions,
$scopeClass,
- $this->anonymousFunctionReflection
+ $this->anonymousFunctionReflection,
);
}
@@ -2868,7 +3646,7 @@ public function restoreOriginalScopeAfterClosureBind(self $originalScope): self
$this->moreSpecificTypes,
$this->conditionalExpressions,
$originalScope->inClosureBindScopeClass,
- $this->anonymousFunctionReflection
+ $this->anonymousFunctionReflection,
);
}
@@ -2887,7 +3665,7 @@ public function enterClosureCall(Type $thisType): self
$this->moreSpecificTypes,
$this->conditionalExpressions,
$thisType instanceof TypeWithClassName ? $thisType->getClassName() : null,
- $this->anonymousFunctionReflection
+ $this->anonymousFunctionReflection,
);
}
@@ -2899,18 +3677,16 @@ public function isInClosureBind(): bool
/**
* @api
- * @param \PhpParser\Node\Expr\Closure $closure
- * @param \PHPStan\Reflection\ParameterReflection[]|null $callableParameters
- * @return self
+ * @param ParameterReflection[]|null $callableParameters
*/
public function enterAnonymousFunction(
Expr\Closure $closure,
- ?array $callableParameters = null
+ ?array $callableParameters = null,
): self
{
$anonymousFunctionReflection = $this->getType($closure);
if (!$anonymousFunctionReflection instanceof ClosureType) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$scope = $this->enterAnonymousFunctionWithoutReflection($closure, $callableParameters);
@@ -2931,18 +3707,16 @@ public function enterAnonymousFunction(
$scope->nativeExpressionTypes,
[],
false,
- $this
+ $this,
);
}
/**
- * @param \PhpParser\Node\Expr\Closure $closure
- * @param \PHPStan\Reflection\ParameterReflection[]|null $callableParameters
- * @return self
+ * @param ParameterReflection[]|null $callableParameters
*/
private function enterAnonymousFunctionWithoutReflection(
Expr\Closure $closure,
- ?array $callableParameters = null
+ ?array $callableParameters = null,
): self
{
$variableTypes = [];
@@ -2965,10 +3739,10 @@ private function enterAnonymousFunctionWithoutReflection(
}
if (!$parameter->var instanceof Variable || !is_string($parameter->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$variableTypes[$parameter->var->name] = VariableTypeHolder::createYes(
- $parameterType
+ $parameterType,
);
}
@@ -2976,7 +3750,7 @@ private function enterAnonymousFunctionWithoutReflection(
$moreSpecificTypes = [];
foreach ($closure->uses as $use) {
if (!is_string($use->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if ($use->byRef) {
continue;
@@ -2990,7 +3764,7 @@ private function enterAnonymousFunctionWithoutReflection(
}
$variableTypes[$variableName] = VariableTypeHolder::createYes($variableType);
foreach ($this->moreSpecificTypes as $exprString => $moreSpecificType) {
- $matches = \Nette\Utils\Strings::matchAll((string) $exprString, '#^\$([a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*)#');
+ $matches = Strings::matchAll((string) $exprString, '#^\$([a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*)#');
if ($matches === []) {
continue;
}
@@ -3024,19 +3798,19 @@ private function enterAnonymousFunctionWithoutReflection(
$nativeTypes,
[],
false,
- $this
+ $this,
);
}
/**
* @api
- * @param \PHPStan\Reflection\ParameterReflection[]|null $callableParameters
+ * @param ParameterReflection[]|null $callableParameters
*/
public function enterArrowFunction(Expr\ArrowFunction $arrowFunction, ?array $callableParameters = null): self
{
$anonymousFunctionReflection = $this->getType($arrowFunction);
if (!$anonymousFunctionReflection instanceof ClosureType) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$scope = $this->enterArrowFunctionWithoutReflection($arrowFunction, $callableParameters);
@@ -3057,18 +3831,19 @@ public function enterArrowFunction(Expr\ArrowFunction $arrowFunction, ?array $ca
[],
[],
$scope->afterExtractCall,
- $scope->parentScope
+ $scope->parentScope,
);
}
/**
- * @param \PHPStan\Reflection\ParameterReflection[]|null $callableParameters
+ * @param ParameterReflection[]|null $callableParameters
*/
private function enterArrowFunctionWithoutReflection(Expr\ArrowFunction $arrowFunction, ?array $callableParameters): self
{
$variableTypes = $this->variableTypes;
$mixed = new MixedType();
$parameterVariables = [];
+ $parameterVariableExpressions = [];
foreach ($arrowFunction->params as $i => $parameter) {
if ($parameter->type === null) {
$parameterType = $mixed;
@@ -3093,11 +3868,12 @@ private function enterArrowFunctionWithoutReflection(Expr\ArrowFunction $arrowFu
}
if (!$parameter->var instanceof Variable || !is_string($parameter->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$variableTypes[$parameter->var->name] = VariableTypeHolder::createYes($parameterType);
$parameterVariables[] = $parameter->var->name;
+ $parameterVariableExpressions[] = $parameter->var;
}
if ($arrowFunction->static) {
@@ -3159,7 +3935,7 @@ private function enterArrowFunctionWithoutReflection(Expr\ArrowFunction $arrowFu
}
}
- return $this->scopeFactory->create(
+ $scope = $this->scopeFactory->create(
$this->context,
$this->isDeclareStrictTypes(),
$this->constantTypes,
@@ -3175,8 +3951,14 @@ private function enterArrowFunctionWithoutReflection(Expr\ArrowFunction $arrowFu
[],
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
+
+ foreach ($parameterVariableExpressions as $expr) {
+ $scope = $scope->invalidateExpression($expr);
+ }
+
+ return $scope;
}
public function isParameterValueNullable(Node\Param $parameter): bool
@@ -3190,23 +3972,28 @@ public function isParameterValueNullable(Node\Param $parameter): bool
/**
* @api
- * @param \PhpParser\Node\Name|\PhpParser\Node\Identifier|\PhpParser\Node\NullableType|\PhpParser\Node\UnionType|null $type
- * @param bool $isNullable
- * @param bool $isVariadic
- * @return Type
+ * @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)
+ $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 new ArrayType(new IntegerType(), $this->getFunctionType(
$type,
false,
- false
+ false,
));
}
@@ -3214,7 +4001,7 @@ public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type
$className = (string) $type;
$lowercasedClassName = strtolower($className);
if ($lowercasedClassName === 'parent') {
- if ($this->isInClass() && $this->getClassReflection()->getParentClass() !== false) {
+ if ($this->isInClass() && $this->getClassReflection()->getParentClass() !== null) {
return new ObjectType($this->getClassReflection()->getParentClass()->getName());
}
@@ -3222,7 +4009,7 @@ public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type
}
}
- return ParserNodeTypeToPHPStanType::resolve($type, $this->isInClass() ? $this->getClassReflection()->getName() : null);
+ return ParserNodeTypeToPHPStanType::resolve($type, $this->isInClass() ? $this->getClassReflection() : null);
}
public function enterForeach(Expr $iteratee, string $valueName, ?string $keyName): self
@@ -3250,15 +4037,11 @@ public function enterForeachKey(Expr $iteratee, string $keyName): self
}
/**
- * @param \PhpParser\Node\Name[] $classes
- * @param string|null $variableName
- * @return self
+ * @param Node\Name[] $classes
*/
public function enterCatch(array $classes, ?string $variableName): self
{
- $type = TypeCombinator::union(...array_map(static function (\PhpParser\Node\Name $class): ObjectType {
- return new ObjectType((string) $class);
- }, $classes));
+ $type = TypeCombinator::union(...array_map(static fn (Node\Name $class): ObjectType => new ObjectType((string) $class), $classes));
return $this->enterCatchType($type, $variableName);
}
@@ -3271,7 +4054,7 @@ public function enterCatchType(Type $catchType, ?string $variableName): self
return $this->assignVariable(
$variableName,
- TypeCombinator::intersect($catchType, new ObjectType(\Throwable::class))
+ TypeCombinator::intersect($catchType, new ObjectType(Throwable::class)),
);
}
@@ -3297,7 +4080,7 @@ public function enterExpressionAssign(Expr $expr): self
$this->nativeExpressionTypes,
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -3323,7 +4106,7 @@ public function exitExpressionAssign(Expr $expr): self
$this->nativeExpressionTypes,
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -3339,7 +4122,7 @@ public function assignVariable(string $variableName, Type $type, ?TrinaryLogic $
if ($certainty === null) {
$certainty = TrinaryLogic::createYes();
} elseif ($certainty->no()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$variableTypes = $this->getVariableTypes();
$variableTypes[$variableName] = new VariableTypeHolder($type, $certainty);
@@ -3350,7 +4133,7 @@ public function assignVariable(string $variableName, Type $type, ?TrinaryLogic $
$variableString = $this->printer->prettyPrintExpr(new Variable($variableName));
$moreSpecificTypeHolders = $this->moreSpecificTypes;
foreach (array_keys($moreSpecificTypeHolders) as $key) {
- $matches = \Nette\Utils\Strings::matchAll((string) $key, '#\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*#');
+ $matches = Strings::matchAll((string) $key, '#\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*#');
if ($matches === []) {
continue;
@@ -3399,7 +4182,7 @@ public function assignVariable(string $variableName, Type $type, ?TrinaryLogic $
$nativeTypes,
$this->inFunctionCallsStack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -3435,34 +4218,17 @@ public function unsetExpression(Expr $expr): self
$nativeTypes,
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
} elseif ($expr instanceof Expr\ArrayDimFetch && $expr->dim !== null) {
- $varType = $this->getType($expr->var);
- $constantArrays = TypeUtils::getConstantArrays($varType);
- if (count($constantArrays) > 0) {
- $unsetArrays = [];
- $dimType = $this->getType($expr->dim);
- foreach ($constantArrays as $constantArray) {
- $unsetArrays[] = $constantArray->unsetOffset($dimType);
- }
- return $this->specifyExpressionType(
- $expr->var,
- TypeCombinator::union(...$unsetArrays)
- );
- }
-
- $args = [new Node\Arg($expr->var)];
-
- $arrays = TypeUtils::getArrays($varType);
- $scope = $this;
- if (count($arrays) > 0) {
- $scope = $scope->specifyExpressionType($expr->var, TypeCombinator::union(...$arrays));
- }
-
- return $scope->invalidateExpression($expr->var)
- ->invalidateExpression(new FuncCall(new Name\FullyQualified('count'), $args))
- ->invalidateExpression(new FuncCall(new Name('count'), $args));
+ return $this->specifyExpressionType(
+ $expr->var,
+ $this->getType($expr->var)->unsetOffset($this->getType($expr->dim)),
+ )->invalidateExpression(
+ new FuncCall(new FullyQualified('count'), [new Arg($expr->var)]),
+ )->invalidateExpression(
+ new FuncCall(new FullyQualified('sizeof'), [new Arg($expr->var)]),
+ );
}
return $this;
@@ -3495,7 +4261,7 @@ public function specifyExpressionType(Expr $expr, Type $type, ?Type $nativeType
$this->nativeExpressionTypes,
$this->inFunctionCallsStack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -3533,7 +4299,7 @@ public function specifyExpressionType(Expr $expr, Type $type, ?Type $nativeType
$nativeTypes,
$this->inFunctionCallsStack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
} elseif ($expr instanceof Expr\ArrayDimFetch && $expr->dim !== null) {
$constantArrays = TypeUtils::getConstantArrays($this->getType($expr->var));
@@ -3545,7 +4311,7 @@ public function specifyExpressionType(Expr $expr, Type $type, ?Type $nativeType
}
$scope = $this->specifyExpressionType(
$expr->var,
- TypeCombinator::union(...$setArrays)
+ TypeCombinator::union(...$setArrays),
);
}
}
@@ -3569,7 +4335,10 @@ public function specifyExpressionType(Expr $expr, Type $type, ?Type $nativeType
public function assignExpression(Expr $expr, Type $type): self
{
$scope = $this;
- if ($expr instanceof PropertyFetch || $expr instanceof Expr\StaticPropertyFetch) {
+ if ($expr instanceof PropertyFetch) {
+ $scope = $this->invalidateExpression($expr)
+ ->invalidateMethodsOnExpression($expr->var);
+ } elseif ($expr instanceof Expr\StaticPropertyFetch) {
$scope = $this->invalidateExpression($expr);
}
@@ -3579,31 +4348,104 @@ public function assignExpression(Expr $expr, Type $type): self
public function invalidateExpression(Expr $expressionToInvalidate, bool $requireMoreCharacters = false): self
{
$exprStringToInvalidate = $this->getNodeKey($expressionToInvalidate);
+ $expressionToInvalidateClass = get_class($expressionToInvalidate);
$moreSpecificTypeHolders = $this->moreSpecificTypes;
$nativeExpressionTypes = $this->nativeExpressionTypes;
$invalidated = false;
+ $nodeFinder = new NodeFinder();
foreach (array_keys($moreSpecificTypeHolders) as $exprString) {
$exprString = (string) $exprString;
- if (Strings::startsWith($exprString, $exprStringToInvalidate)) {
- if ($exprString === $exprStringToInvalidate && $requireMoreCharacters) {
- continue;
+
+ try {
+ $expr = $this->parser->parseString('expr;
+ if ($exprExpr instanceof PropertyFetch) {
+ $propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($exprExpr, $this);
+ if ($propertyReflection !== null) {
+ $nativePropertyReflection = $propertyReflection->getNativeReflection();
+ if ($nativePropertyReflection !== null && $nativePropertyReflection->isReadOnly()) {
+ continue;
+ }
}
- $nextLetter = substr($exprString, strlen($exprStringToInvalidate), 1);
- if (Strings::match($nextLetter, '#[a-zA-Z_0-9\x7f-\xff]#') === null) {
- unset($moreSpecificTypeHolders[$exprString]);
- unset($nativeExpressionTypes[$exprString]);
- $invalidated = true;
- continue;
+ }
+
+ $found = $nodeFinder->findFirst([$exprExpr], function (Node $node) use ($expressionToInvalidateClass, $exprStringToInvalidate): bool {
+ if (!$node instanceof $expressionToInvalidateClass) {
+ return false;
}
+
+ return $this->getNodeKey($node) === $exprStringToInvalidate;
+ });
+ if ($found === null) {
+ continue;
}
- $matches = \Nette\Utils\Strings::matchAll($exprString, '#\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*#');
- if ($matches === []) {
+
+ if ($requireMoreCharacters && $exprString === $exprStringToInvalidate) {
continue;
}
- $matches = array_column($matches, 0);
+ unset($moreSpecificTypeHolders[$exprString]);
+ unset($nativeExpressionTypes[$exprString]);
+ $invalidated = true;
+ }
+
+ if (!$invalidated) {
+ return $this;
+ }
+
+ return $this->scopeFactory->create(
+ $this->context,
+ $this->isDeclareStrictTypes(),
+ $this->constantTypes,
+ $this->getFunction(),
+ $this->getNamespace(),
+ $this->getVariableTypes(),
+ $moreSpecificTypeHolders,
+ $this->conditionalExpressions,
+ $this->inClosureBindScopeClass,
+ $this->anonymousFunctionReflection,
+ $this->inFirstLevelStatement,
+ $this->currentlyAssignedExpressions,
+ $nativeExpressionTypes,
+ [],
+ $this->afterExtractCall,
+ $this->parentScope,
+ );
+ }
+
+ public function invalidateMethodsOnExpression(Expr $expressionToInvalidate): self
+ {
+ $exprStringToInvalidate = $this->getNodeKey($expressionToInvalidate);
+ $moreSpecificTypeHolders = $this->moreSpecificTypes;
+ $nativeExpressionTypes = $this->nativeExpressionTypes;
+ $invalidated = false;
+ $nodeFinder = new NodeFinder();
+ foreach (array_keys($moreSpecificTypeHolders) as $exprString) {
+ $exprString = (string) $exprString;
+
+ try {
+ $expr = $this->parser->parseString('findFirst([$expr->expr], function (Node $node) use ($exprStringToInvalidate): bool {
+ if (!$node instanceof MethodCall) {
+ return false;
+ }
- if (!in_array($exprStringToInvalidate, $matches, true)) {
+ return $this->getNodeKey($node->var) === $exprStringToInvalidate;
+ });
+ if ($found === null) {
continue;
}
@@ -3632,7 +4474,7 @@ public function invalidateExpression(Expr $expressionToInvalidate, bool $require
$nativeExpressionTypes,
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -3650,7 +4492,7 @@ public function removeTypeFromExpression(Expr $expr, Type $typeToRemove): self
}
$scope = $this->specifyExpressionType(
$expr,
- $typeAfterRemove
+ $typeAfterRemove,
);
if ($expr instanceof Variable && is_string($expr->name)) {
$scope->nativeExpressionTypes[sprintf('$%s', $expr->name)] = TypeCombinator::remove($this->getNativeType($expr), $typeToRemove);
@@ -3661,24 +4503,38 @@ public function removeTypeFromExpression(Expr $expr, Type $typeToRemove): self
/**
* @api
- * @param \PhpParser\Node\Expr $expr
- * @return \PHPStan\Analyser\MutatingScope
+ * @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());
- return $this->filterBySpecifiedTypes($specifiedTypes);
+ $scope = $this->filterBySpecifiedTypes($specifiedTypes);
+ $this->truthyScopes[$exprString] = $scope;
+
+ return $scope;
}
/**
* @api
- * @param \PhpParser\Node\Expr $expr
- * @return \PHPStan\Analyser\MutatingScope
+ * @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());
- return $this->filterBySpecifiedTypes($specifiedTypes);
+ $scope = $this->filterBySpecifiedTypes($specifiedTypes);
+ $this->falseyScopes[$exprString] = $scope;
+
+ return $scope;
}
public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
@@ -3702,6 +4558,7 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
}
usort($typeSpecifications, static function (array $a, array $b): int {
+ // @phpstan-ignore-next-line
$length = strlen((string) $a['exprString']) - strlen((string) $b['exprString']);
if ($length !== 0) {
return $length;
@@ -3733,7 +4590,8 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
|| !is_string($expr->name)
|| $specifiedTypes->shouldOverwrite()
) {
- $match = \Nette\Utils\Strings::match((string) $typeSpecification['exprString'], '#^\$([a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*)#');
+ // @phpstan-ignore-next-line
+ $match = Strings::match((string) $typeSpecification['exprString'], '#^\$([a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*)#');
if ($match !== null) {
$skipVariables[$match[1]] = true;
}
@@ -3814,7 +4672,6 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
/**
* @param array $newConditionalExpressionHolders
- * @return self
*/
public function changeConditionalExpressions(array $newConditionalExpressionHolders): self
{
@@ -3834,14 +4691,12 @@ public function changeConditionalExpressions(array $newConditionalExpressionHold
$this->nativeExpressionTypes,
$this->inFunctionCallsStack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
/**
- * @param string $exprString
* @param ConditionalExpressionHolder[] $conditionalExpressionHolders
- * @return self
*/
public function addConditionalExpressions(string $exprString, array $conditionalExpressionHolders): self
{
@@ -3863,7 +4718,7 @@ public function addConditionalExpressions(string $exprString, array $conditional
$this->nativeExpressionTypes,
$this->inFunctionCallsStack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -3885,7 +4740,7 @@ public function exitFirstLevelStatements(): self
$this->nativeExpressionTypes,
$this->inFunctionCallsStack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -3898,7 +4753,6 @@ public function isInFirstLevelStatement(): bool
/**
* @phpcsSuppress SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod
* @param Type[] $types
- * @return self
*/
private function addMoreSpecificTypes(array $types): self
{
@@ -3923,7 +4777,7 @@ private function addMoreSpecificTypes(array $types): self
$this->nativeExpressionTypes,
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -3933,16 +4787,10 @@ public function mergeWith(?self $otherScope): self
return $this;
}
- $variableHolderToType = static function (VariableTypeHolder $holder): Type {
- return $holder->getType();
- };
- $typeToVariableHolder = static function (Type $type): VariableTypeHolder {
- return new VariableTypeHolder($type, TrinaryLogic::createYes());
- };
+ $variableHolderToType = static fn (VariableTypeHolder $holder): Type => $holder->getType();
+ $typeToVariableHolder = static fn (Type $type): VariableTypeHolder => new VariableTypeHolder($type, TrinaryLogic::createYes());
- $filterVariableHolders = static function (VariableTypeHolder $holder): bool {
- return $holder->getCertainty()->yes();
- };
+ $filterVariableHolders = static fn (VariableTypeHolder $holder): bool => $holder->getCertainty()->yes();
$ourVariableTypes = $this->getVariableTypes();
$theirVariableTypes = $otherScope->getVariableTypes();
@@ -3970,20 +4818,20 @@ public function mergeWith(?self $otherScope): self
$conditionalExpressions,
$ourVariableTypes,
$theirVariableTypes,
- $mergedVariableHolders
+ $mergedVariableHolders,
);
$conditionalExpressions = $this->createConditionalExpressions(
$conditionalExpressions,
$theirVariableTypes,
$ourVariableTypes,
- $mergedVariableHolders
+ $mergedVariableHolders,
);
return $this->scopeFactory->create(
$this->context,
$this->isDeclareStrictTypes(),
array_map($variableHolderToType, array_filter($this->mergeVariableHolders(
array_map($typeToVariableHolder, $this->constantTypes),
- array_map($typeToVariableHolder, $otherScope->constantTypes)
+ array_map($typeToVariableHolder, $otherScope->constantTypes),
), $filterVariableHolders)),
$this->getFunction(),
$this->getNamespace(),
@@ -3996,11 +4844,11 @@ public function mergeWith(?self $otherScope): self
[],
array_map($variableHolderToType, array_filter($this->mergeVariableHolders(
array_map($typeToVariableHolder, $this->nativeExpressionTypes),
- array_map($typeToVariableHolder, $otherScope->nativeExpressionTypes)
+ array_map($typeToVariableHolder, $otherScope->nativeExpressionTypes),
), $filterVariableHolders)),
[],
$this->afterExtractCall && $otherScope->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -4040,7 +4888,7 @@ private function createConditionalExpressions(
array $conditionalExpressions,
array $variableTypes,
array $theirVariableTypes,
- array $mergedVariableHolders
+ array $mergedVariableHolders,
): array
{
$newVariableTypes = $variableTypes;
@@ -4136,15 +4984,9 @@ private function mergeVariableHolders(array $ourVariableTypeHolders, array $thei
public function processFinallyScope(self $finallyScope, self $originalFinallyScope): self
{
- $variableHolderToType = static function (VariableTypeHolder $holder): Type {
- return $holder->getType();
- };
- $typeToVariableHolder = static function (Type $type): VariableTypeHolder {
- return new VariableTypeHolder($type, TrinaryLogic::createYes());
- };
- $filterVariableHolders = static function (VariableTypeHolder $holder): bool {
- return $holder->getCertainty()->yes();
- };
+ $variableHolderToType = static fn (VariableTypeHolder $holder): Type => $holder->getType();
+ $typeToVariableHolder = static fn (Type $type): VariableTypeHolder => new VariableTypeHolder($type, TrinaryLogic::createYes());
+ $filterVariableHolders = static fn (VariableTypeHolder $holder): bool => $holder->getCertainty()->yes();
return $this->scopeFactory->create(
$this->context,
@@ -4152,19 +4994,19 @@ public function processFinallyScope(self $finallyScope, self $originalFinallySco
array_map($variableHolderToType, array_filter($this->processFinallyScopeVariableTypeHolders(
array_map($typeToVariableHolder, $this->constantTypes),
array_map($typeToVariableHolder, $finallyScope->constantTypes),
- array_map($typeToVariableHolder, $originalFinallyScope->constantTypes)
+ array_map($typeToVariableHolder, $originalFinallyScope->constantTypes),
), $filterVariableHolders)),
$this->getFunction(),
$this->getNamespace(),
$this->processFinallyScopeVariableTypeHolders(
$this->getVariableTypes(),
$finallyScope->getVariableTypes(),
- $originalFinallyScope->getVariableTypes()
+ $originalFinallyScope->getVariableTypes(),
),
$this->processFinallyScopeVariableTypeHolders(
$this->moreSpecificTypes,
$finallyScope->moreSpecificTypes,
- $originalFinallyScope->moreSpecificTypes
+ $originalFinallyScope->moreSpecificTypes,
),
$this->conditionalExpressions,
$this->inClosureBindScopeClass,
@@ -4174,11 +5016,11 @@ public function processFinallyScope(self $finallyScope, self $originalFinallySco
array_map($variableHolderToType, array_filter($this->processFinallyScopeVariableTypeHolders(
array_map($typeToVariableHolder, $this->nativeExpressionTypes),
array_map($typeToVariableHolder, $finallyScope->nativeExpressionTypes),
- array_map($typeToVariableHolder, $originalFinallyScope->nativeExpressionTypes)
+ array_map($typeToVariableHolder, $originalFinallyScope->nativeExpressionTypes),
), $filterVariableHolders)),
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -4191,7 +5033,7 @@ public function processFinallyScope(self $finallyScope, self $originalFinallySco
private function processFinallyScopeVariableTypeHolders(
array $ourVariableTypeHolders,
array $finallyVariableTypeHolders,
- array $originalVariableTypeHolders
+ array $originalVariableTypeHolders,
): array
{
foreach ($finallyVariableTypeHolders as $name => $variableTypeHolder) {
@@ -4214,15 +5056,12 @@ private function processFinallyScopeVariableTypeHolders(
}
/**
- * @param self $closureScope
- * @param self|null $prevScope
* @param Expr\ClosureUse[] $byRefUses
- * @return self
*/
public function processClosureScope(
self $closureScope,
?self $prevScope,
- array $byRefUses
+ array $byRefUses,
): self
{
$nativeExpressionTypes = $this->nativeExpressionTypes;
@@ -4233,7 +5072,7 @@ public function processClosureScope(
foreach ($byRefUses as $use) {
if (!is_string($use->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$variableName = $use->var->name;
@@ -4274,7 +5113,7 @@ public function processClosureScope(
$nativeExpressionTypes,
$this->inFunctionCallsStack,
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -4291,7 +5130,7 @@ public function processAlwaysIterableForeachScopeWithoutPollute(self $finalScope
$variableTypeHolders[$name] = new VariableTypeHolder(
$variableTypeHolder->getType(),
- $variableTypeHolder->getCertainty()->and($variableTypeHolders[$name]->getCertainty())
+ $variableTypeHolder->getCertainty()->and($variableTypeHolders[$name]->getCertainty()),
);
}
@@ -4304,7 +5143,7 @@ public function processAlwaysIterableForeachScopeWithoutPollute(self $finalScope
$moreSpecificTypes[$exprString] = new VariableTypeHolder(
$variableTypeHolder->getType(),
- $variableTypeHolder->getCertainty()->and($moreSpecificTypes[$exprString]->getCertainty())
+ $variableTypeHolder->getCertainty()->and($moreSpecificTypes[$exprString]->getCertainty()),
);
}
@@ -4324,7 +5163,7 @@ public function processAlwaysIterableForeachScopeWithoutPollute(self $finalScope
$nativeTypes,
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -4332,26 +5171,20 @@ public function generalizeWith(self $otherScope): self
{
$variableTypeHolders = $this->generalizeVariableTypeHolders(
$this->getVariableTypes(),
- $otherScope->getVariableTypes()
+ $otherScope->getVariableTypes(),
);
$moreSpecificTypes = $this->generalizeVariableTypeHolders(
$this->moreSpecificTypes,
- $otherScope->moreSpecificTypes
+ $otherScope->moreSpecificTypes,
);
- $variableHolderToType = static function (VariableTypeHolder $holder): Type {
- return $holder->getType();
- };
- $typeToVariableHolder = static function (Type $type): VariableTypeHolder {
- return new VariableTypeHolder($type, TrinaryLogic::createYes());
- };
- $filterVariableHolders = static function (VariableTypeHolder $holder): bool {
- return $holder->getCertainty()->yes();
- };
+ $variableHolderToType = static fn (VariableTypeHolder $holder): Type => $holder->getType();
+ $typeToVariableHolder = static fn (Type $type): VariableTypeHolder => new VariableTypeHolder($type, TrinaryLogic::createYes());
+ $filterVariableHolders = static fn (VariableTypeHolder $holder): bool => $holder->getCertainty()->yes();
$nativeTypes = array_map($variableHolderToType, array_filter($this->generalizeVariableTypeHolders(
array_map($typeToVariableHolder, $this->nativeExpressionTypes),
- array_map($typeToVariableHolder, $otherScope->nativeExpressionTypes)
+ array_map($typeToVariableHolder, $otherScope->nativeExpressionTypes),
), $filterVariableHolders));
return $this->scopeFactory->create(
@@ -4359,7 +5192,7 @@ public function generalizeWith(self $otherScope): self
$this->isDeclareStrictTypes(),
array_map($variableHolderToType, array_filter($this->generalizeVariableTypeHolders(
array_map($typeToVariableHolder, $this->constantTypes),
- array_map($typeToVariableHolder, $otherScope->constantTypes)
+ array_map($typeToVariableHolder, $otherScope->constantTypes),
), $filterVariableHolders)),
$this->getFunction(),
$this->getNamespace(),
@@ -4373,7 +5206,7 @@ public function generalizeWith(self $otherScope): self
$nativeTypes,
[],
$this->afterExtractCall,
- $this->parentScope
+ $this->parentScope,
);
}
@@ -4384,7 +5217,7 @@ public function generalizeWith(self $otherScope): self
*/
private function generalizeVariableTypeHolders(
array $variableTypeHolders,
- array $otherVariableTypeHolders
+ array $otherVariableTypeHolders,
): array
{
foreach ($variableTypeHolders as $name => $variableTypeHolder) {
@@ -4394,7 +5227,7 @@ private function generalizeVariableTypeHolders(
$variableTypeHolders[$name] = new VariableTypeHolder(
self::generalizeType($variableTypeHolder->getType(), $otherVariableTypeHolders[$name]->getType()),
- $variableTypeHolder->getCertainty()
+ $variableTypeHolder->getCertainty(),
);
}
@@ -4413,6 +5246,7 @@ private static function generalizeType(Type $a, Type $b): Type
$constantStrings = ['a' => [], 'b' => []];
$constantArrays = ['a' => [], 'b' => []];
$generalArrays = ['a' => [], 'b' => []];
+ $integerRanges = ['a' => [], 'b' => []];
$otherTypes = [];
foreach ([
@@ -4444,6 +5278,10 @@ private static function generalizeType(Type $a, Type $b): Type
$generalArrays[$key][] = $type;
continue;
}
+ if ($type instanceof IntegerRangeType) {
+ $integerRanges[$key][] = $type;
+ continue;
+ }
$otherTypes[] = $type;
}
@@ -4451,15 +5289,16 @@ private static function generalizeType(Type $a, Type $b): Type
$resultTypes = [];
foreach ([
- $constantIntegers,
$constantFloats,
$constantBooleans,
$constantStrings,
] as $constantTypes) {
if (count($constantTypes['a']) === 0) {
+ if (count($constantTypes['b']) > 0) {
+ $resultTypes[] = TypeCombinator::union(...$constantTypes['b']);
+ }
continue;
- }
- if (count($constantTypes['b']) === 0) {
+ } elseif (count($constantTypes['b']) === 0) {
$resultTypes[] = TypeCombinator::union(...$constantTypes['a']);
continue;
}
@@ -4471,7 +5310,7 @@ private static function generalizeType(Type $a, Type $b): Type
continue;
}
- $resultTypes[] = TypeUtils::generalizeType($constantTypes['a'][0]);
+ $resultTypes[] = TypeCombinator::union(...$constantTypes['a'], ...$constantTypes['b'])->generalize(GeneralizePrecision::moreSpecific());
}
if (count($constantArrays['a']) > 0) {
@@ -4487,8 +5326,8 @@ private static function generalizeType(Type $a, Type $b): Type
$keyType,
self::generalizeType(
$constantArraysA->getOffsetValueType($keyType),
- $constantArraysB->getOffsetValueType($keyType)
- )
+ $constantArraysB->getOffsetValueType($keyType),
+ ),
);
}
@@ -4496,10 +5335,12 @@ private static function generalizeType(Type $a, Type $b): Type
} else {
$resultTypes[] = new ArrayType(
TypeCombinator::union(self::generalizeType($constantArraysA->getIterableKeyType(), $constantArraysB->getIterableKeyType())),
- TypeCombinator::union(self::generalizeType($constantArraysA->getIterableValueType(), $constantArraysB->getIterableValueType()))
+ TypeCombinator::union(self::generalizeType($constantArraysA->getIterableValueType(), $constantArraysB->getIterableValueType())),
);
}
}
+ } elseif (count($constantArrays['b']) > 0) {
+ $resultTypes[] = TypeCombinator::union(...$constantArrays['b']);
}
if (count($generalArrays['a']) > 0) {
@@ -4532,9 +5373,142 @@ private static function generalizeType(Type $a, Type $b): Type
$resultTypes[] = new ArrayType(
TypeCombinator::union(self::generalizeType($generalArraysA->getIterableKeyType(), $generalArraysB->getIterableKeyType())),
- TypeCombinator::union(self::generalizeType($aValueType, $bValueType))
+ TypeCombinator::union(self::generalizeType($aValueType, $bValueType)),
);
}
+ } 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']);
}
return TypeCombinator::union(...$resultTypes, ...$otherTypes);
@@ -4571,13 +5545,11 @@ public function equals(self $otherScope): bool
return false;
}
- $typeToVariableHolder = static function (Type $type): VariableTypeHolder {
- return new VariableTypeHolder($type, TrinaryLogic::createYes());
- };
+ $typeToVariableHolder = static fn (Type $type): VariableTypeHolder => new VariableTypeHolder($type, TrinaryLogic::createYes());
$nativeExpressionTypesResult = $this->compareVariableTypeHolders(
array_map($typeToVariableHolder, $this->nativeExpressionTypes),
- array_map($typeToVariableHolder, $otherScope->nativeExpressionTypes)
+ array_map($typeToVariableHolder, $otherScope->nativeExpressionTypes),
);
if (!$nativeExpressionTypesResult) {
@@ -4586,14 +5558,13 @@ public function equals(self $otherScope): bool
return $this->compareVariableTypeHolders(
array_map($typeToVariableHolder, $this->constantTypes),
- array_map($typeToVariableHolder, $otherScope->constantTypes)
+ array_map($typeToVariableHolder, $otherScope->constantTypes),
);
}
/**
* @param VariableTypeHolder[] $variableTypeHolders
* @param VariableTypeHolder[] $otherVariableTypeHolders
- * @return bool
*/
private function compareVariableTypeHolders(array $variableTypeHolders, array $otherVariableTypeHolders): bool
{
@@ -4686,7 +5657,7 @@ public function debug(): array
$key = sprintf(
'%s-specified (%s)',
$exprString,
- $typeHolder->getCertainty()->describe()
+ $typeHolder->getCertainty()->describe(),
);
$descriptions[$key] = $typeHolder->getType()->describe(VerbosityLevel::precise());
}
@@ -4724,7 +5695,7 @@ private function exactInstantiation(New_ $node, string $className): ?Type
$methodCall = new Expr\StaticCall(
new Name($resolvedClassName),
new Node\Identifier($constructorMethod->getName()),
- $node->args
+ $node->getArgs(),
);
foreach ($this->dynamicReturnTypeExtensionRegistry->getDynamicStaticMethodReturnTypeExtensionsForClass($classReflection->getName()) as $dynamicStaticMethodReturnTypeExtension) {
@@ -4732,7 +5703,16 @@ private function exactInstantiation(New_ $node, string $className): ?Type
continue;
}
- $resolvedTypes[] = $dynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall($constructorMethod, $methodCall, $this);
+ $resolvedType = $dynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall(
+ $constructorMethod,
+ $methodCall,
+ $this,
+ );
+ if ($resolvedType === null) {
+ continue;
+ }
+
+ $resolvedTypes[] = $resolvedType;
}
if (count($resolvedTypes) > 0) {
@@ -4785,19 +5765,46 @@ private function exactInstantiation(New_ $node, string $className): ?Type
if ($constructorMethod instanceof DummyConstructorReflection || $constructorMethod->getDeclaringClass()->getName() !== $classReflection->getName()) {
return new GenericObjectType(
$resolvedClassName,
- $classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds())
+ $classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
);
}
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
$this,
- $methodCall->args,
- $constructorMethod->getVariants()
+ $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,
- $classReflection->typeMapToList($parametersAcceptor->getResolvedTemplateTypeMap())
+ $list,
);
}
@@ -4807,12 +5814,12 @@ private function getTypeToInstantiateForNew(Type $type): Type
if ($type instanceof ConstantStringType) {
return new ObjectType($type->getValue());
}
- if ($type instanceof TypeWithClassName) {
- return $type;
- }
if ($type instanceof GenericClassStringType) {
return $type->getGenericType();
}
+ if ((new ObjectWithoutClassType())->isSuperTypeOf($type)->yes()) {
+ return $type;
+ }
return null;
};
@@ -4821,11 +5828,7 @@ private function getTypeToInstantiateForNew(Type $type): Type
foreach ($type->getTypes() as $innerType) {
$decidedType = $decideType($innerType);
if ($decidedType === null) {
- if ($this->objectFromNewClass) {
- return new ObjectWithoutClassType();
- }
-
- return new MixedType(false, new StringType());
+ return new ObjectWithoutClassType();
}
$types[] = $decidedType;
@@ -4836,11 +5839,7 @@ private function getTypeToInstantiateForNew(Type $type): Type
$decidedType = $decideType($type);
if ($decidedType === null) {
- if ($this->objectFromNewClass) {
- return new ObjectWithoutClassType();
- }
-
- return new MixedType(false, new StringType());
+ return new ObjectWithoutClassType();
}
return $decidedType;
@@ -4872,10 +5871,7 @@ public function getMethodReflection(Type $typeWithMethod, string $methodName): ?
}
/**
- * @param \PHPStan\Type\Type $typeWithMethod
- * @param string $methodName
- * @param MethodCall|\PhpParser\Node\Expr\StaticCall $methodCall
- * @return \PHPStan\Type\Type|null
+ * @param MethodCall|Node\Expr\StaticCall $methodCall
*/
private function methodCallReturnType(Type $typeWithMethod, string $methodName, Expr $methodCall): ?Type
{
@@ -4892,7 +5888,12 @@ private function methodCallReturnType(Type $typeWithMethod, string $methodName,
continue;
}
- $resolvedTypes[] = $dynamicMethodReturnTypeExtension->getTypeFromMethodCall($methodReflection, $methodCall, $this);
+ $resolvedType = $dynamicMethodReturnTypeExtension->getTypeFromMethodCall($methodReflection, $methodCall, $this);
+ if ($resolvedType === null) {
+ continue;
+ }
+
+ $resolvedTypes[] = $resolvedType;
}
} else {
foreach ($this->dynamicReturnTypeExtensionRegistry->getDynamicStaticMethodReturnTypeExtensionsForClass($className) as $dynamicStaticMethodReturnTypeExtension) {
@@ -4900,7 +5901,16 @@ private function methodCallReturnType(Type $typeWithMethod, string $methodName,
continue;
}
- $resolvedTypes[] = $dynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall($methodReflection, $methodCall, $this);
+ $resolvedType = $dynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall(
+ $methodReflection,
+ $methodCall,
+ $this,
+ );
+ if ($resolvedType === null) {
+ continue;
+ }
+
+ $resolvedTypes[] = $resolvedType;
}
}
}
@@ -4911,8 +5921,8 @@ private function methodCallReturnType(Type $typeWithMethod, string $methodName,
return ParametersAcceptorSelector::selectFromArgs(
$this,
- $methodCall->args,
- $methodReflection->getVariants()
+ $methodCall->getArgs(),
+ $methodReflection->getVariants(),
)->getReturnType();
}
@@ -4941,10 +5951,7 @@ public function getPropertyReflection(Type $typeWithProperty, string $propertyNa
}
/**
- * @param \PHPStan\Type\Type $fetchedOnType
- * @param string $propertyName
- * @param PropertyFetch|\PhpParser\Node\Expr\StaticPropertyFetch $propertyFetch
- * @return \PHPStan\Type\Type|null
+ * @param PropertyFetch|Node\Expr\StaticPropertyFetch $propertyFetch
*/
private function propertyFetchType(Type $fetchedOnType, string $propertyName, Expr $propertyFetch): ?Type
{
@@ -4960,4 +5967,188 @@ private function propertyFetchType(Type $fetchedOnType, string $propertyName, Ex
return $propertyReflection->getReadableType();
}
+ /**
+ * @param ConstantIntegerType|IntegerRangeType $range
+ * @param Node\Expr\AssignOp\Div|Node\Expr\AssignOp\Minus|Node\Expr\AssignOp\Mul|Node\Expr\AssignOp\Plus|Node\Expr\BinaryOp\Div|Node\Expr\BinaryOp\Minus|Node\Expr\BinaryOp\Mul|Node\Expr\BinaryOp\Plus $node
+ * @param IntegerRangeType|ConstantIntegerType|UnionType $operand
+ */
+ private function integerRangeMath(Type $range, Expr $node, Type $operand): Type
+ {
+ if ($range instanceof IntegerRangeType) {
+ $rangeMin = $range->getMin();
+ $rangeMax = $range->getMax();
+ } else {
+ $rangeMin = $range->getValue();
+ $rangeMax = $rangeMin;
+ }
+
+ if ($operand instanceof UnionType) {
+
+ $unionParts = [];
+
+ foreach ($operand->getTypes() as $type) {
+ if ($type instanceof IntegerRangeType || $type instanceof ConstantIntegerType) {
+ $unionParts[] = $this->integerRangeMath($range, $node, $type);
+ } else {
+ $unionParts[] = $type->toNumber();
+ }
+ }
+
+ $union = TypeCombinator::union(...$unionParts);
+ if ($operand instanceof BenevolentUnionType) {
+ return TypeUtils::toBenevolentUnion($union)->toNumber();
+ }
+
+ return $union->toNumber();
+ }
+
+ if ($node instanceof Node\Expr\BinaryOp\Plus || $node instanceof Node\Expr\AssignOp\Plus) {
+ if ($operand instanceof ConstantIntegerType) {
+ /** @var int|float|null $min */
+ $min = $rangeMin !== null ? $rangeMin + $operand->getValue() : null;
+
+ /** @var int|float|null $max */
+ $max = $rangeMax !== null ? $rangeMax + $operand->getValue() : null;
+ } else {
+ /** @var int|float|null $min */
+ $min = $rangeMin !== null && $operand->getMin() !== null ? $rangeMin + $operand->getMin() : null;
+
+ /** @var int|float|null $max */
+ $max = $rangeMax !== null && $operand->getMax() !== null ? $rangeMax + $operand->getMax() : null;
+ }
+ } elseif ($node instanceof Node\Expr\BinaryOp\Minus || $node instanceof Node\Expr\AssignOp\Minus) {
+ if ($operand instanceof ConstantIntegerType) {
+ /** @var int|float|null $min */
+ $min = $rangeMin !== null ? $rangeMin - $operand->getValue() : null;
+
+ /** @var int|float|null $max */
+ $max = $rangeMax !== null ? $rangeMax - $operand->getValue() : null;
+ } else {
+ if ($rangeMin === $rangeMax && $rangeMin !== null
+ && ($operand->getMin() === null || $operand->getMax() === null)) {
+ $min = null;
+ $max = $rangeMin;
+ } else {
+ if ($operand->getMin() === null) {
+ $min = null;
+ } elseif ($rangeMin !== null) {
+ if ($operand->getMax() !== null) {
+ /** @var int|float $min */
+ $min = $rangeMin - $operand->getMax();
+ } else {
+ /** @var int|float $min */
+ $min = $rangeMin - $operand->getMin();
+ }
+ } else {
+ $min = null;
+ }
+
+ if ($operand->getMax() === null) {
+ $min = null;
+ $max = null;
+ } elseif ($rangeMax !== null) {
+ if ($rangeMin !== null && $operand->getMin() === null) {
+ /** @var int|float $min */
+ $min = $rangeMin - $operand->getMax();
+ $max = null;
+ } elseif ($operand->getMin() !== null) {
+ /** @var int|float $max */
+ $max = $rangeMax - $operand->getMin();
+ } else {
+ $max = null;
+ }
+ } else {
+ $max = null;
+ }
+
+ if ($min !== null && $max !== null && $min > $max) {
+ [$min, $max] = [$max, $min];
+ }
+ }
+ }
+ } elseif ($node instanceof Node\Expr\BinaryOp\Mul || $node instanceof Node\Expr\AssignOp\Mul) {
+ if ($operand instanceof ConstantIntegerType) {
+ /** @var int|float|null $min */
+ $min = $rangeMin !== null ? $rangeMin * $operand->getValue() : null;
+
+ /** @var int|float|null $max */
+ $max = $rangeMax !== null ? $rangeMax * $operand->getValue() : null;
+ } else {
+ /** @var int|float|null $min */
+ $min = $rangeMin !== null && $operand->getMin() !== null ? $rangeMin * $operand->getMin() : null;
+
+ /** @var int|float|null $max */
+ $max = $rangeMax !== null && $operand->getMax() !== null ? $rangeMax * $operand->getMax() : null;
+ }
+
+ if ($min !== null && $max !== null && $min > $max) {
+ [$min, $max] = [$max, $min];
+ }
+
+ // invert maximas on multiplication with negative constants
+ if ((($range instanceof ConstantIntegerType && $range->getValue() < 0)
+ || ($operand instanceof ConstantIntegerType && $operand->getValue() < 0))
+ && ($min === null || $max === null)) {
+ [$min, $max] = [$max, $min];
+ }
+
+ } else {
+ if ($operand instanceof ConstantIntegerType) {
+ $min = $rangeMin !== null && $operand->getValue() !== 0 ? $rangeMin / $operand->getValue() : null;
+ $max = $rangeMax !== null && $operand->getValue() !== 0 ? $rangeMax / $operand->getValue() : null;
+ } else {
+ $min = $rangeMin !== null && $operand->getMin() !== null && $operand->getMin() !== 0 ? $rangeMin / $operand->getMin() : null;
+ $max = $rangeMax !== null && $operand->getMax() !== null && $operand->getMax() !== 0 ? $rangeMax / $operand->getMax() : null;
+ }
+
+ if ($range instanceof IntegerRangeType && $operand instanceof IntegerRangeType) {
+ if ($rangeMax === null && $operand->getMax() === null) {
+ $min = 0;
+ } elseif ($rangeMin === null && $operand->getMin() === null) {
+ $min = null;
+ $max = null;
+ }
+ }
+
+ if ($operand instanceof IntegerRangeType
+ && ($operand->getMin() === null || $operand->getMax() === null)
+ || ($rangeMin === null || $rangeMax === null)
+ || is_float($min) || is_float($max)
+ ) {
+ if (is_float($min)) {
+ $min = (int) $min;
+ }
+ if (is_float($max)) {
+ $max = (int) $max;
+ }
+
+ if ($min !== null && $max !== null && $min > $max) {
+ [$min, $max] = [$max, $min];
+ }
+
+ // invert maximas on division with negative constants
+ if ((($range instanceof ConstantIntegerType && $range->getValue() < 0)
+ || ($operand instanceof ConstantIntegerType && $operand->getValue() < 0))
+ && ($min === null || $max === null)) {
+ [$min, $max] = [$max, $min];
+ }
+
+ if ($min === null && $max === null) {
+ return new BenevolentUnionType([new IntegerType(), new FloatType()]);
+ }
+
+ return TypeCombinator::union(IntegerRangeType::fromInterval($min, $max), new FloatType());
+ }
+ }
+
+ if (is_float($min)) {
+ $min = null;
+ }
+ if (is_float($max)) {
+ $max = null;
+ }
+
+ return IntegerRangeType::fromInterval($min, $max);
+ }
+
}
diff --git a/src/Analyser/NameScope.php b/src/Analyser/NameScope.php
index 146f6954c5..683732b28c 100644
--- a/src/Analyser/NameScope.php
+++ b/src/Analyser/NameScope.php
@@ -5,43 +5,31 @@
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeScope;
use PHPStan\Type\Type;
+use function array_key_exists;
+use function array_merge;
+use function array_shift;
+use function count;
+use function explode;
+use function implode;
+use function ltrim;
+use function sprintf;
+use function strpos;
+use function strtolower;
/** @api */
class NameScope
{
- private ?string $namespace;
-
- /** @var array alias(string) => fullName(string) */
- private array $uses;
-
- private ?string $className;
-
- private ?string $functionName;
-
private TemplateTypeMap $templateTypeMap;
- /** @var array */
- private array $typeAliasesMap;
-
- private bool $bypassTypeAliases;
-
/**
* @api
- * @param string|null $namespace
* @param array $uses alias(string) => fullName(string)
- * @param string|null $className
* @param array $typeAliasesMap
*/
- public function __construct(?string $namespace, array $uses, ?string $className = null, ?string $functionName = null, ?TemplateTypeMap $templateTypeMap = null, array $typeAliasesMap = [], bool $bypassTypeAliases = false)
+ public function __construct(private ?string $namespace, private array $uses, private ?string $className = null, private ?string $functionName = null, ?TemplateTypeMap $templateTypeMap = null, private array $typeAliasesMap = [], private bool $bypassTypeAliases = false)
{
- $this->namespace = $namespace;
- $this->uses = $uses;
- $this->className = $className;
- $this->functionName = $functionName;
$this->templateTypeMap = $templateTypeMap ?? TemplateTypeMap::createEmpty();
- $this->typeAliasesMap = $typeAliasesMap;
- $this->bypassTypeAliases = $bypassTypeAliases;
}
public function getNamespace(): ?string
@@ -119,7 +107,7 @@ public function resolveTemplateTypeName(string $name): ?Type
public function withTemplateTypeMap(TemplateTypeMap $map): self
{
- if ($map->isEmpty()) {
+ if ($map->isEmpty() && $this->templateTypeMap->isEmpty()) {
return $this;
}
@@ -130,9 +118,9 @@ public function withTemplateTypeMap(TemplateTypeMap $map): self
$this->functionName,
new TemplateTypeMap(array_merge(
$this->templateTypeMap->getTypes(),
- $map->getTypes()
+ $map->getTypes(),
)),
- $this->typeAliasesMap
+ $this->typeAliasesMap,
);
}
@@ -149,7 +137,7 @@ public function unsetTemplateType(string $name): self
$this->className,
$this->functionName,
$this->templateTypeMap->unsetType($name),
- $this->typeAliasesMap
+ $this->typeAliasesMap,
);
}
@@ -170,7 +158,6 @@ public function hasTypeAlias(string $alias): bool
/**
* @param mixed[] $properties
- * @return self
*/
public static function __set_state(array $properties): self
{
@@ -180,7 +167,7 @@ public static function __set_state(array $properties): self
$properties['className'],
$properties['functionName'],
$properties['templateTypeMap'],
- $properties['typeAliasesMap']
+ $properties['typeAliasesMap'],
);
}
diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php
index eb1a1b497d..e72ffe3a90 100644
--- a/src/Analyser/NodeScopeResolver.php
+++ b/src/Analyser/NodeScopeResolver.php
@@ -2,6 +2,8 @@
namespace PHPStan\Analyser;
+use ArrayAccess;
+use Closure;
use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\Node\Expr;
@@ -47,7 +49,8 @@
use PhpParser\Node\Stmt\Unset_;
use PhpParser\Node\Stmt\While_;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass;
-use PHPStan\BetterReflection\Reflector\ClassReflector;
+use PHPStan\BetterReflection\Reflection\ReflectionEnum;
+use PHPStan\BetterReflection\Reflector\Reflector;
use PHPStan\BetterReflection\SourceLocator\Ast\Strategy\NodeToReflection;
use PHPStan\BetterReflection\SourceLocator\Located\LocatedSource;
use PHPStan\DependencyInjection\Reflection\ClassReflectionExtensionRegistryProvider;
@@ -56,6 +59,7 @@
use PHPStan\File\FileReader;
use PHPStan\Node\BooleanAndNode;
use PHPStan\Node\BooleanOrNode;
+use PHPStan\Node\BreaklessWhileLoopNode;
use PHPStan\Node\CatchWithUnthrownExceptionNode;
use PHPStan\Node\ClassConstantsNode;
use PHPStan\Node\ClassMethodsNode;
@@ -63,40 +67,50 @@
use PHPStan\Node\ClassPropertyNode;
use PHPStan\Node\ClassStatementsGatherer;
use PHPStan\Node\ClosureReturnStatementsNode;
+use PHPStan\Node\DoWhileLoopConditionNode;
use PHPStan\Node\ExecutionEndNode;
+use PHPStan\Node\Expr\GetIterableValueTypeExpr;
+use PHPStan\Node\Expr\GetOffsetValueTypeExpr;
+use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
+use PHPStan\Node\Expr\SetOffsetValueTypeExpr;
use PHPStan\Node\FinallyExitPointsNode;
+use PHPStan\Node\FunctionCallableNode;
use PHPStan\Node\FunctionReturnStatementsNode;
use PHPStan\Node\InArrowFunctionNode;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Node\InClassNode;
use PHPStan\Node\InClosureNode;
+use PHPStan\Node\InForeachNode;
use PHPStan\Node\InFunctionNode;
+use PHPStan\Node\InstantiationCallableNode;
use PHPStan\Node\LiteralArrayItem;
use PHPStan\Node\LiteralArrayNode;
use PHPStan\Node\MatchExpressionArm;
use PHPStan\Node\MatchExpressionArmCondition;
use PHPStan\Node\MatchExpressionNode;
+use PHPStan\Node\MethodCallableNode;
use PHPStan\Node\MethodReturnStatementsNode;
+use PHPStan\Node\PropertyAssignNode;
use PHPStan\Node\ReturnStatement;
+use PHPStan\Node\StaticMethodCallableNode;
use PHPStan\Node\UnreachableStatementNode;
use PHPStan\Parser\Parser;
use PHPStan\Php\PhpVersion;
use PHPStan\PhpDoc\PhpDocInheritanceResolver;
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
+use PHPStan\PhpDoc\StubPhpDocProvider;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\Native\NativeMethodReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ParametersAcceptorSelector;
-use PHPStan\Reflection\PassedByReference;
-use PHPStan\Reflection\Php\DummyParameter;
use PHPStan\Reflection\Php\PhpMethodReflection;
use PHPStan\Reflection\ReflectionProvider;
+use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
-use PHPStan\Type\CallableType;
use PHPStan\Type\ClosureType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
@@ -105,6 +119,7 @@
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\FileTypeMapper;
+use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\IntegerType;
@@ -119,8 +134,30 @@
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\TypeUtils;
+use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use PHPStan\Type\VoidType;
+use Throwable;
+use Traversable;
+use TypeError;
+use function array_fill_keys;
+use function array_filter;
+use function array_key_exists;
+use function array_map;
+use function array_merge;
+use function array_pop;
+use function array_reverse;
+use function array_slice;
+use function base64_decode;
+use function count;
+use function in_array;
+use function is_array;
+use function is_int;
+use function is_string;
+use function sprintf;
+use function strtolower;
+use function trim;
+use const PHP_VERSION_ID;
class NodeScopeResolver
{
@@ -128,98 +165,32 @@ class NodeScopeResolver
private const LOOP_SCOPE_ITERATIONS = 3;
private const GENERALIZE_AFTER_ITERATION = 1;
- private \PHPStan\Reflection\ReflectionProvider $reflectionProvider;
-
- private ClassReflector $classReflector;
-
- private ClassReflectionExtensionRegistryProvider $classReflectionExtensionRegistryProvider;
-
- private \PHPStan\Parser\Parser $parser;
-
- private \PHPStan\Type\FileTypeMapper $fileTypeMapper;
-
- private PhpVersion $phpVersion;
-
- private \PHPStan\PhpDoc\PhpDocInheritanceResolver $phpDocInheritanceResolver;
-
- private \PHPStan\File\FileHelper $fileHelper;
-
- private \PHPStan\Analyser\TypeSpecifier $typeSpecifier;
-
- private DynamicThrowTypeExtensionProvider $dynamicThrowTypeExtensionProvider;
-
- private bool $polluteScopeWithLoopInitialAssignments;
-
- private bool $polluteCatchScopeWithTryAssignments;
-
- private bool $polluteScopeWithAlwaysIterableForeach;
-
- /** @var string[][] className(string) => methods(string[]) */
- private array $earlyTerminatingMethodCalls;
-
- /** @var array */
- private array $earlyTerminatingFunctionCalls;
-
- private bool $implicitThrows;
-
- private bool $preciseExceptionTracking;
-
/** @var bool[] filePath(string) => bool(true) */
private array $analysedFiles = [];
/**
- * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider
- * @param ClassReflector $classReflector
- * @param Parser $parser
- * @param FileTypeMapper $fileTypeMapper
- * @param PhpDocInheritanceResolver $phpDocInheritanceResolver
- * @param FileHelper $fileHelper
- * @param TypeSpecifier $typeSpecifier
- * @param bool $polluteScopeWithLoopInitialAssignments
- * @param bool $polluteCatchScopeWithTryAssignments
- * @param bool $polluteScopeWithAlwaysIterableForeach
* @param string[][] $earlyTerminatingMethodCalls className(string) => methods(string[])
* @param array $earlyTerminatingFunctionCalls
- * @param bool $implicitThrows
- * @param bool $preciseExceptionTracking
*/
public function __construct(
- ReflectionProvider $reflectionProvider,
- ClassReflector $classReflector,
- ClassReflectionExtensionRegistryProvider $classReflectionExtensionRegistryProvider,
- Parser $parser,
- FileTypeMapper $fileTypeMapper,
- PhpVersion $phpVersion,
- PhpDocInheritanceResolver $phpDocInheritanceResolver,
- FileHelper $fileHelper,
- TypeSpecifier $typeSpecifier,
- DynamicThrowTypeExtensionProvider $dynamicThrowTypeExtensionProvider,
- bool $polluteScopeWithLoopInitialAssignments,
- bool $polluteCatchScopeWithTryAssignments,
- bool $polluteScopeWithAlwaysIterableForeach,
- array $earlyTerminatingMethodCalls,
- array $earlyTerminatingFunctionCalls,
- bool $implicitThrows,
- bool $preciseExceptionTracking
+ private ReflectionProvider $reflectionProvider,
+ private Reflector $reflector,
+ private ClassReflectionExtensionRegistryProvider $classReflectionExtensionRegistryProvider,
+ private Parser $parser,
+ private FileTypeMapper $fileTypeMapper,
+ private StubPhpDocProvider $stubPhpDocProvider,
+ private PhpVersion $phpVersion,
+ private PhpDocInheritanceResolver $phpDocInheritanceResolver,
+ private FileHelper $fileHelper,
+ private TypeSpecifier $typeSpecifier,
+ private DynamicThrowTypeExtensionProvider $dynamicThrowTypeExtensionProvider,
+ private bool $polluteScopeWithLoopInitialAssignments,
+ private bool $polluteScopeWithAlwaysIterableForeach,
+ private array $earlyTerminatingMethodCalls,
+ private array $earlyTerminatingFunctionCalls,
+ private bool $implicitThrows,
)
{
- $this->reflectionProvider = $reflectionProvider;
- $this->classReflector = $classReflector;
- $this->classReflectionExtensionRegistryProvider = $classReflectionExtensionRegistryProvider;
- $this->parser = $parser;
- $this->fileTypeMapper = $fileTypeMapper;
- $this->phpVersion = $phpVersion;
- $this->phpDocInheritanceResolver = $phpDocInheritanceResolver;
- $this->fileHelper = $fileHelper;
- $this->typeSpecifier = $typeSpecifier;
- $this->dynamicThrowTypeExtensionProvider = $dynamicThrowTypeExtensionProvider;
- $this->polluteScopeWithLoopInitialAssignments = $polluteScopeWithLoopInitialAssignments;
- $this->polluteCatchScopeWithTryAssignments = $polluteCatchScopeWithTryAssignments;
- $this->polluteScopeWithAlwaysIterableForeach = $polluteScopeWithAlwaysIterableForeach;
- $this->earlyTerminatingMethodCalls = $earlyTerminatingMethodCalls;
- $this->earlyTerminatingFunctionCalls = $earlyTerminatingFunctionCalls;
- $this->implicitThrows = $implicitThrows;
- $this->preciseExceptionTracking = $preciseExceptionTracking;
}
/**
@@ -233,14 +204,13 @@ public function setAnalysedFiles(array $files): void
/**
* @api
- * @param \PhpParser\Node[] $nodes
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
+ * @param Node[] $nodes
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
public function processNodes(
array $nodes,
MutatingScope $scope,
- callable $nodeCallback
+ callable $nodeCallback,
): void
{
$nodesCount = count($nodes);
@@ -268,17 +238,14 @@ public function processNodes(
}
/**
- * @param \PhpParser\Node $parentNode
- * @param \PhpParser\Node\Stmt[] $stmts
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @return StatementResult
+ * @param Node\Stmt[] $stmts
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
public function processStmtNodes(
Node $parentNode,
array $stmts,
MutatingScope $scope,
- callable $nodeCallback
+ callable $nodeCallback,
): StatementResult
{
$exitPoints = [];
@@ -294,7 +261,7 @@ public function processStmtNodes(
$statementResult = $this->processStmtNode(
$stmt,
$scope,
- $nodeCallback
+ $nodeCallback,
);
$scope = $statementResult->getScope();
$hasYield = $hasYield || $statementResult->hasYield();
@@ -309,9 +276,9 @@ public function processStmtNodes(
$hasYield,
$statementResult->isAlwaysTerminating(),
$statementResult->getExitPoints(),
- $statementResult->getThrowPoints()
+ $statementResult->getThrowPoints(),
),
- $parentNode->returnType !== null
+ $parentNode->returnType !== null,
), $scope);
}
@@ -337,7 +304,7 @@ public function processStmtNodes(
$nodeCallback(new ExecutionEndNode(
$parentNode,
$statementResult,
- $parentNode->returnType !== null
+ $parentNode->returnType !== null,
), $scope);
}
@@ -345,15 +312,12 @@ public function processStmtNodes(
}
/**
- * @param \PhpParser\Node\Stmt $stmt
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @return StatementResult
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
private function processStmtNode(
Node\Stmt $stmt,
MutatingScope $scope,
- callable $nodeCallback
+ callable $nodeCallback,
): StatementResult
{
if (
@@ -365,13 +329,15 @@ private function processStmtNode(
!$stmt instanceof Static_
&& !$stmt instanceof Foreach_
&& !$stmt instanceof Node\Stmt\Global_
+ && !$stmt instanceof Node\Stmt\Property
+ && !$stmt instanceof Node\Stmt\PropertyProperty
) {
$scope = $this->processStmtVarAnnotation($scope, $stmt, null);
}
if ($stmt instanceof Node\Stmt\ClassMethod) {
if (!$scope->isInClass()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if (
$scope->isInTrait()
@@ -416,7 +382,7 @@ private function processStmtNode(
foreach ($stmt->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
foreach ($attr->args as $arg) {
- $nodeCallback($arg->value, $scope);
+ $this->processExprNode($arg->value, $scope, $nodeCallback, ExpressionContext::createDeep());
}
}
}
@@ -440,12 +406,13 @@ private function processStmtNode(
$isDeprecated,
$isInternal,
$isFinal,
- $isPure
+ $isPure,
);
$nodeCallback(new InFunctionNode($stmt), $functionScope);
$gatheredReturnStatements = [];
- $statementResult = $this->processStmtNodes($stmt, $stmt->stmts, $functionScope, static function (\PhpParser\Node $node, Scope $scope) use ($nodeCallback, $functionScope, &$gatheredReturnStatements): void {
+ $executionEnds = [];
+ $statementResult = $this->processStmtNodes($stmt, $stmt->stmts, $functionScope, static function (Node $node, Scope $scope) use ($nodeCallback, $functionScope, &$gatheredReturnStatements, &$executionEnds): void {
$nodeCallback($node, $scope);
if ($scope->getFunction() !== $functionScope->getFunction()) {
return;
@@ -453,6 +420,10 @@ private function processStmtNode(
if ($scope->isInAnonymousFunction()) {
return;
}
+ if ($node instanceof ExecutionEndNode) {
+ $executionEnds[] = $node;
+ return;
+ }
if (!$node instanceof Return_) {
return;
}
@@ -463,7 +434,8 @@ private function processStmtNode(
$nodeCallback(new FunctionReturnStatementsNode(
$stmt,
$gatheredReturnStatements,
- $statementResult
+ $statementResult,
+ $executionEnds,
), $functionScope);
} elseif ($stmt instanceof Node\Stmt\ClassMethod) {
$hasYield = false;
@@ -471,7 +443,7 @@ private function processStmtNode(
foreach ($stmt->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
foreach ($attr->args as $arg) {
- $nodeCallback($arg->value, $scope);
+ $this->processExprNode($arg->value, $scope, $nodeCallback, ExpressionContext::createDeep());
}
}
}
@@ -495,7 +467,7 @@ private function processStmtNode(
$isDeprecated,
$isInternal,
$isFinal,
- $isPure
+ $isPure,
);
if ($stmt->name->toLowerString() === '__construct') {
@@ -505,7 +477,7 @@ private function processStmtNode(
}
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$phpDoc = null;
if ($param->getDocComment() !== null) {
@@ -515,10 +487,10 @@ private function processStmtNode(
$param->var->name,
$param->flags,
$param->type,
- $param->default,
+ null,
$phpDoc,
true,
- $param
+ $param,
), $methodScope);
}
}
@@ -529,7 +501,8 @@ private function processStmtNode(
if ($stmt->stmts !== null) {
$gatheredReturnStatements = [];
- $statementResult = $this->processStmtNodes($stmt, $stmt->stmts, $methodScope, static function (\PhpParser\Node $node, Scope $scope) use ($nodeCallback, $methodScope, &$gatheredReturnStatements): void {
+ $executionEnds = [];
+ $statementResult = $this->processStmtNodes($stmt, $stmt->stmts, $methodScope, static function (Node $node, Scope $scope) use ($nodeCallback, $methodScope, &$gatheredReturnStatements, &$executionEnds): void {
$nodeCallback($node, $scope);
if ($scope->getFunction() !== $methodScope->getFunction()) {
return;
@@ -537,6 +510,10 @@ private function processStmtNode(
if ($scope->isInAnonymousFunction()) {
return;
}
+ if ($node instanceof ExecutionEndNode) {
+ $executionEnds[] = $node;
+ return;
+ }
if (!$node instanceof Return_) {
return;
}
@@ -546,7 +523,8 @@ private function processStmtNode(
$nodeCallback(new MethodReturnStatementsNode(
$stmt,
$gatheredReturnStatements,
- $statementResult
+ $statementResult,
+ $executionEnds,
), $methodScope);
}
} elseif ($stmt instanceof Echo_) {
@@ -595,7 +573,7 @@ private function processStmtNode(
$scope = $scope->filterBySpecifiedTypes($this->typeSpecifier->specifyTypesInCondition(
$scope,
$stmt->expr,
- TypeSpecifierContext::createNull()
+ TypeSpecifierContext::createNull(),
));
$hasYield = $result->hasYield();
$throwPoints = $result->getThrowPoints();
@@ -619,12 +597,12 @@ private function processStmtNode(
$hasYield = false;
$throwPoints = [];
if (isset($stmt->namespacedName)) {
- $classReflection = $this->getCurrentClassReflection($stmt, $scope);
+ $classReflection = $this->getCurrentClassReflection($stmt, $stmt->namespacedName->toString(), $scope);
$classScope = $scope->enterClass($classReflection);
$nodeCallback(new InClassNode($stmt, $classReflection), $classScope);
} elseif ($stmt instanceof Class_) {
if ($stmt->name === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if ($stmt->getAttribute('anonymousClass', false) === false) {
$classReflection = $this->reflectionProvider->getClass($stmt->name->toString());
@@ -634,13 +612,13 @@ private function processStmtNode(
$classScope = $scope->enterClass($classReflection);
$nodeCallback(new InClassNode($stmt, $classReflection), $classScope);
} else {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
foreach ($stmt->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
foreach ($attr->args as $arg) {
- $nodeCallback($arg->value, $classScope);
+ $this->processExprNode($arg->value, $classScope, $nodeCallback, ExpressionContext::createDeep());
}
}
}
@@ -656,7 +634,7 @@ private function processStmtNode(
foreach ($stmt->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
foreach ($attr->args as $arg) {
- $nodeCallback($arg->value, $scope);
+ $this->processExprNode($arg->value, $scope, $nodeCallback, ExpressionContext::createDeep());
}
}
}
@@ -671,9 +649,9 @@ private function processStmtNode(
$prop->default,
$docComment !== null ? $docComment->getText() : null,
false,
- $prop
+ $prop,
),
- $scope
+ $scope,
);
}
@@ -789,8 +767,13 @@ private function processStmtNode(
$scope = $condResult->getScope();
$arrayComparisonExpr = new BinaryOp\NotIdentical(
$stmt->expr,
- new Array_([])
+ new Array_([]),
);
+ $inForeachScope = $scope;
+ if ($stmt->expr instanceof Variable && is_string($stmt->expr->name)) {
+ $inForeachScope = $this->processVarAnnotation($scope, [$stmt->expr->name], $stmt);
+ }
+ $nodeCallback(new InForeachNode($stmt), $inForeachScope);
$bodyScope = $this->enterForeach($scope->filterByTruthyValue($arrayComparisonExpr), $stmt);
$count = 0;
do {
@@ -809,7 +792,7 @@ private function processStmtNode(
}
if ($count >= self::GENERALIZE_AFTER_ITERATION) {
- $bodyScope = $bodyScope->generalizeWith($prevScope);
+ $bodyScope = $prevScope->generalizeWith($bodyScope);
}
$count++;
} while (!$alwaysTerminating && $count < self::LOOP_SCOPE_ITERATIONS);
@@ -842,13 +825,16 @@ private function processStmtNode(
if (!$isIterableAtLeastOnce->no()) {
$throwPoints = array_merge($throwPoints, $finalScopeResult->getThrowPoints());
}
+ if (!(new ObjectType(Traversable::class))->isSuperTypeOf($scope->getType($stmt->expr))->no()) {
+ $throwPoints[] = ThrowPoint::createImplicit($scope, $stmt->expr);
+ }
return new StatementResult(
$finalScope,
$finalScopeResult->hasYield() || $condResult->hasYield(),
$isIterableAtLeastOnce->yes() && $finalScopeResult->isAlwaysTerminating(),
$finalScopeResult->getExitPointsForOuterLoop(),
- $throwPoints
+ $throwPoints,
);
} elseif ($stmt instanceof While_) {
$condResult = $this->processExprNode($stmt->cond, $scope, static function (): void {
@@ -872,7 +858,7 @@ private function processStmtNode(
}
if ($count >= self::GENERALIZE_AFTER_ITERATION) {
- $bodyScope = $bodyScope->generalizeWith($prevScope);
+ $bodyScope = $prevScope->generalizeWith($bodyScope);
}
$count++;
} while (!$alwaysTerminating && $count < self::LOOP_SCOPE_ITERATIONS);
@@ -881,7 +867,7 @@ private function processStmtNode(
$bodyScopeMaybeRan = $bodyScope;
$bodyScope = $this->processExprNode($stmt->cond, $bodyScope, $nodeCallback, ExpressionContext::createDeep())->getTruthyScope();
$finalScopeResult = $this->processStmtNodes($stmt, $stmt->stmts, $bodyScope, $nodeCallback)->filterOutLoopExitPoints();
- $finalScope = $finalScopeResult->getScope();
+ $finalScope = $finalScopeResult->getScope()->filterByFalseyValue($stmt->cond);
foreach ($finalScopeResult->getExitPointsByType(Continue_::class) as $continueExitPoint) {
$finalScope = $finalScope->mergeWith($continueExitPoint->getScope());
}
@@ -895,6 +881,7 @@ private function processStmtNode(
$isIterableAtLeastOnce = $beforeCondBooleanType instanceof ConstantBooleanType && $beforeCondBooleanType->getValue();
$alwaysIterates = $condBooleanType instanceof ConstantBooleanType && $condBooleanType->getValue();
$neverIterates = $condBooleanType instanceof ConstantBooleanType && !$condBooleanType->getValue();
+ $nodeCallback(new BreaklessWhileLoopNode($stmt, $finalScopeResult->getExitPoints()), $bodyScopeMaybeRan);
if ($alwaysIterates) {
$isAlwaysTerminating = count($finalScopeResult->getExitPointsByType(Break_::class)) === 0;
@@ -921,7 +908,7 @@ private function processStmtNode(
$finalScopeResult->hasYield() || $condResult->hasYield(),
$isAlwaysTerminating,
$finalScopeResult->getExitPointsForOuterLoop(),
- $throwPoints
+ $throwPoints,
);
} elseif ($stmt instanceof Do_) {
$finalScope = null;
@@ -950,7 +937,7 @@ private function processStmtNode(
}
if ($count >= self::GENERALIZE_AFTER_ITERATION) {
- $bodyScope = $bodyScope->generalizeWith($prevScope);
+ $bodyScope = $prevScope->generalizeWith($bodyScope);
}
$count++;
} while (!$alwaysTerminating && $count < self::LOOP_SCOPE_ITERATIONS);
@@ -959,17 +946,19 @@ private function processStmtNode(
$bodyScopeResult = $this->processStmtNodes($stmt, $stmt->stmts, $bodyScope, $nodeCallback)->filterOutLoopExitPoints();
$bodyScope = $bodyScopeResult->getScope();
+ foreach ($bodyScopeResult->getExitPointsByType(Continue_::class) as $continueExitPoint) {
+ $bodyScope = $bodyScope->mergeWith($continueExitPoint->getScope());
+ }
$condBooleanType = $bodyScope->getType($stmt->cond)->toBoolean();
$alwaysIterates = $condBooleanType instanceof ConstantBooleanType && $condBooleanType->getValue();
+ $nodeCallback(new DoWhileLoopConditionNode($stmt->cond, $bodyScopeResult->getExitPoints()), $bodyScope);
+
if ($alwaysIterates) {
$alwaysTerminating = count($bodyScopeResult->getExitPointsByType(Break_::class)) === 0;
} else {
$alwaysTerminating = $bodyScopeResult->isAlwaysTerminating();
}
- foreach ($bodyScopeResult->getExitPointsByType(Continue_::class) as $continueExitPoint) {
- $bodyScope = $bodyScope->mergeWith($continueExitPoint->getScope());
- }
$finalScope = $alwaysTerminating ? $finalScope : $bodyScope->mergeWith($finalScope);
if ($finalScope === null) {
$finalScope = $scope;
@@ -979,6 +968,8 @@ private function processStmtNode(
$hasYield = $condResult->hasYield();
$throwPoints = $condResult->getThrowPoints();
$finalScope = $condResult->getFalseyScope();
+ } else {
+ $this->processExprNode($stmt->cond, $bodyScope, $nodeCallback, ExpressionContext::createDeep());
}
foreach ($bodyScopeResult->getExitPointsByType(Break_::class) as $breakExitPoint) {
$finalScope = $breakExitPoint->getScope()->mergeWith($finalScope);
@@ -989,7 +980,7 @@ private function processStmtNode(
$bodyScopeResult->hasYield() || $hasYield,
$alwaysTerminating,
$bodyScopeResult->getExitPointsForOuterLoop(),
- array_merge($throwPoints, $bodyScopeResult->getThrowPoints())
+ array_merge($throwPoints, $bodyScopeResult->getThrowPoints()),
);
} elseif ($stmt instanceof For_) {
$initScope = $scope;
@@ -1003,9 +994,18 @@ private function processStmtNode(
}
$bodyScope = $initScope;
+ $isIterableAtLeastOnce = TrinaryLogic::createYes();
foreach ($stmt->cond as $condExpr) {
$condResult = $this->processExprNode($condExpr, $bodyScope, static function (): void {
}, ExpressionContext::createDeep());
+ $initScope = $condResult->getScope();
+ $condTruthiness = $condResult->getScope()->getType($condExpr)->toBoolean();
+ if ($condTruthiness instanceof ConstantBooleanType) {
+ $condTruthinessTrinary = TrinaryLogic::createFromBoolean($condTruthiness->getValue());
+ } else {
+ $condTruthinessTrinary = TrinaryLogic::createMaybe();
+ }
+ $isIterableAtLeastOnce = $isIterableAtLeastOnce->and($condTruthinessTrinary);
$hasYield = $hasYield || $condResult->hasYield();
$throwPoints = array_merge($throwPoints, $condResult->getThrowPoints());
$bodyScope = $condResult->getTruthyScope();
@@ -1039,7 +1039,7 @@ private function processStmtNode(
}
if ($count >= self::GENERALIZE_AFTER_ITERATION) {
- $bodyScope = $bodyScope->generalizeWith($prevScope);
+ $bodyScope = $prevScope->generalizeWith($bodyScope);
}
$count++;
} while (!$alwaysTerminating && $count < self::LOOP_SCOPE_ITERATIONS);
@@ -1054,25 +1054,45 @@ private function processStmtNode(
foreach ($finalScopeResult->getExitPointsByType(Continue_::class) as $continueExitPoint) {
$finalScope = $continueExitPoint->getScope()->mergeWith($finalScope);
}
+
+ $loopScope = $finalScope;
foreach ($stmt->loop as $loopExpr) {
- $finalScope = $this->processExprNode($loopExpr, $finalScope, $nodeCallback, ExpressionContext::createTopLevel())->getScope();
+ $loopScope = $this->processExprNode($loopExpr, $loopScope, $nodeCallback, ExpressionContext::createTopLevel())->getScope();
+ }
+ $finalScope = $finalScope->generalizeWith($loopScope);
+ foreach ($stmt->cond as $condExpr) {
+ $finalScope = $finalScope->filterByFalseyValue($condExpr);
}
+
foreach ($finalScopeResult->getExitPointsByType(Break_::class) as $breakExitPoint) {
$finalScope = $breakExitPoint->getScope()->mergeWith($finalScope);
}
- if ($this->polluteScopeWithLoopInitialAssignments) {
- $scope = $initScope;
- }
+ if ($isIterableAtLeastOnce->no() || $finalScopeResult->isAlwaysTerminating()) {
+ if ($this->polluteScopeWithLoopInitialAssignments) {
+ $finalScope = $initScope;
+ } else {
+ $finalScope = $scope;
+ }
- $finalScope = $finalScope->mergeWith($scope);
+ } elseif ($isIterableAtLeastOnce->maybe()) {
+ if ($this->polluteScopeWithLoopInitialAssignments) {
+ $finalScope = $finalScope->mergeWith($initScope);
+ } else {
+ $finalScope = $finalScope->mergeWith($scope);
+ }
+ } else {
+ if (!$this->polluteScopeWithLoopInitialAssignments) {
+ $finalScope = $finalScope->mergeWith($scope);
+ }
+ }
return new StatementResult(
$finalScope,
$finalScopeResult->hasYield() || $hasYield,
false/* $finalScopeResult->isAlwaysTerminating() && $isAlwaysIterable*/,
$finalScopeResult->getExitPointsForOuterLoop(),
- array_merge($throwPoints, $finalScopeResult->getThrowPoints())
+ array_merge($throwPoints, $finalScopeResult->getThrowPoints()),
);
} elseif ($stmt instanceof Switch_) {
$condResult = $this->processExprNode($stmt->cond, $scope, $nodeCallback, ExpressionContext::createDeep());
@@ -1146,6 +1166,7 @@ private function processStmtNode(
$finalScope = $branchScopeResult->isAlwaysTerminating() ? null : $branchScope;
$exitPoints = [];
+ $finallyExitPoints = [];
$alwaysTerminating = $branchScopeResult->isAlwaysTerminating();
$hasYield = $branchScopeResult->hasYield();
@@ -1155,6 +1176,7 @@ private function processStmtNode(
$finallyScope = null;
}
foreach ($branchScopeResult->getExitPoints() as $exitPoint) {
+ $finallyExitPoints[] = $exitPoint;
if ($exitPoint->getStatement() instanceof Throw_) {
continue;
}
@@ -1171,89 +1193,76 @@ private function processStmtNode(
foreach ($stmt->catches as $catchNode) {
$nodeCallback($catchNode, $scope);
- if ($this->preciseExceptionTracking || !$this->polluteCatchScopeWithTryAssignments) {
- $catchType = TypeCombinator::union(...array_map(static function (Name $name): Type {
- return new ObjectType($name->toString());
- }, $catchNode->types));
- $originalCatchType = $catchType;
- $catchType = TypeCombinator::remove($catchType, $pastCatchTypes);
- $pastCatchTypes = TypeCombinator::union($pastCatchTypes, $originalCatchType);
- $matchingThrowPoints = [];
- $newThrowPoints = [];
- foreach ($throwPoints as $throwPoint) {
- if (!$throwPoint->isExplicit() && !$catchType->isSuperTypeOf(new ObjectType(\Throwable::class))->yes()) {
- continue;
- }
- $isSuperType = $catchType->isSuperTypeOf($throwPoint->getType());
- if ($isSuperType->no()) {
- continue;
- }
+ $catchType = TypeCombinator::union(...array_map(static fn (Name $name): Type => new ObjectType($name->toString()), $catchNode->types));
+ $originalCatchType = $catchType;
+ $catchType = TypeCombinator::remove($catchType, $pastCatchTypes);
+ $pastCatchTypes = TypeCombinator::union($pastCatchTypes, $originalCatchType);
+ $matchingThrowPoints = [];
+ $newThrowPoints = [];
+ foreach ($throwPoints as $throwPoint) {
+ if (!$throwPoint->isExplicit() && !$catchType->isSuperTypeOf(new ObjectType(Throwable::class))->yes()) {
+ continue;
+ }
+ $isSuperType = $catchType->isSuperTypeOf($throwPoint->getType());
+ if ($isSuperType->no()) {
+ continue;
+ }
+ $matchingThrowPoints[] = $throwPoint;
+ }
+ $hasExplicit = count($matchingThrowPoints) > 0;
+ foreach ($throwPoints as $throwPoint) {
+ $isSuperType = $catchType->isSuperTypeOf($throwPoint->getType());
+ if (!$hasExplicit && !$isSuperType->no()) {
$matchingThrowPoints[] = $throwPoint;
}
- $hasExplicit = count($matchingThrowPoints) > 0;
- foreach ($throwPoints as $throwPoint) {
- $isSuperType = $catchType->isSuperTypeOf($throwPoint->getType());
- if (!$hasExplicit && !$isSuperType->no()) {
- $matchingThrowPoints[] = $throwPoint;
- }
- if ($isSuperType->yes()) {
- continue;
- }
- $newThrowPoints[] = $throwPoint->subtractCatchType($catchType);
+ if ($isSuperType->yes()) {
+ continue;
}
- $throwPoints = $newThrowPoints;
-
- if (count($matchingThrowPoints) === 0) {
- $throwableThrowPoints = [];
- if ($originalCatchType->isSuperTypeOf(new ObjectType(\Throwable::class))->yes()) {
- foreach ($branchScopeResult->getThrowPoints() as $originalThrowPoint) {
- if (!$originalThrowPoint->canContainAnyThrowable()) {
- continue;
- }
+ $newThrowPoints[] = $throwPoint->subtractCatchType($catchType);
+ }
+ $throwPoints = $newThrowPoints;
- $throwableThrowPoints[] = $originalThrowPoint;
+ if (count($matchingThrowPoints) === 0) {
+ $throwableThrowPoints = [];
+ if ($originalCatchType->isSuperTypeOf(new ObjectType(Throwable::class))->yes()) {
+ foreach ($branchScopeResult->getThrowPoints() as $originalThrowPoint) {
+ if (!$originalThrowPoint->canContainAnyThrowable()) {
+ continue;
}
- }
- if (count($throwableThrowPoints) === 0) {
- $nodeCallback(new CatchWithUnthrownExceptionNode($catchNode, $catchType), $scope);
- continue;
+ $throwableThrowPoints[] = $originalThrowPoint;
}
-
- $matchingThrowPoints = $throwableThrowPoints;
}
- $catchScope = null;
- foreach ($matchingThrowPoints as $matchingThrowPoint) {
- if ($catchScope === null) {
- $catchScope = $matchingThrowPoint->getScope();
- } else {
- $catchScope = $catchScope->mergeWith($matchingThrowPoint->getScope());
- }
+ if (count($throwableThrowPoints) === 0) {
+ $nodeCallback(new CatchWithUnthrownExceptionNode($catchNode, $catchType, $originalCatchType), $scope);
+ continue;
}
- $variableName = null;
- if ($catchNode->var !== null) {
- if (!is_string($catchNode->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
- }
+ $matchingThrowPoints = $throwableThrowPoints;
+ }
- $variableName = $catchNode->var->name;
+ $catchScope = null;
+ foreach ($matchingThrowPoints as $matchingThrowPoint) {
+ if ($catchScope === null) {
+ $catchScope = $matchingThrowPoint->getScope();
+ } else {
+ $catchScope = $catchScope->mergeWith($matchingThrowPoint->getScope());
}
+ }
- $catchScopeResult = $this->processStmtNodes($catchNode, $catchNode->stmts, $catchScope->enterCatchType($catchType, $variableName), $nodeCallback);
- $catchScopeForFinally = $catchScopeResult->getScope();
- } else {
- $initialScope = $scope;
- if (count($throwPoints) > 0) {
- $initialScope = $throwPoints[0]->getScope();
+ $variableName = null;
+ if ($catchNode->var !== null) {
+ if (!is_string($catchNode->var->name)) {
+ throw new ShouldNotHappenException();
}
- $catchScopeForFinally = $this->processCatchNode($catchNode, $branchScope, $nodeCallback)->getScope();
- $catchScopeResult = $this->processCatchNode($catchNode, $initialScope->mergeWith($branchScope), static function (): void {
- });
+ $variableName = $catchNode->var->name;
}
+ $catchScopeResult = $this->processStmtNodes($catchNode, $catchNode->stmts, $catchScope->enterCatchType($catchType, $variableName), $nodeCallback);
+ $catchScopeForFinally = $catchScopeResult->getScope();
+
$finalScope = $catchScopeResult->isAlwaysTerminating() ? $finalScope : $catchScopeResult->getScope()->mergeWith($finalScope);
$alwaysTerminating = $alwaysTerminating && $catchScopeResult->isAlwaysTerminating();
$hasYield = $hasYield || $catchScopeResult->hasYield();
@@ -1264,6 +1273,7 @@ private function processStmtNode(
$finallyScope = $finallyScope->mergeWith($catchScopeForFinally);
}
foreach ($catchScopeResult->getExitPoints() as $exitPoint) {
+ $finallyExitPoints[] = $exitPoint;
if ($exitPoint->getStatement() instanceof Throw_) {
continue;
}
@@ -1303,7 +1313,7 @@ private function processStmtNode(
if (count($finallyResult->getExitPoints()) > 0) {
$nodeCallback(new FinallyExitPointsNode(
$finallyResult->getExitPoints(),
- $exitPoints
+ $finallyExitPoints,
), $scope);
}
$exitPoints = array_merge($exitPoints, $finallyResult->getExitPoints());
@@ -1331,7 +1341,7 @@ private function processStmtNode(
$vars = [];
foreach ($stmt->vars as $var) {
if (!$var instanceof Variable) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$scope = $this->lookForEnterVariableAssign($scope, $var);
$this->processExprNode($var, $scope, $nodeCallback, ExpressionContext::createDeep());
@@ -1364,7 +1374,7 @@ private function processStmtNode(
$hasYield = false;
$throwPoints = [];
if (!is_string($stmt->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if ($stmt->default !== null) {
$this->processExprNode($stmt->default, $scope, $nodeCallback, ExpressionContext::createDeep());
@@ -1380,7 +1390,7 @@ private function processStmtNode(
foreach ($stmt->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
foreach ($attr->args as $arg) {
- $nodeCallback($arg->value, $scope);
+ $this->processExprNode($arg->value, $scope, $nodeCallback, ExpressionContext::createDeep());
}
}
}
@@ -1408,8 +1418,6 @@ private function processStmtNode(
}
/**
- * @param Node\Stmt $statement
- * @param MutatingScope $scope
* @return ThrowPoint[]|null
*/
private function getOverridingThrowPoints(Node\Stmt $statement, MutatingScope $scope): ?array
@@ -1425,100 +1433,81 @@ private function getOverridingThrowPoints(Node\Stmt $statement, MutatingScope $s
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
$function !== null ? $function->getName() : null,
- $comment->getText()
+ $comment->getText(),
);
$throwsTag = $resolvedPhpDoc->getThrowsTag();
if ($throwsTag !== null) {
- return [ThrowPoint::createExplicit($scope, $throwsTag->getType(), $statement, false)];
+ $throwsType = $throwsTag->getType();
+ if ($throwsType instanceof VoidType) {
+ return [];
+ }
+
+ return [ThrowPoint::createExplicit($scope, $throwsType, $statement, false)];
}
}
return null;
}
- private function getCurrentClassReflection(Node\Stmt\ClassLike $stmt, Scope $scope): ClassReflection
+ private function getCurrentClassReflection(Node\Stmt\ClassLike $stmt, string $className, Scope $scope): ClassReflection
{
- $className = $stmt->namespacedName->toString();
if (!$this->reflectionProvider->hasClass($className)) {
- return $this->createAstClassReflection($stmt, $scope);
+ return $this->createAstClassReflection($stmt, $className, $scope);
}
- $defaultClassReflection = $this->reflectionProvider->getClass($stmt->namespacedName->toString());
+ $defaultClassReflection = $this->reflectionProvider->getClass($className);
if ($defaultClassReflection->getFileName() !== $scope->getFile()) {
- return $this->createAstClassReflection($stmt, $scope);
+ return $this->createAstClassReflection($stmt, $className, $scope);
}
$startLine = $defaultClassReflection->getNativeReflection()->getStartLine();
if ($startLine !== $stmt->getStartLine()) {
- return $this->createAstClassReflection($stmt, $scope);
+ return $this->createAstClassReflection($stmt, $className, $scope);
}
return $defaultClassReflection;
}
- private function createAstClassReflection(Node\Stmt\ClassLike $stmt, Scope $scope): ClassReflection
+ private function createAstClassReflection(Node\Stmt\ClassLike $stmt, string $className, Scope $scope): ClassReflection
{
$nodeToReflection = new NodeToReflection();
$betterReflectionClass = $nodeToReflection->__invoke(
- $this->classReflector,
+ $this->reflector,
$stmt,
- new LocatedSource(FileReader::read($scope->getFile()), $scope->getFile()),
- $scope->getNamespace() !== null ? new Node\Stmt\Namespace_(new Name($scope->getNamespace())) : null
+ new LocatedSource(FileReader::read($scope->getFile()), $className, $scope->getFile()),
+ $scope->getNamespace() !== null ? new Node\Stmt\Namespace_(new Name($scope->getNamespace())) : null,
);
if (!$betterReflectionClass instanceof \PHPStan\BetterReflection\Reflection\ReflectionClass) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
+ $enumAdapter = base64_decode('UEhQU3RhblxCZXR0ZXJSZWZsZWN0aW9uXFJlZmxlY3Rpb25cQWRhcHRlclxSZWZsZWN0aW9uRW51bQ==', true);
+
return new ClassReflection(
$this->reflectionProvider,
$this->fileTypeMapper,
+ $this->stubPhpDocProvider,
+ $this->phpDocInheritanceResolver,
$this->phpVersion,
$this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(),
$this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(),
$betterReflectionClass->getName(),
- new ReflectionClass($betterReflectionClass),
+ $betterReflectionClass instanceof ReflectionEnum && PHP_VERSION_ID >= 80000 ? new $enumAdapter($betterReflectionClass) : new ReflectionClass($betterReflectionClass),
null,
null,
null,
- sprintf('%s:%d', $scope->getFile(), $stmt->getStartLine())
+ sprintf('%s:%d', $scope->getFile(), $stmt->getStartLine()),
);
}
- /**
- * @param Node\Stmt\Catch_ $catchNode
- * @param MutatingScope $catchScope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @return StatementResult
- */
- private function processCatchNode(
- Node\Stmt\Catch_ $catchNode,
- MutatingScope $catchScope,
- callable $nodeCallback
- ): StatementResult
- {
- $variableName = null;
- if ($catchNode->var !== null) {
- if (!is_string($catchNode->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
- }
-
- $variableName = $catchNode->var->name;
- }
-
- $catchScope = $catchScope->enterCatch($catchNode->types, $variableName);
- return $this->processStmtNodes($catchNode, $catchNode->stmts, $catchScope, $nodeCallback);
- }
-
private function lookForEnterVariableAssign(MutatingScope $scope, Expr $expr): MutatingScope
{
if (!$expr instanceof ArrayDimFetch || $expr->dim !== null) {
$scope = $scope->enterExpressionAssign($expr);
}
if (!$expr instanceof Variable) {
- return $this->lookForVariableAssignCallback($scope, $expr, static function (MutatingScope $scope, Expr $expr): MutatingScope {
- return $scope->enterExpressionAssign($expr);
- });
+ return $this->lookForVariableAssignCallback($scope, $expr, static fn (MutatingScope $scope, Expr $expr): MutatingScope => $scope->enterExpressionAssign($expr));
}
return $scope;
@@ -1526,32 +1515,29 @@ private function lookForEnterVariableAssign(MutatingScope $scope, Expr $expr): M
private function lookForExitVariableAssign(MutatingScope $scope, Expr $expr): MutatingScope
{
- $scope = $scope->exitExpressionAssign($expr);
+ if (!$expr instanceof ArrayDimFetch || $expr->dim !== null) {
+ $scope = $scope->exitExpressionAssign($expr);
+ }
if (!$expr instanceof Variable) {
- return $this->lookForVariableAssignCallback($scope, $expr, static function (MutatingScope $scope, Expr $expr): MutatingScope {
- return $scope->exitExpressionAssign($expr);
- });
+ return $this->lookForVariableAssignCallback($scope, $expr, static fn (MutatingScope $scope, Expr $expr): MutatingScope => $scope->exitExpressionAssign($expr));
}
return $scope;
}
/**
- * @param MutatingScope $scope
- * @param Expr $expr
- * @param \Closure(MutatingScope $scope, Expr $expr): MutatingScope $callback
- * @return MutatingScope
+ * @param Closure(MutatingScope $scope, Expr $expr): MutatingScope $callback
*/
- private function lookForVariableAssignCallback(MutatingScope $scope, Expr $expr, \Closure $callback): MutatingScope
+ private function lookForVariableAssignCallback(MutatingScope $scope, Expr $expr, Closure $callback): MutatingScope
{
if ($expr instanceof Variable) {
$scope = $callback($scope, $expr);
} elseif ($expr instanceof ArrayDimFetch) {
- while ($expr instanceof ArrayDimFetch) {
- $expr = $expr->var;
+ if ($expr->dim !== null) {
+ $scope = $callback($scope, $expr);
}
- $scope = $this->lookForVariableAssignCallback($scope, $expr, $callback);
+ $scope = $this->lookForVariableAssignCallback($scope, $expr->var, $callback);
} elseif ($expr instanceof PropertyFetch || $expr instanceof Expr\NullsafePropertyFetch) {
$scope = $this->lookForVariableAssignCallback($scope, $expr->var, $callback);
} elseif ($expr instanceof StaticPropertyFetch) {
@@ -1580,14 +1566,14 @@ private function ensureShallowNonNullability(MutatingScope $scope, Expr $exprToS
$scope = $scope->specifyExpressionType(
$exprToSpecify,
$exprTypeWithoutNull,
- TypeCombinator::removeNull($nativeType)
+ TypeCombinator::removeNull($nativeType),
);
return new EnsuredNonNullabilityResult(
$scope,
[
new EnsuredNonNullabilityResultExpression($exprToSpecify, $exprType, $nativeType),
- ]
+ ],
);
}
@@ -1622,9 +1608,7 @@ private function ensureNonNullability(MutatingScope $scope, Expr $expr, bool $fi
}
/**
- * @param MutatingScope $scope
* @param EnsuredNonNullabilityResultExpression[] $specifiedExpressions
- * @return MutatingScope
*/
private function revertNonNullability(MutatingScope $scope, array $specifiedExpressions): MutatingScope
{
@@ -1632,7 +1616,7 @@ private function revertNonNullability(MutatingScope $scope, array $specifiedExpr
$scope = $scope->specifyExpressionType(
$specifiedExpressionResult->getExpression(),
$specifiedExpressionResult->getOriginalType(),
- $specifiedExpressionResult->getOriginalNativeType()
+ $specifiedExpressionResult->getOriginalNativeType(),
);
}
@@ -1688,14 +1672,26 @@ private function findEarlyTerminatingExpr(Expr $expr, Scope $scope): ?Expr
}
/**
- * @param \PhpParser\Node\Expr $expr
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @param \PHPStan\Analyser\ExpressionContext $context
- * @return \PHPStan\Analyser\ExpressionResult
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
private function processExprNode(Expr $expr, MutatingScope $scope, callable $nodeCallback, ExpressionContext $context): ExpressionResult
{
+ if ($expr instanceof Expr\CallLike && $expr->isFirstClassCallable()) {
+ if ($expr instanceof FuncCall) {
+ $newExpr = new FunctionCallableNode($expr->name, $expr->getAttributes());
+ } elseif ($expr instanceof MethodCall) {
+ $newExpr = new MethodCallableNode($expr->var, $expr->name, $expr);
+ } elseif ($expr instanceof StaticCall) {
+ $newExpr = new StaticMethodCallableNode($expr->class, $expr->name, $expr);
+ } elseif ($expr instanceof New_ && !$expr->class instanceof Class_) {
+ $newExpr = new InstantiationCallableNode($expr->class, $expr->getAttributes());
+ } else {
+ throw new ShouldNotHappenException();
+ }
+
+ return $this->processExprNode($newExpr, $scope, $nodeCallback, $context);
+ }
+
$this->callNodeCallbackWithExpression($nodeCallback, $expr, $scope, $context);
if ($expr instanceof Variable) {
@@ -1705,84 +1701,49 @@ private function processExprNode(Expr $expr, MutatingScope $scope, callable $nod
return $this->processExprNode($expr->name, $scope, $nodeCallback, $context->enterDeep());
}
} elseif ($expr instanceof Assign || $expr instanceof AssignRef) {
- if (!$expr->var instanceof Array_ && !$expr->var instanceof List_) {
- $result = $this->processAssignVar(
- $scope,
- $expr->var,
- $expr->expr,
- $nodeCallback,
- $context,
- function (MutatingScope $scope) use ($expr, $nodeCallback, $context): ExpressionResult {
- if ($expr instanceof AssignRef) {
- $scope = $scope->enterExpressionAssign($expr->expr);
- }
+ $result = $this->processAssignVar(
+ $scope,
+ $expr->var,
+ $expr->expr,
+ $nodeCallback,
+ $context,
+ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): ExpressionResult {
+ if ($expr instanceof AssignRef) {
+ $scope = $scope->enterExpressionAssign($expr->expr);
+ }
- if ($expr->var instanceof Variable && is_string($expr->var->name)) {
- $context = $context->enterRightSideAssign(
- $expr->var->name,
- $scope->getType($expr->expr)
- );
- }
+ if ($expr->var instanceof Variable && is_string($expr->var->name)) {
+ $context = $context->enterRightSideAssign(
+ $expr->var->name,
+ $scope->getType($expr->expr),
+ );
+ }
- $result = $this->processExprNode($expr->expr, $scope, $nodeCallback, $context->enterDeep());
- $hasYield = $result->hasYield();
- $throwPoints = $result->getThrowPoints();
- $scope = $result->getScope();
+ $result = $this->processExprNode($expr->expr, $scope, $nodeCallback, $context->enterDeep());
+ $hasYield = $result->hasYield();
+ $throwPoints = $result->getThrowPoints();
+ $scope = $result->getScope();
- if ($expr instanceof AssignRef) {
- $scope = $scope->exitExpressionAssign($expr->expr);
- }
+ if ($expr instanceof AssignRef) {
+ $scope = $scope->exitExpressionAssign($expr->expr);
+ }
- return new ExpressionResult($scope, $hasYield, $throwPoints);
- },
- true
- );
- $scope = $result->getScope();
- $hasYield = $result->hasYield();
- $throwPoints = $result->getThrowPoints();
+ return new ExpressionResult($scope, $hasYield, $throwPoints);
+ },
+ true,
+ );
+ $scope = $result->getScope();
+ $hasYield = $result->hasYield();
+ $throwPoints = $result->getThrowPoints();
+ $vars = $this->getAssignedVariables($expr->var);
+ if (count($vars) > 0) {
$varChangedScope = false;
- if ($expr->var instanceof Variable && is_string($expr->var->name)) {
- $scope = $this->processVarAnnotation($scope, [$expr->var->name], $expr, $varChangedScope);
- }
-
+ $scope = $this->processVarAnnotation($scope, $vars, $expr, $varChangedScope);
if (!$varChangedScope) {
$scope = $this->processStmtVarAnnotation($scope, new Node\Stmt\Expression($expr, [
'comments' => $expr->getAttribute('comments'),
]), null);
}
- } else {
- $result = $this->processExprNode($expr->expr, $scope, $nodeCallback, $context->enterDeep());
- $hasYield = $result->hasYield();
- $throwPoints = $result->getThrowPoints();
- $scope = $result->getScope();
- foreach ($expr->var->items as $arrayItem) {
- if ($arrayItem === null) {
- continue;
- }
-
- $itemScope = $scope;
- if ($arrayItem->value instanceof ArrayDimFetch && $arrayItem->value->dim === null) {
- $itemScope = $itemScope->enterExpressionAssign($arrayItem->value);
- }
- $itemScope = $this->lookForEnterVariableAssign($itemScope, $arrayItem->value);
-
- $itemResult = $this->processExprNode($arrayItem, $itemScope, $nodeCallback, $context->enterDeep());
- $hasYield = $hasYield || $itemResult->hasYield();
- $throwPoints = array_merge($throwPoints, $itemResult->getThrowPoints());
- $scope = $result->getScope();
- }
- $scope = $this->lookForArrayDestructuringArray($scope, $expr->var, $scope->getType($expr->expr));
- $vars = $this->getAssignedVariables($expr->var);
-
- if (count($vars) > 0) {
- $varChangedScope = false;
- $scope = $this->processVarAnnotation($scope, $vars, $expr, $varChangedScope);
- if (!$varChangedScope) {
- $scope = $this->processStmtVarAnnotation($scope, new Node\Stmt\Expression($expr, [
- 'comments' => $expr->getAttribute('comments'),
- ]), null);
- }
- }
}
} elseif ($expr instanceof Expr\AssignOp) {
$result = $this->processAssignVar(
@@ -1791,10 +1752,8 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$expr,
$nodeCallback,
$context,
- function (MutatingScope $scope) use ($expr, $nodeCallback, $context): ExpressionResult {
- return $this->processExprNode($expr->expr, $scope, $nodeCallback, $context->enterDeep());
- },
- $expr instanceof Expr\AssignOp\Coalesce
+ fn (MutatingScope $scope): ExpressionResult => $this->processExprNode($expr->expr, $scope, $nodeCallback, $context->enterDeep()),
+ $expr instanceof Expr\AssignOp\Coalesce,
);
$scope = $result->getScope();
$hasYield = $result->hasYield();
@@ -1804,6 +1763,14 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$functionReflection = null;
$throwPoints = [];
if ($expr->name instanceof Expr) {
+ $nameType = $scope->getType($expr->name);
+ if ($nameType->isCallable()->yes()) {
+ $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
+ $scope,
+ $expr->getArgs(),
+ $nameType->getCallableParametersAcceptors($scope),
+ );
+ }
$nameResult = $this->processExprNode($expr->name, $scope, $nodeCallback, $context->enterDeep());
$throwPoints = $nameResult->getThrowPoints();
$scope = $nameResult->getScope();
@@ -1811,11 +1778,11 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$functionReflection = $this->reflectionProvider->getFunction($expr->name, $scope);
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
$scope,
- $expr->args,
- $functionReflection->getVariants()
+ $expr->getArgs(),
+ $functionReflection->getVariants(),
);
}
- $result = $this->processArgs($functionReflection, $parametersAcceptor, $expr->args, $scope, $nodeCallback, $context);
+ $result = $this->processArgs($functionReflection, $parametersAcceptor, $expr->getArgs(), $scope, $nodeCallback, $context);
$scope = $result->getScope();
$hasYield = $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
@@ -1842,13 +1809,11 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
if (
isset($functionReflection)
&& in_array($functionReflection->getName(), ['array_pop', 'array_shift'], true)
- && count($expr->args) >= 1
+ && count($expr->getArgs()) >= 1
) {
- $arrayArg = $expr->args[0]->value;
+ $arrayArg = $expr->getArgs()[0]->value;
$constantArrays = TypeUtils::getConstantArrays($scope->getType($arrayArg));
- $scope = $scope->invalidateExpression($arrayArg)
- ->invalidateExpression(new FuncCall(new Name\FullyQualified('count'), [$expr->args[0]]))
- ->invalidateExpression(new FuncCall(new Name('count'), [$expr->args[0]]));
+ $scope = $scope->invalidateExpression($arrayArg);
if (count($constantArrays) > 0) {
$resultArrayTypes = [];
@@ -1862,7 +1827,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$scope = $scope->specifyExpressionType(
$arrayArg,
- TypeCombinator::union(...$resultArrayTypes)
+ TypeCombinator::union(...$resultArrayTypes),
);
} else {
$arrays = TypeUtils::getAnyArrays($scope->getType($arrayArg));
@@ -1875,10 +1840,10 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
if (
isset($functionReflection)
&& in_array($functionReflection->getName(), ['array_push', 'array_unshift'], true)
- && count($expr->args) >= 2
+ && count($expr->getArgs()) >= 2
) {
$argumentTypes = [];
- foreach (array_slice($expr->args, 1) as $callArg) {
+ foreach (array_slice($expr->getArgs(), 1) as $callArg) {
$callArgType = $scope->getType($callArg->value);
if ($callArg->unpack) {
$iterableValueType = $callArgType->getIterableValueType();
@@ -1895,7 +1860,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$argumentTypes[] = $callArgType;
}
- $arrayArg = $expr->args[0]->value;
+ $arrayArg = $expr->getArgs()[0]->value;
$originalArrayType = $scope->getType($arrayArg);
$constantArrays = TypeUtils::getConstantArrays($originalArrayType);
if (
@@ -1931,7 +1896,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$scope = $scope->invalidateExpression($arrayArg)->specifyExpressionType(
$arrayArg,
- TypeCombinator::union(...$arrayTypes)
+ TypeCombinator::union(...$arrayTypes),
);
}
}
@@ -1943,6 +1908,23 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$scope = $scope->assignVariable('http_response_header', new ArrayType(new IntegerType(), new StringType()));
}
+ if (
+ isset($functionReflection)
+ && $functionReflection->getName() === 'array_splice'
+ && count($expr->getArgs()) >= 1
+ ) {
+ $arrayArg = $expr->getArgs()[0]->value;
+ $arrayArgType = $scope->getType($arrayArg);
+ $valueType = $arrayArgType->getIterableValueType();
+ if (count($expr->getArgs()) >= 4) {
+ $valueType = TypeCombinator::union($valueType, $scope->getType($expr->getArgs()[3]->value)->getIterableValueType());
+ }
+ $scope = $scope->invalidateExpression($arrayArg)->specifyExpressionType(
+ $arrayArg,
+ new ArrayType($arrayArgType->getIterableKeyType(), $valueType),
+ );
+ }
+
if (isset($functionReflection) && $functionReflection->getName() === 'extract') {
$scope = $scope->afterExtractCall();
}
@@ -1952,7 +1934,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
}
if (isset($functionReflection) && $functionReflection->hasSideEffects()->yes()) {
- foreach ($expr->args as $arg) {
+ foreach ($expr->getArgs() as $arg) {
$scope = $scope->invalidateExpression($arg->value, true);
}
}
@@ -1963,9 +1945,9 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
($expr->var instanceof Expr\Closure || $expr->var instanceof Expr\ArrowFunction)
&& $expr->name instanceof Node\Identifier
&& strtolower($expr->name->name) === 'call'
- && isset($expr->args[0])
+ && isset($expr->getArgs()[0])
) {
- $closureCallScope = $scope->enterClosureCall($scope->getType($expr->args[0]->value));
+ $closureCallScope = $scope->enterClosureCall($scope->getType($expr->getArgs()[0]->value));
}
$result = $this->processExprNode($expr->var, $closureCallScope ?? $scope, $nodeCallback, $context->enterDeep());
@@ -1988,27 +1970,27 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
if ($methodReflection !== null) {
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
$scope,
- $expr->args,
- $methodReflection->getVariants()
+ $expr->getArgs(),
+ $methodReflection->getVariants(),
);
- $methodThrowPoint = $this->getMethodThrowPoint($methodReflection, $expr, $scope);
+ $methodThrowPoint = $this->getMethodThrowPoint($methodReflection, $parametersAcceptor, $expr, $scope);
if ($methodThrowPoint !== null) {
$throwPoints[] = $methodThrowPoint;
}
- } else {
- $throwPoints[] = ThrowPoint::createImplicit($scope, $expr);
}
}
- $result = $this->processArgs($methodReflection, $parametersAcceptor, $expr->args, $scope, $nodeCallback, $context);
+ $result = $this->processArgs($methodReflection, $parametersAcceptor, $expr->getArgs(), $scope, $nodeCallback, $context);
$scope = $result->getScope();
if ($methodReflection !== null) {
$hasSideEffects = $methodReflection->hasSideEffects();
if ($hasSideEffects->yes()) {
$scope = $scope->invalidateExpression($expr->var, true);
- foreach ($expr->args as $arg) {
+ foreach ($expr->getArgs() as $arg) {
$scope = $scope->invalidateExpression($arg->value, true);
}
}
+ } else {
+ $throwPoints[] = ThrowPoint::createImplicit($scope, $expr);
}
$hasYield = $hasYield || $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
@@ -2021,12 +2003,8 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$scope,
$exprResult->hasYield(),
$exprResult->getThrowPoints(),
- static function () use ($scope, $expr): MutatingScope {
- return $scope->filterByTruthyValue($expr);
- },
- static function () use ($scope, $expr): MutatingScope {
- return $scope->filterByFalseyValue($expr);
- }
+ static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
+ static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
);
} elseif ($expr instanceof StaticCall) {
$hasYield = false;
@@ -2072,8 +2050,8 @@ static function () use ($scope, $expr): MutatingScope {
$methodReflection = $classReflection->getMethod($methodName, $scope);
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
$scope,
- $expr->args,
- $methodReflection->getVariants()
+ $expr->getArgs(),
+ $methodReflection->getVariants(),
);
$methodThrowPoint = $this->getStaticMethodThrowPoint($methodReflection, $expr, $scope);
if ($methodThrowPoint !== null) {
@@ -2084,8 +2062,8 @@ static function () use ($scope, $expr): MutatingScope {
&& strtolower($methodName) === 'bind'
) {
$thisType = null;
- if (isset($expr->args[1])) {
- $argType = $scope->getType($expr->args[1]->value);
+ if (isset($expr->getArgs()[1])) {
+ $argType = $scope->getType($expr->getArgs()[1]->value);
if ($argType instanceof NullType) {
$thisType = null;
} else {
@@ -2093,25 +2071,23 @@ static function () use ($scope, $expr): MutatingScope {
}
}
$scopeClass = 'static';
- if (isset($expr->args[2])) {
- $argValue = $expr->args[2]->value;
+ if (isset($expr->getArgs()[2])) {
+ $argValue = $expr->getArgs()[2]->value;
$argValueType = $scope->getType($argValue);
$directClassNames = TypeUtils::getDirectClassNames($argValueType);
if (count($directClassNames) === 1) {
$scopeClass = $directClassNames[0];
$thisType = new ObjectType($scopeClass);
- } elseif (
- $argValue instanceof Expr\ClassConstFetch
- && $argValue->name instanceof Node\Identifier
- && strtolower($argValue->name->name) === 'class'
- && $argValue->class instanceof Name
- ) {
- $scopeClass = $scope->resolveName($argValue->class);
- $thisType = new ObjectType($scopeClass);
} elseif ($argValueType instanceof ConstantStringType) {
$scopeClass = $argValueType->getValue();
$thisType = new ObjectType($scopeClass);
+ } elseif (
+ $argValueType instanceof GenericClassStringType
+ && $argValueType->getGenericType() instanceof TypeWithClassName
+ ) {
+ $scopeClass = $argValueType->getGenericType()->getClassName();
+ $thisType = $argValueType->getGenericType();
}
}
$closureBindScope = $scope->enterClosureBind($thisType, $scopeClass);
@@ -2123,7 +2099,7 @@ static function () use ($scope, $expr): MutatingScope {
$throwPoints[] = ThrowPoint::createImplicit($scope, $expr);
}
}
- $result = $this->processArgs($methodReflection, $parametersAcceptor, $expr->args, $scope, $nodeCallback, $context, $closureBindScope ?? null);
+ $result = $this->processArgs($methodReflection, $parametersAcceptor, $expr->getArgs(), $scope, $nodeCallback, $context, $closureBindScope ?? null);
$scope = $result->getScope();
$scopeFunction = $scope->getFunction();
if (
@@ -2143,7 +2119,7 @@ static function () use ($scope, $expr): MutatingScope {
if ($methodReflection !== null) {
if ($methodReflection->hasSideEffects()->yes()) {
- foreach ($expr->args as $arg) {
+ foreach ($expr->getArgs() as $arg) {
$scope = $scope->invalidateExpression($arg->value, true);
}
}
@@ -2170,12 +2146,8 @@ static function () use ($scope, $expr): MutatingScope {
$scope,
$exprResult->hasYield(),
$exprResult->getThrowPoints(),
- static function () use ($scope, $expr): MutatingScope {
- return $scope->filterByTruthyValue($expr);
- },
- static function () use ($scope, $expr): MutatingScope {
- return $scope->filterByFalseyValue($expr);
- }
+ static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
+ static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
);
} elseif ($expr instanceof StaticPropertyFetch) {
$hasYield = false;
@@ -2276,12 +2248,8 @@ static function () use ($scope, $expr): MutatingScope {
$leftMergedWithRightScope,
$leftResult->hasYield() || $rightResult->hasYield(),
array_merge($leftResult->getThrowPoints(), $rightResult->getThrowPoints()),
- static function () use ($expr, $rightResult): MutatingScope {
- return $rightResult->getScope()->filterByTruthyValue($expr);
- },
- static function () use ($leftMergedWithRightScope, $expr): MutatingScope {
- return $leftMergedWithRightScope->filterByFalseyValue($expr);
- }
+ static fn (): MutatingScope => $rightResult->getScope()->filterByTruthyValue($expr),
+ static fn (): MutatingScope => $leftMergedWithRightScope->filterByFalseyValue($expr),
);
} elseif ($expr instanceof BooleanOr || $expr instanceof BinaryOp\LogicalOr) {
$leftResult = $this->processExprNode($expr->left, $scope, $nodeCallback, $context->enterDeep());
@@ -2294,18 +2262,20 @@ static function () use ($leftMergedWithRightScope, $expr): MutatingScope {
$leftMergedWithRightScope,
$leftResult->hasYield() || $rightResult->hasYield(),
array_merge($leftResult->getThrowPoints(), $rightResult->getThrowPoints()),
- static function () use ($leftMergedWithRightScope, $expr): MutatingScope {
- return $leftMergedWithRightScope->filterByTruthyValue($expr);
- },
- static function () use ($expr, $rightResult): MutatingScope {
- return $rightResult->getScope()->filterByFalseyValue($expr);
- }
+ static fn (): MutatingScope => $leftMergedWithRightScope->filterByTruthyValue($expr),
+ static fn (): MutatingScope => $rightResult->getScope()->filterByFalseyValue($expr),
);
} elseif ($expr instanceof Coalesce) {
$nonNullabilityResult = $this->ensureNonNullability($scope, $expr->left, false);
- if ($expr->left instanceof PropertyFetch || $expr->left instanceof StaticPropertyFetch || $expr->left instanceof Expr\NullsafePropertyFetch) {
- $scope = $nonNullabilityResult->getScope();
+ if ($expr->left instanceof PropertyFetch || $expr->left instanceof Expr\NullsafePropertyFetch) {
+ $scope = $this->lookForEnterVariableAssign($nonNullabilityResult->getScope(), $expr->left->var);
+ } elseif ($expr->left instanceof StaticPropertyFetch) {
+ if ($expr->left->class instanceof Expr) {
+ $scope = $this->lookForEnterVariableAssign($nonNullabilityResult->getScope(), $expr->left->class);
+ } else {
+ $scope = $nonNullabilityResult->getScope();
+ }
} else {
$scope = $this->lookForEnterVariableAssign($nonNullabilityResult->getScope(), $expr->left);
}
@@ -2315,7 +2285,13 @@ static function () use ($expr, $rightResult): MutatingScope {
$scope = $result->getScope();
$scope = $this->revertNonNullability($scope, $nonNullabilityResult->getSpecifiedExpressions());
- if (!$expr->left instanceof PropertyFetch) {
+ if ($expr->left instanceof PropertyFetch || $expr->left instanceof Expr\NullsafePropertyFetch) {
+ $scope = $this->lookForExitVariableAssign($scope, $expr->left->var);
+ } elseif ($expr->left instanceof StaticPropertyFetch) {
+ if ($expr->left->class instanceof Expr) {
+ $scope = $this->lookForExitVariableAssign($scope, $expr->left->class);
+ }
+ } else {
$scope = $this->lookForExitVariableAssign($scope, $expr->left);
}
$result = $this->processExprNode($expr->right, $scope, $nodeCallback, $context->enterDeep());
@@ -2341,7 +2317,6 @@ static function () use ($expr, $rightResult): MutatingScope {
$expr instanceof Expr\BitwiseNot
|| $expr instanceof Cast
|| $expr instanceof Expr\Clone_
- || $expr instanceof Expr\Eval_
|| $expr instanceof Expr\Print_
|| $expr instanceof Expr\UnaryMinus
|| $expr instanceof Expr\UnaryPlus
@@ -2350,6 +2325,13 @@ static function () use ($expr, $rightResult): MutatingScope {
$throwPoints = $result->getThrowPoints();
$hasYield = $result->hasYield();
+ $scope = $result->getScope();
+ } elseif ($expr instanceof Expr\Eval_) {
+ $result = $this->processExprNode($expr->expr, $scope, $nodeCallback, $context->enterDeep());
+ $throwPoints = $result->getThrowPoints();
+ $throwPoints[] = ThrowPoint::createImplicit($scope, $expr);
+ $hasYield = $result->hasYield();
+
$scope = $result->getScope();
} elseif ($expr instanceof Expr\YieldFrom) {
$result = $this->processExprNode($expr->expr, $scope, $nodeCallback, $context->enterDeep());
@@ -2445,10 +2427,10 @@ static function () use ($expr, $rightResult): MutatingScope {
$constructorReflection = $classReflection->getConstructor();
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
$scope,
- $expr->args,
- $constructorReflection->getVariants()
+ $expr->getArgs(),
+ $constructorReflection->getVariants(),
);
- $constructorThrowPoint = $this->getConstructorThrowPoint($constructorReflection, $classReflection, $expr, $expr->class, $expr->args, $scope);
+ $constructorThrowPoint = $this->getConstructorThrowPoint($constructorReflection, $classReflection, $expr, $expr->class, $expr->getArgs(), $scope);
if ($constructorThrowPoint !== null) {
$throwPoints[] = $constructorThrowPoint;
}
@@ -2457,7 +2439,7 @@ static function () use ($expr, $rightResult): MutatingScope {
$throwPoints[] = ThrowPoint::createImplicit($scope, $expr);
}
}
- $result = $this->processArgs($constructorReflection, $parametersAcceptor, $expr->args, $scope, $nodeCallback, $context);
+ $result = $this->processArgs($constructorReflection, $parametersAcceptor, $expr->getArgs(), $scope, $nodeCallback, $context);
$scope = $result->getScope();
$hasYield = $hasYield || $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
@@ -2471,36 +2453,29 @@ static function () use ($expr, $rightResult): MutatingScope {
$scope = $result->getScope();
$hasYield = $result->hasYield();
$throwPoints = [];
- if (
- $expr->var instanceof Variable
- || $expr->var instanceof ArrayDimFetch
- || $expr->var instanceof PropertyFetch
- || $expr->var instanceof StaticPropertyFetch
- ) {
- $newExpr = $expr;
- if ($expr instanceof Expr\PostInc) {
- $newExpr = new Expr\PreInc($expr->var);
- } elseif ($expr instanceof Expr\PostDec) {
- $newExpr = new Expr\PreDec($expr->var);
- }
- if (!$scope->getType($expr->var)->equals($scope->getType($newExpr))) {
- $scope = $this->processAssignVar(
- $scope,
- $expr->var,
- $newExpr,
- static function (): void {
- },
- $context,
- static function (MutatingScope $scope): ExpressionResult {
- return new ExpressionResult($scope, false, []);
- },
- false
- )->getScope();
- } else {
- $scope = $scope->invalidateExpression($expr->var);
- }
+ $newExpr = $expr;
+ if ($expr instanceof Expr\PostInc) {
+ $newExpr = new Expr\PreInc($expr->var);
+ } elseif ($expr instanceof Expr\PostDec) {
+ $newExpr = new Expr\PreDec($expr->var);
}
+
+ $scope = $this->processAssignVar(
+ $scope,
+ $expr->var,
+ $newExpr,
+ static function (Node $node, Scope $scope) use ($nodeCallback): void {
+ if (!$node instanceof PropertyAssignNode) {
+ return;
+ }
+
+ $nodeCallback($node, $scope);
+ },
+ $context,
+ static fn (MutatingScope $scope): ExpressionResult => new ExpressionResult($scope, false, []),
+ false,
+ )->getScope();
} elseif ($expr instanceof Ternary) {
$ternaryCondResult = $this->processExprNode($expr->cond, $scope, $nodeCallback, $context->enterDeep());
$throwPoints = $ternaryCondResult->getThrowPoints();
@@ -2523,12 +2498,8 @@ static function (MutatingScope $scope): ExpressionResult {
$finalScope,
$ternaryCondResult->hasYield(),
$throwPoints,
- static function () use ($finalScope, $expr): MutatingScope {
- return $finalScope->filterByTruthyValue($expr);
- },
- static function () use ($finalScope, $expr): MutatingScope {
- return $finalScope->filterByFalseyValue($expr);
- }
+ static fn (): MutatingScope => $finalScope->filterByTruthyValue($expr),
+ static fn (): MutatingScope => $finalScope->filterByFalseyValue($expr),
);
} elseif ($expr instanceof Expr\Yield_) {
@@ -2566,7 +2537,7 @@ static function () use ($finalScope, $expr): MutatingScope {
}
if (count($arm->conds) === 0) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$filteringExpr = null;
@@ -2593,7 +2564,7 @@ static function () use ($finalScope, $expr): MutatingScope {
$arm->body,
$matchScope->filterByTruthyValue($filteringExpr),
$nodeCallback,
- ExpressionContext::createTopLevel()
+ ExpressionContext::createTopLevel(),
);
$armScope = $armResult->getScope();
$scope = $scope->mergeWith($armScope);
@@ -2608,6 +2579,50 @@ static function () use ($finalScope, $expr): MutatingScope {
$result = $this->processExprNode($expr->expr, $scope, $nodeCallback, ExpressionContext::createDeep());
$throwPoints = $result->getThrowPoints();
$throwPoints[] = ThrowPoint::createExplicit($scope, $scope->getType($expr->expr), $expr, false);
+ } elseif ($expr instanceof FunctionCallableNode) {
+ $throwPoints = [];
+ $hasYield = false;
+ if ($expr->getName() instanceof Expr) {
+ $result = $this->processExprNode($expr->getName(), $scope, $nodeCallback, ExpressionContext::createDeep());
+ $scope = $result->getScope();
+ $hasYield = $result->hasYield();
+ $throwPoints = $result->getThrowPoints();
+ }
+ } elseif ($expr instanceof MethodCallableNode) {
+ $result = $this->processExprNode($expr->getVar(), $scope, $nodeCallback, ExpressionContext::createDeep());
+ $scope = $result->getScope();
+ $hasYield = $result->hasYield();
+ $throwPoints = $result->getThrowPoints();
+ if ($expr->getName() instanceof Expr) {
+ $nameResult = $this->processExprNode($expr->getVar(), $scope, $nodeCallback, ExpressionContext::createDeep());
+ $scope = $nameResult->getScope();
+ $hasYield = $hasYield || $nameResult->hasYield();
+ $throwPoints = array_merge($throwPoints, $nameResult->getThrowPoints());
+ }
+ } elseif ($expr instanceof StaticMethodCallableNode) {
+ $throwPoints = [];
+ $hasYield = false;
+ if ($expr->getClass() instanceof Expr) {
+ $classResult = $this->processExprNode($expr->getClass(), $scope, $nodeCallback, ExpressionContext::createDeep());
+ $scope = $classResult->getScope();
+ $hasYield = $classResult->hasYield();
+ $throwPoints = $classResult->getThrowPoints();
+ }
+ if ($expr->getName() instanceof Expr) {
+ $nameResult = $this->processExprNode($expr->getName(), $scope, $nodeCallback, ExpressionContext::createDeep());
+ $scope = $nameResult->getScope();
+ $hasYield = $hasYield || $nameResult->hasYield();
+ $throwPoints = array_merge($throwPoints, $nameResult->getThrowPoints());
+ }
+ } elseif ($expr instanceof InstantiationCallableNode) {
+ $throwPoints = [];
+ $hasYield = false;
+ if ($expr->getClass() instanceof Expr) {
+ $classResult = $this->processExprNode($expr->getClass(), $scope, $nodeCallback, ExpressionContext::createDeep());
+ $scope = $classResult->getScope();
+ $hasYield = $classResult->hasYield();
+ $throwPoints = $classResult->getThrowPoints();
+ }
} else {
$hasYield = false;
$throwPoints = [];
@@ -2617,12 +2632,8 @@ static function () use ($finalScope, $expr): MutatingScope {
$scope,
$hasYield,
$throwPoints,
- static function () use ($scope, $expr): MutatingScope {
- return $scope->filterByTruthyValue($expr);
- },
- static function () use ($scope, $expr): MutatingScope {
- return $scope->filterByFalseyValue($expr);
- }
+ static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
+ static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
);
}
@@ -2630,7 +2641,7 @@ private function getFunctionThrowPoint(
FunctionReflection $functionReflection,
?ParametersAcceptor $parametersAcceptor,
FuncCall $funcCall,
- MutatingScope $scope
+ MutatingScope $scope,
): ?ThrowPoint
{
foreach ($this->dynamicThrowTypeExtensionProvider->getDynamicFunctionThrowTypeExtensions() as $extension) {
@@ -2646,8 +2657,15 @@ private function getFunctionThrowPoint(
return ThrowPoint::createExplicit($scope, $throwType, $funcCall, false);
}
- if ($functionReflection->getThrowType() !== null) {
- $throwType = $functionReflection->getThrowType();
+ $throwType = $functionReflection->getThrowType();
+ if ($throwType === null && $parametersAcceptor !== null) {
+ $returnType = $parametersAcceptor->getReturnType();
+ if ($returnType instanceof NeverType && $returnType->isExplicit()) {
+ $throwType = new ObjectType(Throwable::class);
+ }
+ }
+
+ if ($throwType !== null) {
if (!$throwType instanceof VoidType) {
return ThrowPoint::createExplicit($scope, $throwType, $funcCall, true);
}
@@ -2667,10 +2685,10 @@ private function getFunctionThrowPoint(
!$functionReflection->isBuiltin()
|| $requiredParameters === null
|| $requiredParameters > 0
- || count($funcCall->args) > 0
+ || count($funcCall->getArgs()) > 0
) {
$functionReturnedType = $scope->getType($funcCall);
- if (!(new ObjectType(\Throwable::class))->isSuperTypeOf($functionReturnedType)->yes()) {
+ if (!(new ObjectType(Throwable::class))->isSuperTypeOf($functionReturnedType)->yes()) {
return ThrowPoint::createImplicit($scope, $funcCall);
}
}
@@ -2679,7 +2697,7 @@ private function getFunctionThrowPoint(
return null;
}
- private function getMethodThrowPoint(MethodReflection $methodReflection, MethodCall $methodCall, MutatingScope $scope): ?ThrowPoint
+ private function getMethodThrowPoint(MethodReflection $methodReflection, ParametersAcceptor $parametersAcceptor, MethodCall $methodCall, MutatingScope $scope): ?ThrowPoint
{
foreach ($this->dynamicThrowTypeExtensionProvider->getDynamicMethodThrowTypeExtensions() as $extension) {
if (!$extension->isMethodSupported($methodReflection)) {
@@ -2694,14 +2712,21 @@ private function getMethodThrowPoint(MethodReflection $methodReflection, MethodC
return ThrowPoint::createExplicit($scope, $throwType, $methodCall, false);
}
- if ($methodReflection->getThrowType() !== null) {
- $throwType = $methodReflection->getThrowType();
+ $throwType = $methodReflection->getThrowType();
+ if ($throwType === null) {
+ $returnType = $parametersAcceptor->getReturnType();
+ if ($returnType instanceof NeverType && $returnType->isExplicit()) {
+ $throwType = new ObjectType(Throwable::class);
+ }
+ }
+
+ if ($throwType !== null) {
if (!$throwType instanceof VoidType) {
return ThrowPoint::createExplicit($scope, $throwType, $methodCall, true);
}
} elseif ($this->implicitThrows) {
$methodReturnedType = $scope->getType($methodCall);
- if (!(new ObjectType(\Throwable::class))->isSuperTypeOf($methodReturnedType)->yes()) {
+ if (!(new ObjectType(Throwable::class))->isSuperTypeOf($methodReturnedType)->yes()) {
return ThrowPoint::createImplicit($scope, $methodCall);
}
}
@@ -2734,7 +2759,7 @@ private function getConstructorThrowPoint(MethodReflection $constructorReflectio
return ThrowPoint::createExplicit($scope, $throwType, $new, true);
}
} elseif ($this->implicitThrows) {
- if ($classReflection->getName() !== \Throwable::class && !$classReflection->isSubclassOf(\Throwable::class)) {
+ if ($classReflection->getName() !== Throwable::class && !$classReflection->isSubclassOf(Throwable::class)) {
return ThrowPoint::createImplicit($scope, $methodCall);
}
}
@@ -2764,7 +2789,7 @@ private function getStaticMethodThrowPoint(MethodReflection $methodReflection, S
}
} elseif ($this->implicitThrows) {
$methodReturnedType = $scope->getType($methodCall);
- if (!(new ObjectType(\Throwable::class))->isSuperTypeOf($methodReturnedType)->yes()) {
+ if (!(new ObjectType(Throwable::class))->isSuperTypeOf($methodReturnedType)->yes()) {
return ThrowPoint::createImplicit($scope, $methodCall);
}
}
@@ -2773,7 +2798,6 @@ private function getStaticMethodThrowPoint(MethodReflection $methodReflection, S
}
/**
- * @param Expr $expr
* @return string[]
*/
private function getAssignedVariables(Expr $expr): array
@@ -2799,20 +2823,21 @@ private function getAssignedVariables(Expr $expr): array
return $names;
}
+ if ($expr instanceof ArrayDimFetch) {
+ return $this->getAssignedVariables($expr->var);
+ }
+
return [];
}
/**
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @param Expr $expr
- * @param MutatingScope $scope
- * @param ExpressionContext $context
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
private function callNodeCallbackWithExpression(
callable $nodeCallback,
Expr $expr,
MutatingScope $scope,
- ExpressionContext $context
+ ExpressionContext $context,
): void
{
if ($context->isDeep()) {
@@ -2822,19 +2847,14 @@ private function callNodeCallbackWithExpression(
}
/**
- * @param \PhpParser\Node\Expr\Closure $expr
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @param ExpressionContext $context
- * @param Type|null $passedToType
- * @return \PHPStan\Analyser\ExpressionResult
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
private function processClosureNode(
Expr\Closure $expr,
MutatingScope $scope,
callable $nodeCallback,
ExpressionContext $context,
- ?Type $passedToType
+ ?Type $passedToType,
): ExpressionResult
{
foreach ($expr->params as $param) {
@@ -2844,6 +2864,13 @@ private function processClosureNode(
$byRefUses = [];
if ($passedToType !== null && !$passedToType->isCallable()->no()) {
+ if ($passedToType instanceof UnionType) {
+ $passedToType = TypeCombinator::union(...array_filter(
+ $passedToType->getTypes(),
+ static fn (Type $type) => $type->isCallable()->yes(),
+ ));
+ }
+
$callableParameters = null;
$acceptors = $passedToType->getCallableParametersAcceptors($scope);
if (count($acceptors) === 1) {
@@ -2896,7 +2923,7 @@ private function processClosureNode(
$gatheredReturnStatements = [];
$gatheredYieldStatements = [];
- $closureStmtsCallback = static function (\PhpParser\Node $node, Scope $scope) use ($nodeCallback, &$gatheredReturnStatements, &$gatheredYieldStatements, &$closureScope): void {
+ $closureStmtsCallback = static function (Node $node, Scope $scope) use ($nodeCallback, &$gatheredReturnStatements, &$gatheredYieldStatements, &$closureScope): void {
$nodeCallback($node, $scope);
if ($scope->getAnonymousFunctionReflection() !== $closureScope->getAnonymousFunctionReflection()) {
return;
@@ -2916,7 +2943,7 @@ private function processClosureNode(
$expr,
$gatheredReturnStatements,
$gatheredYieldStatements,
- $statementResult
+ $statementResult,
), $closureScope);
return new ExpressionResult($scope, false, []);
@@ -2937,6 +2964,9 @@ private function processClosureNode(
if ($closureScope->equals($prevScope)) {
break;
}
+ if ($count >= self::GENERALIZE_AFTER_ITERATION) {
+ $closureScope = $prevScope->generalizeWith($closureScope);
+ }
$count++;
} while ($count < self::LOOP_SCOPE_ITERATIONS);
@@ -2945,26 +2975,21 @@ private function processClosureNode(
$expr,
$gatheredReturnStatements,
$gatheredYieldStatements,
- $statementResult
+ $statementResult,
), $closureScope);
return new ExpressionResult($scope->processClosureScope($closureScope, null, $byRefUses), false, []);
}
/**
- * @param \PhpParser\Node\Expr\ArrowFunction $expr
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @param ExpressionContext $context
- * @param Type|null $passedToType
- * @return \PHPStan\Analyser\ExpressionResult
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
private function processArrowFunctionNode(
Expr\ArrowFunction $expr,
MutatingScope $scope,
callable $nodeCallback,
ExpressionContext $context,
- ?Type $passedToType
+ ?Type $passedToType,
): ExpressionResult
{
foreach ($expr->params as $param) {
@@ -2975,6 +3000,13 @@ private function processArrowFunctionNode(
}
if ($passedToType !== null && !$passedToType->isCallable()->no()) {
+ if ($passedToType instanceof UnionType) {
+ $passedToType = TypeCombinator::union(...array_filter(
+ $passedToType->getTypes(),
+ static fn (Type $type) => $type->isCallable()->yes(),
+ ));
+ }
+
$callableParameters = null;
$acceptors = $passedToType->getCallableParametersAcceptors($scope);
if (count($acceptors) === 1) {
@@ -2991,66 +3023,19 @@ private function processArrowFunctionNode(
return new ExpressionResult($scope, false, []);
}
- private function lookForArrayDestructuringArray(MutatingScope $scope, Expr $expr, Type $valueType): MutatingScope
- {
- if ($expr instanceof Array_ || $expr instanceof List_) {
- foreach ($expr->items as $key => $item) {
- /** @var \PhpParser\Node\Expr\ArrayItem|null $itemValue */
- $itemValue = $item;
- if ($itemValue === null) {
- continue;
- }
-
- $keyType = $itemValue->key === null ? new ConstantIntegerType($key) : $scope->getType($itemValue->key);
- $scope = $this->specifyItemFromArrayDestructuring($scope, $itemValue, $valueType, $keyType);
- }
- } elseif ($expr instanceof Variable && is_string($expr->name)) {
- $scope = $scope->assignVariable($expr->name, new MixedType());
- } elseif ($expr instanceof ArrayDimFetch && $expr->var instanceof Variable && is_string($expr->var->name)) {
- $scope = $scope->assignVariable($expr->var->name, new MixedType());
- }
-
- return $scope;
- }
-
- private function specifyItemFromArrayDestructuring(MutatingScope $scope, ArrayItem $arrayItem, Type $valueType, Type $keyType): MutatingScope
- {
- $type = $valueType->getOffsetValueType($keyType);
-
- $itemNode = $arrayItem->value;
- if ($itemNode instanceof Variable && is_string($itemNode->name)) {
- $scope = $scope->assignVariable($itemNode->name, $type);
- } elseif ($itemNode instanceof ArrayDimFetch && $itemNode->var instanceof Variable && is_string($itemNode->var->name)) {
- $currentType = $scope->hasVariableType($itemNode->var->name)->no()
- ? new ConstantArrayType([], [])
- : $scope->getVariableType($itemNode->var->name);
- $dimType = null;
- if ($itemNode->dim !== null) {
- $dimType = $scope->getType($itemNode->dim);
- }
- $scope = $scope->assignVariable($itemNode->var->name, $currentType->setOffsetValueType($dimType, $type));
- } else {
- $scope = $this->lookForArrayDestructuringArray($scope, $itemNode, $type);
- }
-
- return $scope;
- }
-
/**
- * @param \PhpParser\Node\Param $param
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
private function processParamNode(
Node\Param $param,
MutatingScope $scope,
- callable $nodeCallback
+ callable $nodeCallback,
): void
{
foreach ($param->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
foreach ($attr->args as $arg) {
- $nodeCallback($arg->value, $scope);
+ $this->processExprNode($arg->value, $scope, $nodeCallback, ExpressionContext::createDeep());
}
}
}
@@ -3066,14 +3051,9 @@ private function processParamNode(
}
/**
- * @param \PHPStan\Reflection\MethodReflection|\PHPStan\Reflection\FunctionReflection|null $calleeReflection
- * @param ParametersAcceptor|null $parametersAcceptor
- * @param \PhpParser\Node\Arg[] $args
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @param ExpressionContext $context
- * @param \PHPStan\Analyser\MutatingScope|null $closureBindScope
- * @return \PHPStan\Analyser\ExpressionResult
+ * @param MethodReflection|FunctionReflection|null $calleeReflection
+ * @param Node\Arg[] $args
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
private function processArgs(
$calleeReflection,
@@ -3082,7 +3062,7 @@ private function processArgs(
MutatingScope $scope,
callable $nodeCallback,
ExpressionContext $context,
- ?MutatingScope $closureBindScope = null
+ ?MutatingScope $closureBindScope = null,
): ExpressionResult
{
if ($parametersAcceptor !== null) {
@@ -3114,47 +3094,6 @@ private function processArgs(
$scope = $scope->assignVariable($argValue->name, new MixedType());
}
}
-
- if ($calleeReflection instanceof FunctionReflection) {
- if (
- $i === 0
- && $calleeReflection->getName() === 'array_map'
- && isset($args[1])
- ) {
- $parameterType = new CallableType([
- new DummyParameter('item', $scope->getType($args[1]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null),
- ], new MixedType(), false);
- }
-
- if (
- $i === 1
- && $calleeReflection->getName() === 'array_filter'
- && isset($args[0])
- ) {
- if (isset($args[2])) {
- $mode = $scope->getType($args[2]->value);
- if ($mode instanceof ConstantIntegerType) {
- if ($mode->getValue() === ARRAY_FILTER_USE_KEY) {
- $arrayFilterParameters = [
- new DummyParameter('key', $scope->getType($args[0]->value)->getIterableKeyType(), false, PassedByReference::createNo(), false, null),
- ];
- } elseif ($mode->getValue() === ARRAY_FILTER_USE_BOTH) {
- $arrayFilterParameters = [
- new DummyParameter('item', $scope->getType($args[0]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null),
- new DummyParameter('key', $scope->getType($args[0]->value)->getIterableKeyType(), false, PassedByReference::createNo(), false, null),
- ];
- }
- }
- }
- $parameterType = new CallableType(
- $arrayFilterParameters ?? [
- new DummyParameter('item', $scope->getType($args[0]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null),
- ],
- new MixedType(),
- false
- );
- }
- }
}
$originalScope = $scope;
@@ -3190,14 +3129,8 @@ private function processArgs(
}
/**
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param \PhpParser\Node\Expr $var
- * @param \PhpParser\Node\Expr $assignedExpr
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
- * @param ExpressionContext $context
- * @param \Closure(MutatingScope $scope): ExpressionResult $processExprCallback
- * @param bool $enterExpressionAssign
- * @return ExpressionResult
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
+ * @param Closure(MutatingScope $scope): ExpressionResult $processExprCallback
*/
private function processAssignVar(
MutatingScope $scope,
@@ -3205,13 +3138,14 @@ private function processAssignVar(
Expr $assignedExpr,
callable $nodeCallback,
ExpressionContext $context,
- \Closure $processExprCallback,
- bool $enterExpressionAssign
+ Closure $processExprCallback,
+ bool $enterExpressionAssign,
): ExpressionResult
{
$nodeCallback($var, $enterExpressionAssign ? $scope->enterExpressionAssign($var) : $scope);
$hasYield = false;
$throwPoints = [];
+ $isAssignOp = $assignedExpr instanceof Expr\AssignOp && !$enterExpressionAssign;
if ($var instanceof Variable && is_string($var->name)) {
$result = $processExprCallback($scope);
$hasYield = $result->hasYield();
@@ -3238,20 +3172,30 @@ private function processAssignVar(
} elseif ($var instanceof ArrayDimFetch) {
$dimExprStack = [];
$originalVar = $var;
+ $assignedPropertyExpr = $assignedExpr;
while ($var instanceof ArrayDimFetch) {
+ $varForSetOffsetValue = $var->var;
+ if ($varForSetOffsetValue instanceof PropertyFetch || $varForSetOffsetValue instanceof StaticPropertyFetch) {
+ $varForSetOffsetValue = new OriginalPropertyTypeExpr($varForSetOffsetValue);
+ }
+ $assignedPropertyExpr = new SetOffsetValueTypeExpr(
+ $varForSetOffsetValue,
+ $var->dim,
+ $assignedPropertyExpr,
+ );
$dimExprStack[] = $var->dim;
$var = $var->var;
}
// 1. eval root expr
- if ($enterExpressionAssign && $var instanceof Variable) {
+ if ($enterExpressionAssign) {
$scope = $scope->enterExpressionAssign($var);
}
$result = $this->processExprNode($var, $scope, $nodeCallback, $context->enterDeep());
$hasYield = $result->hasYield();
$throwPoints = $result->getThrowPoints();
$scope = $result->getScope();
- if ($enterExpressionAssign && $var instanceof Variable) {
+ if ($enterExpressionAssign) {
$scope = $scope->exitExpressionAssign($var);
}
@@ -3288,39 +3232,43 @@ private function processAssignVar(
$scope = $result->getScope();
$varType = $scope->getType($var);
- if (!(new ObjectType(\ArrayAccess::class))->isSuperTypeOf($varType)->yes()) {
- // 4. compose types
- if ($varType instanceof ErrorType) {
- $varType = new ConstantArrayType([], []);
- }
- $offsetValueType = $varType;
- $offsetValueTypeStack = [$offsetValueType];
- foreach (array_slice($offsetTypes, 0, -1) as $offsetType) {
- if ($offsetType === null) {
- $offsetValueType = new ConstantArrayType([], []);
- } else {
- $offsetValueType = $offsetValueType->getOffsetValueType($offsetType);
- if ($offsetValueType instanceof ErrorType) {
- $offsetValueType = new ConstantArrayType([], []);
- }
- }
+ // 4. compose types
+ if ($varType instanceof ErrorType) {
+ $varType = new ConstantArrayType([], []);
+ }
+ $offsetValueType = $varType;
+ $offsetValueTypeStack = [$offsetValueType];
+ foreach (array_slice($offsetTypes, 0, -1) as $offsetType) {
+ if ($offsetType === null) {
+ $offsetValueType = new ConstantArrayType([], []);
- $offsetValueTypeStack[] = $offsetValueType;
+ } else {
+ $offsetValueType = $offsetValueType->getOffsetValueType($offsetType);
+ if ($offsetValueType instanceof ErrorType) {
+ $offsetValueType = new ConstantArrayType([], []);
+ }
}
- foreach (array_reverse($offsetTypes) as $i => $offsetType) {
- /** @var Type $offsetValueType */
- $offsetValueType = array_pop($offsetValueTypeStack);
- $valueToWrite = $offsetValueType->setOffsetValueType($offsetType, $valueToWrite, $i === 0);
- }
+ $offsetValueTypeStack[] = $offsetValueType;
+ }
+ foreach (array_reverse($offsetTypes) as $i => $offsetType) {
+ /** @var Type $offsetValueType */
+ $offsetValueType = array_pop($offsetValueTypeStack);
+ $valueToWrite = $offsetValueType->setOffsetValueType($offsetType, $valueToWrite, $i === 0);
+ }
+
+ if (!(new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->yes()) {
if ($var instanceof Variable && is_string($var->name)) {
$scope = $scope->assignVariable($var->name, $valueToWrite);
} else {
+ if ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {
+ $nodeCallback(new PropertyAssignNode($var, $assignedPropertyExpr, $isAssignOp), $scope);
+ }
$scope = $scope->assignExpression(
$var,
- $valueToWrite
+ $valueToWrite,
);
}
@@ -3329,10 +3277,24 @@ private function processAssignVar(
if (!$originalValueToWrite->isSuperTypeOf($currentVarType)->yes()) {
$scope = $scope->assignExpression(
$originalVar,
- $originalValueToWrite
+ $originalValueToWrite,
);
}
}
+ } else {
+ if ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {
+ $nodeCallback(new PropertyAssignNode($var, $assignedPropertyExpr, $isAssignOp), $scope);
+ }
+ }
+
+ if (!(new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->no()) {
+ $throwPoints = array_merge($throwPoints, $this->processExprNode(
+ new MethodCall($var, 'offsetSet'),
+ $scope,
+ static function (): void {
+ },
+ $context,
+ )->getThrowPoints());
}
} elseif ($var instanceof PropertyFetch) {
$objectResult = $this->processExprNode($var->var, $scope, $nodeCallback, $context);
@@ -3358,16 +3320,37 @@ private function processAssignVar(
$propertyHolderType = $scope->getType($var->var);
if ($propertyName !== null && $propertyHolderType->hasProperty($propertyName)->yes()) {
$propertyReflection = $propertyHolderType->getProperty($propertyName, $scope);
+ $assignedExprType = $scope->getType($assignedExpr);
+ $nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
if ($propertyReflection->canChangeTypeAfterAssignment()) {
- $scope = $scope->assignExpression($var, $scope->getType($assignedExpr));
+ $scope = $scope->assignExpression($var, $assignedExprType);
+ }
+ $declaringClass = $propertyReflection->getDeclaringClass();
+ if (
+ $declaringClass->hasNativeProperty($propertyName)
+ && !$declaringClass->getNativeProperty($propertyName)->getNativeType()->accepts($assignedExprType, true)->yes()
+ ) {
+ $throwPoints[] = ThrowPoint::createExplicit($scope, new ObjectType(TypeError::class), $assignedExpr, false);
}
} else {
// fallback
- $scope = $scope->assignExpression($var, $scope->getType($assignedExpr));
+ $assignedExprType = $scope->getType($assignedExpr);
+ $nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
+ $scope = $scope->assignExpression($var, $assignedExprType);
+ // simulate dynamic property assign by __set to get throw points
+ if (!$propertyHolderType->hasMethod('__set')->no()) {
+ $throwPoints = array_merge($throwPoints, $this->processExprNode(
+ new MethodCall($var->var, '__set'),
+ $scope,
+ static function (): void {
+ },
+ $context,
+ )->getThrowPoints());
+ }
}
} elseif ($var instanceof Expr\StaticPropertyFetch) {
- if ($var->class instanceof \PhpParser\Node\Name) {
+ if ($var->class instanceof Node\Name) {
$propertyHolderType = $scope->resolveTypeByName($var->class);
} else {
$this->processExprNode($var->class, $scope, $nodeCallback, $context);
@@ -3393,12 +3376,53 @@ private function processAssignVar(
if ($propertyName !== null) {
$propertyReflection = $scope->getPropertyReflection($propertyHolderType, $propertyName);
+ $assignedExprType = $scope->getType($assignedExpr);
+ $nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
if ($propertyReflection !== null && $propertyReflection->canChangeTypeAfterAssignment()) {
- $scope = $scope->assignExpression($var, $scope->getType($assignedExpr));
+ $scope = $scope->assignExpression($var, $assignedExprType);
}
} else {
// fallback
- $scope = $scope->assignExpression($var, $scope->getType($assignedExpr));
+ $assignedExprType = $scope->getType($assignedExpr);
+ $nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
+ $scope = $scope->assignExpression($var, $assignedExprType);
+ }
+ } elseif ($var instanceof List_ || $var instanceof Array_) {
+ $result = $processExprCallback($scope);
+ $hasYield = $result->hasYield();
+ $throwPoints = array_merge($throwPoints, $result->getThrowPoints());
+ $scope = $result->getScope();
+ foreach ($var->items as $i => $arrayItem) {
+ if ($arrayItem === null) {
+ continue;
+ }
+
+ $itemScope = $scope;
+ if ($arrayItem->value instanceof ArrayDimFetch && $arrayItem->value->dim === null) {
+ $itemScope = $itemScope->enterExpressionAssign($arrayItem->value);
+ }
+ $itemScope = $this->lookForEnterVariableAssign($itemScope, $arrayItem->value);
+ $itemResult = $this->processExprNode($arrayItem, $itemScope, $nodeCallback, $context->enterDeep());
+ $hasYield = $hasYield || $itemResult->hasYield();
+ $throwPoints = array_merge($throwPoints, $itemResult->getThrowPoints());
+
+ if ($arrayItem->key === null) {
+ $dimExpr = new Node\Scalar\LNumber($i);
+ } else {
+ $dimExpr = $arrayItem->key;
+ }
+ $result = $this->processAssignVar(
+ $scope,
+ $arrayItem->value,
+ new GetOffsetValueTypeExpr($assignedExpr, $dimExpr),
+ $nodeCallback,
+ $context,
+ static fn (MutatingScope $scope): ExpressionResult => new ExpressionResult($scope, false, []),
+ $enterExpressionAssign,
+ );
+ $scope = $result->getScope();
+ $hasYield = $hasYield || $result->hasYield();
+ $throwPoints = array_merge($throwPoints, $result->getThrowPoints());
}
}
@@ -3415,11 +3439,7 @@ private function unwrapAssign(Expr $expr): Expr
}
/**
- * @param Scope $scope
- * @param string $variableName
* @param array $conditionalExpressions
- * @param SpecifiedTypes $specifiedTypes
- * @param Type $variableType
* @return array
*/
private function processSureTypesForConditionalExpressionsAfterAssign(Scope $scope, string $variableName, array $conditionalExpressions, SpecifiedTypes $specifiedTypes, Type $variableType): array
@@ -3439,7 +3459,7 @@ private function processSureTypesForConditionalExpressionsAfterAssign(Scope $sco
$conditionalExpressions[$exprString][] = new ConditionalExpressionHolder([
'$' . $variableName => $variableType,
], VariableTypeHolder::createYes(
- TypeCombinator::intersect($scope->getType($expr), $exprType)
+ TypeCombinator::intersect($scope->getType($expr), $exprType),
));
}
@@ -3447,11 +3467,7 @@ private function processSureTypesForConditionalExpressionsAfterAssign(Scope $sco
}
/**
- * @param Scope $scope
- * @param string $variableName
* @param array $conditionalExpressions
- * @param SpecifiedTypes $specifiedTypes
- * @param Type $variableType
* @return array
*/
private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $scope, string $variableName, array $conditionalExpressions, SpecifiedTypes $specifiedTypes, Type $variableType): array
@@ -3471,7 +3487,7 @@ private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $
$conditionalExpressions[$exprString][] = new ConditionalExpressionHolder([
'$' . $variableName => $variableType,
], VariableTypeHolder::createYes(
- TypeCombinator::remove($scope->getType($expr), $exprType)
+ TypeCombinator::remove($scope->getType($expr), $exprType),
));
}
@@ -3493,7 +3509,7 @@ private function processStmtVarAnnotation(MutatingScope $scope, Node\Stmt $stmt,
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
$function !== null ? $function->getName() : null,
- $comment->getText()
+ $comment->getText(),
);
$assignedVariable = null;
@@ -3541,11 +3557,7 @@ private function processStmtVarAnnotation(MutatingScope $scope, Node\Stmt $stmt,
}
/**
- * @param MutatingScope $scope
* @param array $variableNames
- * @param Node $node
- * @param bool $changed
- * @return MutatingScope
*/
private function processVarAnnotation(MutatingScope $scope, array $variableNames, Node $node, bool &$changed = false): MutatingScope
{
@@ -3561,7 +3573,7 @@ private function processVarAnnotation(MutatingScope $scope, array $variableNames
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
$function !== null ? $function->getName() : null,
- $comment->getText()
+ $comment->getText(),
);
foreach ($resolvedPhpDoc->getVarTags() as $key => $varTag) {
$varTags[$key] = $varTag;
@@ -3597,25 +3609,41 @@ private function enterForeach(MutatingScope $scope, Foreach_ $stmt): MutatingSco
$scope = $this->processVarAnnotation($scope, [$stmt->expr->name], $stmt);
}
$iterateeType = $scope->getType($stmt->expr);
- $vars = [];
if ($stmt->valueVar instanceof Variable && is_string($stmt->valueVar->name)) {
+ $keyVarName = null;
+ if ($stmt->keyVar !== null
+ && $stmt->keyVar instanceof Variable
+ && is_string($stmt->keyVar->name)
+ ) {
+ $keyVarName = $stmt->keyVar->name;
+ }
$scope = $scope->enterForeach(
$stmt->expr,
$stmt->valueVar->name,
- $stmt->keyVar !== null
- && $stmt->keyVar instanceof Variable
- && is_string($stmt->keyVar->name)
- ? $stmt->keyVar->name
- : null
+ $keyVarName,
);
- $vars[] = $stmt->valueVar->name;
- }
-
- if (
- $stmt->keyVar instanceof Variable && is_string($stmt->keyVar->name)
- ) {
- $scope = $scope->enterForeachKey($stmt->expr, $stmt->keyVar->name);
- $vars[] = $stmt->keyVar->name;
+ $vars = [$stmt->valueVar->name];
+ if ($keyVarName !== null) {
+ $vars[] = $keyVarName;
+ }
+ } else {
+ $scope = $this->processAssignVar(
+ $scope,
+ $stmt->valueVar,
+ new GetIterableValueTypeExpr($stmt->expr),
+ static function (): void {
+ },
+ ExpressionContext::createDeep(),
+ static fn (MutatingScope $scope): ExpressionResult => new ExpressionResult($scope, false, []),
+ true,
+ )->getScope();
+ $vars = $this->getAssignedVariables($stmt->valueVar);
+ if (
+ $stmt->keyVar instanceof Variable && is_string($stmt->keyVar->name)
+ ) {
+ $scope = $scope->enterForeachKey($stmt->expr, $stmt->keyVar->name);
+ $vars[] = $stmt->keyVar->name;
+ }
}
if (
@@ -3634,26 +3662,15 @@ private function enterForeach(MutatingScope $scope, Foreach_ $stmt): MutatingSco
$scope = $scope->addConditionalExpressions(
'$' . $stmt->valueVar->name,
- $conditionalHolders
+ $conditionalHolders,
);
}
- if ($stmt->valueVar instanceof List_ || $stmt->valueVar instanceof Array_) {
- $exprType = $scope->getType($stmt->expr);
- $itemType = $exprType->getIterableValueType();
- $scope = $this->lookForArrayDestructuringArray($scope, $stmt->valueVar, $itemType);
- $vars = array_merge($vars, $this->getAssignedVariables($stmt->valueVar));
- }
-
- $scope = $this->processVarAnnotation($scope, $vars, $stmt);
-
- return $scope;
+ return $this->processVarAnnotation($scope, $vars, $stmt);
}
/**
- * @param \PhpParser\Node\Stmt\TraitUse $node
- * @param MutatingScope $classScope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
private function processTraitUse(Node\Stmt\TraitUse $node, MutatingScope $classScope, callable $nodeCallback): void
{
@@ -3664,7 +3681,7 @@ private function processTraitUse(Node\Stmt\TraitUse $node, MutatingScope $classS
}
$traitReflection = $this->reflectionProvider->getClass($traitName);
$traitFileName = $traitReflection->getFileName();
- if ($traitFileName === false) {
+ if ($traitFileName === null) {
continue; // trait from eval or from PHP itself
}
$fileName = $this->fileHelper->normalizePath($traitFileName);
@@ -3672,21 +3689,47 @@ private function processTraitUse(Node\Stmt\TraitUse $node, MutatingScope $classS
continue;
}
$parserNodes = $this->parser->parseFile($fileName);
- $this->processNodesForTraitUse($parserNodes, $traitReflection, $classScope, $nodeCallback);
+ $this->processNodesForTraitUse($parserNodes, $traitReflection, $classScope, $node->adaptations, $nodeCallback);
}
}
/**
- * @param \PhpParser\Node[]|\PhpParser\Node|scalar $node
- * @param ClassReflection $traitReflection
- * @param \PHPStan\Analyser\MutatingScope $scope
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
+ * @param Node[]|Node|scalar $node
+ * @param Node\Stmt\TraitUseAdaptation[] $adaptations
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
- private function processNodesForTraitUse($node, ClassReflection $traitReflection, MutatingScope $scope, callable $nodeCallback): void
+ private function processNodesForTraitUse($node, ClassReflection $traitReflection, MutatingScope $scope, array $adaptations, callable $nodeCallback): void
{
if ($node instanceof Node) {
if ($node instanceof Node\Stmt\Trait_ && $traitReflection->getName() === (string) $node->namespacedName && $traitReflection->getNativeReflection()->getStartLine() === $node->getStartLine()) {
- $this->processStmtNodes($node, $node->stmts, $scope->enterTrait($traitReflection), $nodeCallback);
+ $methodModifiers = [];
+ foreach ($adaptations as $adaptation) {
+ if (!$adaptation instanceof Node\Stmt\TraitUseAdaptation\Alias) {
+ continue;
+ }
+
+ if ($adaptation->newModifier === null) {
+ continue;
+ }
+
+ $methodModifiers[$adaptation->method->toLowerString()] = $adaptation->newModifier;
+ }
+
+ $stmts = $node->stmts;
+ foreach ($stmts as $i => $stmt) {
+ if (!$stmt instanceof Node\Stmt\ClassMethod) {
+ continue;
+ }
+ $methodName = $stmt->name->toLowerString();
+ if (!array_key_exists($methodName, $methodModifiers)) {
+ continue;
+ }
+
+ $methodAst = clone $stmt;
+ $methodAst->flags = ($methodAst->flags & ~ Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK) | $methodModifiers[$methodName];
+ $stmts[$i] = $methodAst;
+ }
+ $this->processStmtNodes($node, $stmts, $scope->enterTrait($traitReflection), $nodeCallback);
return;
}
if ($node instanceof Node\Stmt\ClassLike) {
@@ -3697,18 +3740,16 @@ private function processNodesForTraitUse($node, ClassReflection $traitReflection
}
foreach ($node->getSubNodeNames() as $subNodeName) {
$subNode = $node->{$subNodeName};
- $this->processNodesForTraitUse($subNode, $traitReflection, $scope, $nodeCallback);
+ $this->processNodesForTraitUse($subNode, $traitReflection, $scope, $adaptations, $nodeCallback);
}
} elseif (is_array($node)) {
foreach ($node as $subNode) {
- $this->processNodesForTraitUse($subNode, $traitReflection, $scope, $nodeCallback);
+ $this->processNodesForTraitUse($subNode, $traitReflection, $scope, $adaptations, $nodeCallback);
}
}
}
/**
- * @param Scope $scope
- * @param Node\FunctionLike $functionLike
* @return array{TemplateTypeMap, Type[], ?Type, ?Type, ?string, bool, bool, bool, bool|null}
*/
public function getPhpDocs(Scope $scope, Node\FunctionLike $functionLike): array
@@ -3734,12 +3775,12 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike $functionLike): array
if ($functionLike instanceof Node\Stmt\ClassMethod) {
if (!$scope->isInClass()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$functionName = $functionLike->name->name;
$positionalParameterNames = array_map(static function (Node\Param $param): string {
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return $param->var->name;
@@ -3750,7 +3791,7 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike $functionLike): array
$scope->getClassReflection(),
$trait,
$functionLike->name->name,
- $positionalParameterNames
+ $positionalParameterNames,
);
if ($functionLike->name->toLowerString() === '__construct') {
@@ -3767,7 +3808,7 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike $functionLike): array
!$param->var instanceof Variable
|| !is_string($param->var->name)
) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$paramPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
@@ -3775,7 +3816,7 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike $functionLike): array
$class,
$trait,
'__construct',
- $param->getDocComment()->getText()
+ $param->getDocComment()->getText(),
);
$varTags = $paramPhpDoc->getVarTags();
if (isset($varTags[0]) && count($varTags) === 1) {
@@ -3799,7 +3840,7 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike $functionLike): array
$class,
$trait,
$functionName,
- $docComment
+ $docComment,
);
}
diff --git a/src/Analyser/NullsafeOperatorHelper.php b/src/Analyser/NullsafeOperatorHelper.php
index 5c4ce696f9..9b34346264 100644
--- a/src/Analyser/NullsafeOperatorHelper.php
+++ b/src/Analyser/NullsafeOperatorHelper.php
@@ -3,10 +3,25 @@
namespace PHPStan\Analyser;
use PhpParser\Node\Expr;
+use PHPStan\Type\TypeCombinator;
class NullsafeOperatorHelper
{
+ public static function getNullsafeShortcircuitedExprRespectingScope(Scope $scope, Expr $expr): Expr
+ {
+ if (!TypeCombinator::containsNull($scope->getType($expr))) {
+ // We're in most likely in context of a null-safe operator ($scope->moreSpecificType is defined for $expr)
+ // Modifying the expression would not bring any value or worse ruin the context information
+ return $expr;
+ }
+
+ return self::getNullsafeShortcircuitedExpr($expr);
+ }
+
+ /**
+ * @internal Use NullsafeOperatorHelper::getNullsafeShortcircuitedExprRespectingScope
+ */
public static function getNullsafeShortcircuitedExpr(Expr $expr): Expr
{
if ($expr instanceof Expr\NullsafeMethodCall) {
@@ -19,7 +34,7 @@ public static function getNullsafeShortcircuitedExpr(Expr $expr): Expr
return $expr;
}
- return new Expr\MethodCall($var, $expr->name, $expr->args);
+ return new Expr\MethodCall($var, $expr->name, $expr->getArgs());
}
if ($expr instanceof Expr\StaticCall && $expr->class instanceof Expr) {
@@ -28,7 +43,7 @@ public static function getNullsafeShortcircuitedExpr(Expr $expr): Expr
return $expr;
}
- return new Expr\StaticCall($class, $expr->name, $expr->args);
+ return new Expr\StaticCall($class, $expr->name, $expr->getArgs());
}
if ($expr instanceof Expr\ArrayDimFetch) {
diff --git a/src/Analyser/ResultCache/ResultCache.php b/src/Analyser/ResultCache/ResultCache.php
index d18cb4a449..c4f5452802 100644
--- a/src/Analyser/ResultCache/ResultCache.php
+++ b/src/Analyser/ResultCache/ResultCache.php
@@ -8,51 +8,23 @@
class ResultCache
{
- private bool $fullAnalysis;
-
- /** @var string[] */
- private array $filesToAnalyse;
-
- private int $lastFullAnalysisTime;
-
- /** @var mixed[] */
- private array $meta;
-
- /** @var array> */
- private array $errors;
-
- /** @var array> */
- private array $dependencies;
-
- /** @var array> */
- private array $exportedNodes;
-
/**
* @param string[] $filesToAnalyse
- * @param bool $fullAnalysis
- * @param int $lastFullAnalysisTime
* @param mixed[] $meta
* @param array> $errors
* @param array> $dependencies
* @param array> $exportedNodes
*/
public function __construct(
- array $filesToAnalyse,
- bool $fullAnalysis,
- int $lastFullAnalysisTime,
- array $meta,
- array $errors,
- array $dependencies,
- array $exportedNodes
+ private array $filesToAnalyse,
+ private bool $fullAnalysis,
+ private int $lastFullAnalysisTime,
+ private array $meta,
+ private array $errors,
+ private array $dependencies,
+ private array $exportedNodes,
)
{
- $this->filesToAnalyse = $filesToAnalyse;
- $this->fullAnalysis = $fullAnalysis;
- $this->lastFullAnalysisTime = $lastFullAnalysisTime;
- $this->meta = $meta;
- $this->errors = $errors;
- $this->dependencies = $dependencies;
- $this->exportedNodes = $exportedNodes;
}
/**
diff --git a/src/Analyser/ResultCache/ResultCacheClearer.php b/src/Analyser/ResultCache/ResultCacheClearer.php
index e6628002bb..d511a3fa92 100644
--- a/src/Analyser/ResultCache/ResultCacheClearer.php
+++ b/src/Analyser/ResultCache/ResultCacheClearer.php
@@ -3,18 +3,15 @@
namespace PHPStan\Analyser\ResultCache;
use Symfony\Component\Finder\Finder;
+use function dirname;
+use function is_file;
+use function unlink;
class ResultCacheClearer
{
- private string $cacheFilePath;
-
- private string $tempResultCachePath;
-
- public function __construct(string $cacheFilePath, string $tempResultCachePath)
+ public function __construct(private string $cacheFilePath, private string $tempResultCachePath)
{
- $this->cacheFilePath = $cacheFilePath;
- $this->tempResultCachePath = $tempResultCachePath;
}
public function clear(): string
diff --git a/src/Analyser/ResultCache/ResultCacheManager.php b/src/Analyser/ResultCache/ResultCacheManager.php
index a00e958339..9780ba1cf7 100644
--- a/src/Analyser/ResultCache/ResultCacheManager.php
+++ b/src/Analyser/ResultCache/ResultCacheManager.php
@@ -2,8 +2,10 @@
namespace PHPStan\Analyser\ResultCache;
+use Jean85\PrettyVersions;
use Nette\DI\Definitions\Statement;
use Nette\Neon\Neon;
+use OutOfBoundsException;
use PHPStan\Analyser\AnalyserResult;
use PHPStan\Analyser\Error;
use PHPStan\Command\Output;
@@ -13,109 +15,74 @@
use PHPStan\File\FileReader;
use PHPStan\File\FileWriter;
use PHPStan\Reflection\ReflectionProvider;
+use PHPStan\ShouldNotHappenException;
+use Throwable;
+use function array_diff;
use function array_fill_keys;
+use function array_filter;
use function array_key_exists;
+use function array_keys;
+use function array_merge;
+use function array_unique;
+use function array_values;
+use function count;
+use function get_loaded_extensions;
+use function is_array;
+use function is_file;
+use function is_string;
+use function ksort;
+use function sha1;
+use function sort;
+use function sprintf;
+use function str_replace;
+use function time;
+use function unlink;
+use function var_export;
+use const PHP_VERSION_ID;
class ResultCacheManager
{
private const CACHE_VERSION = 'v9-project-extensions';
- private ExportedNodeFetcher $exportedNodeFetcher;
-
- private FileFinder $scanFileFinder;
-
- private ReflectionProvider $reflectionProvider;
-
- private string $cacheFilePath;
-
- private string $tempResultCachePath;
-
- /** @var string[] */
- private array $analysedPaths;
-
- /** @var string[] */
- private array $composerAutoloaderProjectPaths;
-
- /** @var string[] */
- private array $stubFiles;
-
- private string $usedLevel;
-
- private ?string $cliAutoloadFile;
-
- /** @var string[] */
- private array $bootstrapFiles;
-
- /** @var string[] */
- private array $scanFiles;
-
- /** @var string[] */
- private array $scanDirectories;
-
/** @var array */
private array $fileHashes = [];
- /** @var array */
- private array $fileReplacements = [];
-
/** @var array */
private array $alreadyProcessed = [];
/**
- * @param ExportedNodeFetcher $exportedNodeFetcher
- * @param FileFinder $scanFileFinder
- * @param ReflectionProvider $reflectionProvider
- * @param string $cacheFilePath
- * @param string $tempResultCachePath
* @param string[] $analysedPaths
* @param string[] $composerAutoloaderProjectPaths
* @param string[] $stubFiles
- * @param string $usedLevel
- * @param string|null $cliAutoloadFile
* @param string[] $bootstrapFiles
* @param string[] $scanFiles
* @param string[] $scanDirectories
* @param array $fileReplacements
*/
public function __construct(
- ExportedNodeFetcher $exportedNodeFetcher,
- FileFinder $scanFileFinder,
- ReflectionProvider $reflectionProvider,
- string $cacheFilePath,
- string $tempResultCachePath,
- array $analysedPaths,
- array $composerAutoloaderProjectPaths,
- array $stubFiles,
- string $usedLevel,
- ?string $cliAutoloadFile,
- array $bootstrapFiles,
- array $scanFiles,
- array $scanDirectories,
- array $fileReplacements
+ private ExportedNodeFetcher $exportedNodeFetcher,
+ private FileFinder $scanFileFinder,
+ private ReflectionProvider $reflectionProvider,
+ private string $cacheFilePath,
+ private string $tempResultCachePath,
+ private array $analysedPaths,
+ private array $composerAutoloaderProjectPaths,
+ private array $stubFiles,
+ private string $usedLevel,
+ private ?string $cliAutoloadFile,
+ private array $bootstrapFiles,
+ private array $scanFiles,
+ private array $scanDirectories,
+ private array $fileReplacements,
+ private bool $checkDependenciesOfProjectExtensionFiles,
)
{
- $this->exportedNodeFetcher = $exportedNodeFetcher;
- $this->scanFileFinder = $scanFileFinder;
- $this->reflectionProvider = $reflectionProvider;
- $this->cacheFilePath = $cacheFilePath;
- $this->tempResultCachePath = $tempResultCachePath;
- $this->analysedPaths = $analysedPaths;
- $this->composerAutoloaderProjectPaths = $composerAutoloaderProjectPaths;
- $this->stubFiles = $stubFiles;
- $this->usedLevel = $usedLevel;
- $this->cliAutoloadFile = $cliAutoloadFile;
- $this->bootstrapFiles = $bootstrapFiles;
- $this->scanFiles = $scanFiles;
- $this->scanDirectories = $scanDirectories;
- $this->fileReplacements = $fileReplacements;
}
/**
* @param string[] $allAnalysedFiles
* @param mixed[]|null $projectConfigArray
- * @param bool $debug
- * @return ResultCache
*/
public function restore(array $allAnalysedFiles, bool $debug, bool $onlyFiles, ?array $projectConfigArray, Output $output, ?string $resultCacheName = null): ResultCache
{
@@ -149,7 +116,7 @@ public function restore(array $allAnalysedFiles, bool $debug, bool $onlyFiles, ?
try {
$data = require $cacheFilePath;
- } catch (\Throwable $e) {
+ } catch (Throwable $e) {
if ($output->isDebug()) {
$output->writeLineFormatted(sprintf('Result cache not used because an error occurred while loading the cache file: %s', $e->getMessage()));
}
@@ -212,6 +179,15 @@ public function restore(array $allAnalysedFiles, bool $debug, bool $onlyFiles, ?
$filteredErrors = [];
$filteredExportedNodes = [];
$newFileAppeared = false;
+
+ foreach ($this->stubFiles as $stubFile) {
+ if (!array_key_exists($stubFile, $errors)) {
+ continue;
+ }
+
+ $filteredErrors[$stubFile] = $errors[$stubFile];
+ }
+
foreach ($allAnalysedFiles as $analysedFile) {
if (array_key_exists($analysedFile, $errors)) {
$filteredErrors[$analysedFile] = $errors[$analysedFile];
@@ -286,7 +262,6 @@ public function restore(array $allAnalysedFiles, bool $debug, bool $onlyFiles, ?
/**
* @param mixed[] $cachedMeta
* @param mixed[] $currentMeta
- * @return bool
*/
private function isMetaDifferent(array $cachedMeta, array $currentMeta): bool
{
@@ -299,9 +274,7 @@ private function isMetaDifferent(array $cachedMeta, array $currentMeta): bool
}
/**
- * @param string $analysedFile
* @param array $cachedFileExportedNodes
- * @return bool
*/
private function exportedNodesChanged(string $analysedFile, array $cachedFileExportedNodes): bool
{
@@ -324,10 +297,7 @@ private function exportedNodesChanged(string $analysedFile, array $cachedFileExp
}
/**
- * @param AnalyserResult $analyserResult
- * @param ResultCache $resultCache
* @param bool|string $save
- * @return ResultCacheProcessResult
*/
public function process(AnalyserResult $analyserResult, ResultCache $resultCache, Output $output, bool $onlyFiles, $save): ResultCacheProcessResult
{
@@ -416,12 +386,11 @@ public function process(AnalyserResult $analyserResult, ResultCache $resultCache
$internalErrors,
$dependencies,
$exportedNodes,
- $analyserResult->hasReachedInternalErrorsCountLimit()
+ $analyserResult->hasReachedInternalErrorsCountLimit(),
), $saved);
}
/**
- * @param ResultCache $resultCache
* @param array> $freshErrorsByFile
* @return array>
*/
@@ -440,7 +409,6 @@ private function mergeErrors(ResultCache $resultCache, array $freshErrorsByFile)
}
/**
- * @param ResultCache $resultCache
* @param array>|null $freshDependencies
* @return array>|null
*/
@@ -462,7 +430,7 @@ private function mergeDependencies(ResultCache $resultCache, ?array $freshDepend
foreach (array_keys($filesNoOneIsDependingOn) as $file) {
if (array_key_exists($file, $cachedDependencies)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$cachedDependencies[$file] = [];
@@ -482,7 +450,6 @@ private function mergeDependencies(ResultCache $resultCache, ?array $freshDepend
}
/**
- * @param ResultCache $resultCache
* @param array> $freshExportedNodes
* @return array>
*/
@@ -502,8 +469,6 @@ private function mergeExportedNodes(ResultCache $resultCache, array $freshExport
}
/**
- * @param int $lastFullAnalysisTime
- * @param string|null $resultCacheName
* @param array> $errors
* @param array> $dependencies
* @param array> $exportedNodes
@@ -515,7 +480,7 @@ private function save(
array $errors,
array $dependencies,
array $exportedNodes,
- array $meta
+ array $meta,
): void
{
$invertedDependencies = [];
@@ -535,7 +500,7 @@ private function save(
foreach (array_keys($filesNoOneIsDependingOn) as $file) {
if (array_key_exists($file, $invertedDependencies)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if (!is_file($file)) {
@@ -591,8 +556,8 @@ private function save(
var_export($this->getProjectExtensionFiles($projectConfigArray, $dependencies), true),
var_export($errors, true),
var_export($invertedDependencies, true),
- var_export($exportedNodes, true)
- )
+ var_export($exportedNodes, true),
+ ),
);
}
@@ -606,7 +571,10 @@ private function getProjectExtensionFiles(?array $projectConfig, array $dependen
$this->alreadyProcessed = [];
$projectExtensionFiles = [];
if ($projectConfig !== null) {
- $services = $projectConfig['services'] ?? [];
+ $services = array_merge(
+ $projectConfig['services'] ?? [],
+ $projectConfig['rules'] ?? [],
+ );
foreach ($services as $service) {
$classes = $this->getClassesFromConfigDefinition($service);
if (is_array($service)) {
@@ -626,7 +594,7 @@ private function getProjectExtensionFiles(?array $projectConfig, array $dependen
$classReflection = $this->reflectionProvider->getClass($class);
$fileName = $classReflection->getFileName();
- if ($fileName === false) {
+ if ($fileName === null) {
continue;
}
@@ -668,7 +636,6 @@ private function getClassesFromConfigDefinition($definition): array
}
/**
- * @param string $fileName
* @param array> $dependencies
* @return array
*/
@@ -685,9 +652,12 @@ private function getAllDependencies(string $fileName, array $dependencies): arra
$this->alreadyProcessed[$fileName] = true;
$files = [$fileName];
- foreach ($dependencies[$fileName] as $fileDep) {
- foreach ($this->getAllDependencies($fileDep, $dependencies) as $fileDep2) {
- $files[] = $fileDep2;
+
+ if ($this->checkDependenciesOfProjectExtensionFiles) {
+ foreach ($dependencies[$fileName] as $fileDep) {
+ foreach ($this->getAllDependencies($fileDep, $dependencies) as $fileDep2) {
+ $files[] = $fileDep2;
+ }
}
}
@@ -701,9 +671,7 @@ private function getAllDependencies(string $fileName, array $dependencies): arra
*/
private function getMeta(array $allAnalysedFiles, ?array $projectConfigArray): array
{
- $extensions = array_values(array_filter(get_loaded_extensions(), static function (string $extension): bool {
- return $extension !== 'xdebug';
- }));
+ $extensions = array_values(array_filter(get_loaded_extensions(), static fn (string $extension): bool => $extension !== 'xdebug'));
sort($extensions);
if ($projectConfigArray !== null) {
@@ -796,8 +764,8 @@ private function getExecutedFileHashes(): array
private function getPhpStanVersion(): string
{
try {
- return \Jean85\PrettyVersions::getVersion('phpstan/phpstan')->getPrettyVersion();
- } catch (\OutOfBoundsException $e) {
+ return PrettyVersions::getVersion('phpstan/phpstan')->getPrettyVersion();
+ } catch (OutOfBoundsException) {
return 'Version unknown';
}
}
diff --git a/src/Analyser/ResultCache/ResultCacheManagerFactory.php b/src/Analyser/ResultCache/ResultCacheManagerFactory.php
index c2bdadefaa..333bc6136e 100644
--- a/src/Analyser/ResultCache/ResultCacheManagerFactory.php
+++ b/src/Analyser/ResultCache/ResultCacheManagerFactory.php
@@ -7,7 +7,6 @@ interface ResultCacheManagerFactory
/**
* @param array $fileReplacements
- * @return ResultCacheManager
*/
public function create(array $fileReplacements): ResultCacheManager;
diff --git a/src/Analyser/ResultCache/ResultCacheProcessResult.php b/src/Analyser/ResultCache/ResultCacheProcessResult.php
index b3230ca968..27326f33a0 100644
--- a/src/Analyser/ResultCache/ResultCacheProcessResult.php
+++ b/src/Analyser/ResultCache/ResultCacheProcessResult.php
@@ -7,14 +7,8 @@
class ResultCacheProcessResult
{
- private AnalyserResult $analyserResult;
-
- private bool $saved;
-
- public function __construct(AnalyserResult $analyserResult, bool $saved)
+ public function __construct(private AnalyserResult $analyserResult, private bool $saved)
{
- $this->analyserResult = $analyserResult;
- $this->saved = $saved;
}
public function getAnalyserResult(): AnalyserResult
diff --git a/src/Analyser/Scope.php b/src/Analyser/Scope.php
index fe9c726a1c..82436e50ac 100644
--- a/src/Analyser/Scope.php
+++ b/src/Analyser/Scope.php
@@ -2,11 +2,13 @@
namespace PHPStan\Analyser;
+use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PHPStan\Reflection\ClassMemberAccessAnswerer;
use PHPStan\Reflection\ClassReflection;
+use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\PropertyReflection;
@@ -29,7 +31,7 @@ public function isInTrait(): bool;
public function getTraitReflection(): ?ClassReflection;
/**
- * @return \PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection|null
+ * @return FunctionReflection|MethodReflection|null
*/
public function getFunction();
@@ -60,7 +62,7 @@ public function isInAnonymousFunction(): bool;
public function getAnonymousFunctionReflection(): ?ParametersAcceptor;
- public function getAnonymousFunctionReturnType(): ?\PHPStan\Type\Type;
+ public function getAnonymousFunctionReturnType(): ?Type;
public function getType(Expr $node): Type;
@@ -69,8 +71,6 @@ public function getType(Expr $node): Type;
* Works for function/method parameters only.
*
* @internal
- * @param Expr $expr
- * @return Type
*/
public function getNativeType(Expr $expr): Type;
@@ -89,15 +89,14 @@ public function isSpecified(Expr $node): bool;
public function isInClassExists(string $className): bool;
+ public function isInFunctionExists(string $functionName): bool;
+
public function isInClosureBind(): bool;
public function isParameterValueNullable(Param $parameter): bool;
/**
- * @param \PhpParser\Node\Name|\PhpParser\Node\Identifier|\PhpParser\Node\NullableType|\PhpParser\Node\UnionType|null $type
- * @param bool $isNullable
- * @param bool $isVariadic
- * @return Type
+ * @param Node\Name|Node\Identifier|Node\ComplexType|null $type
*/
public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type;
diff --git a/src/Analyser/ScopeContext.php b/src/Analyser/ScopeContext.php
index ea03d664f9..4118909860 100644
--- a/src/Analyser/ScopeContext.php
+++ b/src/Analyser/ScopeContext.php
@@ -3,25 +3,17 @@
namespace PHPStan\Analyser;
use PHPStan\Reflection\ClassReflection;
+use PHPStan\ShouldNotHappenException;
class ScopeContext
{
- private string $file;
-
- private ?ClassReflection $classReflection;
-
- private ?ClassReflection $traitReflection;
-
private function __construct(
- string $file,
- ?ClassReflection $classReflection,
- ?ClassReflection $traitReflection
+ private string $file,
+ private ?ClassReflection $classReflection,
+ private ?ClassReflection $traitReflection,
)
{
- $this->file = $file;
- $this->classReflection = $classReflection;
- $this->traitReflection = $traitReflection;
}
/** @api */
@@ -38,10 +30,10 @@ public function beginFile(): self
public function enterClass(ClassReflection $classReflection): self
{
if ($this->classReflection !== null && !$classReflection->isAnonymous()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if ($classReflection->isTrait()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return new self($this->file, $classReflection, null);
}
@@ -49,10 +41,10 @@ public function enterClass(ClassReflection $classReflection): self
public function enterTrait(ClassReflection $traitReflection): self
{
if ($this->classReflection === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if (!$traitReflection->isTrait()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return new self($this->file, $this->classReflection, $traitReflection);
diff --git a/src/Analyser/ScopeFactory.php b/src/Analyser/ScopeFactory.php
index b0fd874e7d..1d7db30f3e 100644
--- a/src/Analyser/ScopeFactory.php
+++ b/src/Analyser/ScopeFactory.php
@@ -12,30 +12,20 @@ interface ScopeFactory
/**
* @api
- * @param \PHPStan\Analyser\ScopeContext $context
- * @param bool $declareStrictTypes
* @param array $constantTypes
- * @param \PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection|null $function
- * @param string|null $namespace
- * @param \PHPStan\Analyser\VariableTypeHolder[] $variablesTypes
- * @param \PHPStan\Analyser\VariableTypeHolder[] $moreSpecificTypes
+ * @param VariableTypeHolder[] $variablesTypes
+ * @param VariableTypeHolder[] $moreSpecificTypes
* @param array $conditionalExpressions
- * @param string|null $inClosureBindScopeClass
- * @param \PHPStan\Reflection\ParametersAcceptor|null $anonymousFunctionReflection
- * @param bool $inFirstLevelStatement
* @param array $currentlyAssignedExpressions
* @param array $nativeExpressionTypes
* @param array $inFunctionCallsStack
- * @param bool $afterExtractCall
- * @param Scope|null $parentScope
*
- * @return MutatingScope
*/
public function create(
ScopeContext $context,
bool $declareStrictTypes = false,
array $constantTypes = [],
- $function = null,
+ FunctionReflection|MethodReflection|null $function = null,
?string $namespace = null,
array $variablesTypes = [],
array $moreSpecificTypes = [],
@@ -47,7 +37,7 @@ public function create(
array $nativeExpressionTypes = [],
array $inFunctionCallsStack = [],
bool $afterExtractCall = false,
- ?Scope $parentScope = null
+ ?Scope $parentScope = null,
): MutatingScope;
}
diff --git a/src/Analyser/SpecifiedTypes.php b/src/Analyser/SpecifiedTypes.php
index ec3005c8c1..aefc6214f2 100644
--- a/src/Analyser/SpecifiedTypes.php
+++ b/src/Analyser/SpecifiedTypes.php
@@ -2,45 +2,31 @@
namespace PHPStan\Analyser;
+use PhpParser\Node\Expr;
+use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
class SpecifiedTypes
{
- /** @var mixed[] */
- private array $sureTypes;
-
- /** @var mixed[] */
- private array $sureNotTypes;
-
- private bool $overwrite;
-
- /** @var array */
- private array $newConditionalExpressionHolders;
-
/**
* @api
- * @param mixed[] $sureTypes
- * @param mixed[] $sureNotTypes
- * @param bool $overwrite
+ * @param array $sureTypes
+ * @param array $sureNotTypes
* @param array $newConditionalExpressionHolders
*/
public function __construct(
- array $sureTypes = [],
- array $sureNotTypes = [],
- bool $overwrite = false,
- array $newConditionalExpressionHolders = []
+ private array $sureTypes = [],
+ private array $sureNotTypes = [],
+ private bool $overwrite = false,
+ private array $newConditionalExpressionHolders = [],
)
{
- $this->sureTypes = $sureTypes;
- $this->sureNotTypes = $sureNotTypes;
- $this->overwrite = $overwrite;
- $this->newConditionalExpressionHolders = $newConditionalExpressionHolders;
}
/**
* @api
- * @return mixed[]
+ * @return array
*/
public function getSureTypes(): array
{
@@ -49,7 +35,7 @@ public function getSureTypes(): array
/**
* @api
- * @return mixed[]
+ * @return array
*/
public function getSureNotTypes(): array
{
@@ -131,4 +117,20 @@ public function unionWith(SpecifiedTypes $other): self
return new self($sureTypeUnion, $sureNotTypeUnion);
}
+ public function normalize(Scope $scope): self
+ {
+ $sureTypes = $this->sureTypes;
+
+ foreach ($this->sureNotTypes as $exprString => [$exprNode, $sureNotType]) {
+ if (!isset($sureTypes[$exprString])) {
+ $sureTypes[$exprString] = [$exprNode, TypeCombinator::remove($scope->getType($exprNode), $sureNotType)];
+ continue;
+ }
+
+ $sureTypes[$exprString][1] = TypeCombinator::remove($sureTypes[$exprString][1], $sureNotType);
+ }
+
+ return new self($sureTypes, [], $this->overwrite, $this->newConditionalExpressionHolders);
+ }
+
}
diff --git a/src/Analyser/StatementExitPoint.php b/src/Analyser/StatementExitPoint.php
index f5f6874438..bb372c1786 100644
--- a/src/Analyser/StatementExitPoint.php
+++ b/src/Analyser/StatementExitPoint.php
@@ -7,14 +7,8 @@
class StatementExitPoint
{
- private Stmt $statement;
-
- private MutatingScope $scope;
-
- public function __construct(Stmt $statement, MutatingScope $scope)
+ public function __construct(private Stmt $statement, private MutatingScope $scope)
{
- $this->statement = $statement;
- $this->scope = $scope;
}
public function getStatement(): Stmt
diff --git a/src/Analyser/StatementResult.php b/src/Analyser/StatementResult.php
index ed4a85465f..729e7a5ec0 100644
--- a/src/Analyser/StatementResult.php
+++ b/src/Analyser/StatementResult.php
@@ -8,38 +8,18 @@
class StatementResult
{
- private MutatingScope $scope;
-
- private bool $hasYield;
-
- private bool $isAlwaysTerminating;
-
- /** @var StatementExitPoint[] */
- private array $exitPoints;
-
- /** @var ThrowPoint[] */
- private array $throwPoints;
-
/**
- * @param MutatingScope $scope
- * @param bool $hasYield
- * @param bool $isAlwaysTerminating
* @param StatementExitPoint[] $exitPoints
* @param ThrowPoint[] $throwPoints
*/
public function __construct(
- MutatingScope $scope,
- bool $hasYield,
- bool $isAlwaysTerminating,
- array $exitPoints,
- array $throwPoints
+ private MutatingScope $scope,
+ private bool $hasYield,
+ private bool $isAlwaysTerminating,
+ private array $exitPoints,
+ private array $throwPoints,
)
{
- $this->scope = $scope;
- $this->hasYield = $hasYield;
- $this->isAlwaysTerminating = $isAlwaysTerminating;
- $this->exitPoints = $exitPoints;
- $this->throwPoints = $throwPoints;
}
public function getScope(): MutatingScope
@@ -136,6 +116,7 @@ public function getExitPointsForOuterLoop(): array
foreach ($this->exitPoints as $exitPoint) {
$statement = $exitPoint->getStatement();
if (!$statement instanceof Stmt\Continue_ && !$statement instanceof Stmt\Break_) {
+ $exitPoints[] = $exitPoint;
continue;
}
if ($statement->num === null) {
diff --git a/src/Analyser/ThrowPoint.php b/src/Analyser/ThrowPoint.php
index 4a63f3dcce..c25c5dc05d 100644
--- a/src/Analyser/ThrowPoint.php
+++ b/src/Analyser/ThrowPoint.php
@@ -6,49 +6,26 @@
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
+use Throwable;
class ThrowPoint
{
- private MutatingScope $scope;
-
- private Type $type;
-
- /** @var Node\Expr|Node\Stmt */
- private Node $node;
-
- private bool $explicit;
-
- private bool $canContainAnyThrowable;
-
/**
- * @param MutatingScope $scope
- * @param Type $type
* @param Node\Expr|Node\Stmt $node
- * @param bool $explicit
- * @param bool $canContainAnyThrowable
*/
private function __construct(
- MutatingScope $scope,
- Type $type,
- Node $node,
- bool $explicit,
- bool $canContainAnyThrowable
+ private MutatingScope $scope,
+ private Type $type,
+ private Node $node,
+ private bool $explicit,
+ private bool $canContainAnyThrowable,
)
{
- $this->scope = $scope;
- $this->type = $type;
- $this->node = $node;
- $this->explicit = $explicit;
- $this->canContainAnyThrowable = $canContainAnyThrowable;
}
/**
- * @param MutatingScope $scope
- * @param Type $type
* @param Node\Expr|Node\Stmt $node
- * @param bool $canContainAnyThrowable
- * @return self
*/
public static function createExplicit(MutatingScope $scope, Type $type, Node $node, bool $canContainAnyThrowable): self
{
@@ -56,13 +33,11 @@ public static function createExplicit(MutatingScope $scope, Type $type, Node $no
}
/**
- * @param MutatingScope $scope
* @param Node\Expr|Node\Stmt $node
- * @return self
*/
public static function createImplicit(MutatingScope $scope, Node $node): self
{
- return new self($scope, new ObjectType(\Throwable::class), $node, false, true);
+ return new self($scope, new ObjectType(Throwable::class), $node, false, true);
}
public function getScope(): MutatingScope
diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php
index 547b4f27ef..7b95f49614 100644
--- a/src/Analyser/TypeSpecifier.php
+++ b/src/Analyser/TypeSpecifier.php
@@ -9,90 +9,84 @@
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
use PhpParser\Node\Expr\BinaryOp\LogicalAnd;
use PhpParser\Node\Expr\BinaryOp\LogicalOr;
+use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Expr\MethodCall;
-use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Name;
+use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Node\VirtualNode;
use PHPStan\Reflection\ReflectionProvider;
+use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
+use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Accessory\HasOffsetType;
use PHPStan\Type\Accessory\HasPropertyType;
use PHPStan\Type\Accessory\NonEmptyArrayType;
-use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
+use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
+use PHPStan\Type\ConstantScalarType;
use PHPStan\Type\ConstantType;
+use PHPStan\Type\Enum\EnumCaseObjectType;
+use PHPStan\Type\FloatType;
+use PHPStan\Type\FunctionTypeSpecifyingExtension;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IntersectionType;
+use PHPStan\Type\MethodTypeSpecifyingExtension;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\NonexistentParentClassType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
+use PHPStan\Type\StaticMethodTypeSpecifyingExtension;
use PHPStan\Type\StaticType;
use PHPStan\Type\StaticTypeFactory;
+use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
+use function array_merge;
use function array_reverse;
+use function count;
+use function in_array;
+use function is_string;
+use function strtolower;
class TypeSpecifier
{
- private \PhpParser\PrettyPrinter\Standard $printer;
-
- private ReflectionProvider $reflectionProvider;
-
- private bool $rememberFunctionValues;
-
- /** @var \PHPStan\Type\FunctionTypeSpecifyingExtension[] */
- private array $functionTypeSpecifyingExtensions;
-
- /** @var \PHPStan\Type\MethodTypeSpecifyingExtension[] */
- private array $methodTypeSpecifyingExtensions;
-
- /** @var \PHPStan\Type\StaticMethodTypeSpecifyingExtension[] */
- private array $staticMethodTypeSpecifyingExtensions;
-
- /** @var \PHPStan\Type\MethodTypeSpecifyingExtension[][]|null */
+ /** @var MethodTypeSpecifyingExtension[][]|null */
private ?array $methodTypeSpecifyingExtensionsByClass = null;
- /** @var \PHPStan\Type\StaticMethodTypeSpecifyingExtension[][]|null */
+ /** @var StaticMethodTypeSpecifyingExtension[][]|null */
private ?array $staticMethodTypeSpecifyingExtensionsByClass = null;
/**
- * @param \PhpParser\PrettyPrinter\Standard $printer
- * @param ReflectionProvider $reflectionProvider
- * @param \PHPStan\Type\FunctionTypeSpecifyingExtension[] $functionTypeSpecifyingExtensions
- * @param \PHPStan\Type\MethodTypeSpecifyingExtension[] $methodTypeSpecifyingExtensions
- * @param \PHPStan\Type\StaticMethodTypeSpecifyingExtension[] $staticMethodTypeSpecifyingExtensions
+ * @param FunctionTypeSpecifyingExtension[] $functionTypeSpecifyingExtensions
+ * @param MethodTypeSpecifyingExtension[] $methodTypeSpecifyingExtensions
+ * @param StaticMethodTypeSpecifyingExtension[] $staticMethodTypeSpecifyingExtensions
*/
public function __construct(
- \PhpParser\PrettyPrinter\Standard $printer,
- ReflectionProvider $reflectionProvider,
- bool $rememberFunctionValues,
- array $functionTypeSpecifyingExtensions,
- array $methodTypeSpecifyingExtensions,
- array $staticMethodTypeSpecifyingExtensions
+ private Standard $printer,
+ private ReflectionProvider $reflectionProvider,
+ private array $functionTypeSpecifyingExtensions,
+ private array $methodTypeSpecifyingExtensions,
+ private array $staticMethodTypeSpecifyingExtensions,
)
{
- $this->printer = $printer;
- $this->reflectionProvider = $reflectionProvider;
- $this->rememberFunctionValues = $rememberFunctionValues;
-
foreach (array_merge($functionTypeSpecifyingExtensions, $methodTypeSpecifyingExtensions, $staticMethodTypeSpecifyingExtensions) as $extension) {
if (!($extension instanceof TypeSpecifierAwareExtension)) {
continue;
@@ -100,19 +94,19 @@ public function __construct(
$extension->setTypeSpecifier($this);
}
-
- $this->functionTypeSpecifyingExtensions = $functionTypeSpecifyingExtensions;
- $this->methodTypeSpecifyingExtensions = $methodTypeSpecifyingExtensions;
- $this->staticMethodTypeSpecifyingExtensions = $staticMethodTypeSpecifyingExtensions;
}
/** @api */
public function specifyTypesInCondition(
Scope $scope,
Expr $expr,
- TypeSpecifierContext $context
+ TypeSpecifierContext $context,
): SpecifiedTypes
{
+ if ($expr instanceof Expr\CallLike && $expr->isFirstClassCallable()) {
+ return new SpecifiedTypes();
+ }
+
if ($expr instanceof Instanceof_) {
$exprNode = $expr->expr;
if ($expr->class instanceof Name) {
@@ -125,7 +119,7 @@ public function specifyTypesInCondition(
} elseif ($lowercasedClassName === 'parent') {
if (
$scope->isInClass()
- && $scope->getClassReflection()->getParentClass() !== false
+ && $scope->getClassReflection()->getParentClass() !== null
) {
$type = new ObjectType($scope->getClassReflection()->getParentClass()->getName());
} else {
@@ -158,7 +152,7 @@ public function specifyTypesInCondition(
if ($context->true()) {
$type = TypeCombinator::intersect(
$type,
- new ObjectWithoutClassType()
+ new ObjectWithoutClassType(),
);
return $this->create($exprNode, $type, $context, false, $scope);
} elseif ($context->false()) {
@@ -176,14 +170,14 @@ public function specifyTypesInCondition(
if ($expressions !== null) {
/** @var Expr $exprNode */
$exprNode = $expressions[0];
- /** @var \PHPStan\Type\ConstantScalarType $constantType */
+ /** @var ConstantScalarType $constantType */
$constantType = $expressions[1];
if ($constantType->getValue() === false) {
$types = $this->create($exprNode, $constantType, $context, false, $scope);
return $types->unionWith($this->specifyTypesInCondition(
$scope,
$exprNode,
- $context->true() ? TypeSpecifierContext::createFalse() : TypeSpecifierContext::createFalse()->negate()
+ $context->true() ? TypeSpecifierContext::createFalse() : TypeSpecifierContext::createFalse()->negate(),
));
}
@@ -192,7 +186,7 @@ public function specifyTypesInCondition(
return $types->unionWith($this->specifyTypesInCondition(
$scope,
$exprNode,
- $context->true() ? TypeSpecifierContext::createTrue() : TypeSpecifierContext::createTrue()->negate()
+ $context->true() ? TypeSpecifierContext::createTrue() : TypeSpecifierContext::createTrue()->negate(),
));
}
@@ -203,9 +197,9 @@ public function specifyTypesInCondition(
if (
!$context->null()
&& $exprNode instanceof FuncCall
- && count($exprNode->args) === 1
+ && count($exprNode->getArgs()) === 1
&& $exprNode->name instanceof Name
- && strtolower((string) $exprNode->name) === 'count'
+ && in_array(strtolower((string) $exprNode->name), ['count', 'sizeof'], true)
&& $constantType instanceof ConstantIntegerType
) {
if ($context->truthy() || $constantType->getValue() === 0) {
@@ -213,21 +207,57 @@ public function specifyTypesInCondition(
if ($constantType->getValue() === 0) {
$newContext = $newContext->negate();
}
- $argType = $scope->getType($exprNode->args[0]->value);
+ $argType = $scope->getType($exprNode->getArgs()[0]->value);
if ($argType->isArray()->yes()) {
- return $this->create($exprNode->args[0]->value, new NonEmptyArrayType(), $newContext, false, $scope);
+ $funcTypes = $this->create($exprNode, $constantType, $context, false, $scope);
+ $valueTypes = $this->create($exprNode->getArgs()[0]->value, new NonEmptyArrayType(), $newContext, false, $scope);
+ return $funcTypes->unionWith($valueTypes);
}
}
}
- }
- if ($context->true()) {
- $type = TypeCombinator::intersect($scope->getType($expr->right), $scope->getType($expr->left));
- $leftTypes = $this->create($expr->left, $type, $context, false, $scope);
- $rightTypes = $this->create($expr->right, $type, $context, false, $scope);
- return $leftTypes->unionWith($rightTypes);
+ if (
+ !$context->null()
+ && $exprNode instanceof FuncCall
+ && count($exprNode->getArgs()) === 1
+ && $exprNode->name instanceof Name
+ && strtolower((string) $exprNode->name) === 'strlen'
+ && $constantType instanceof ConstantIntegerType
+ ) {
+ if ($context->truthy() || $constantType->getValue() === 0) {
+ $newContext = $context;
+ if ($constantType->getValue() === 0) {
+ $newContext = $newContext->negate();
+ }
+ $argType = $scope->getType($exprNode->getArgs()[0]->value);
+ if ($argType instanceof StringType) {
+ $funcTypes = $this->create($exprNode, $constantType, $context, false, $scope);
+ $valueTypes = $this->create($exprNode->getArgs()[0]->value, new AccessoryNonEmptyStringType(), $newContext, false, $scope);
+ return $funcTypes->unionWith($valueTypes);
+ }
+ }
+ }
+ }
- } elseif ($context->false()) {
+ $rightType = $scope->getType($expr->right);
+ if (
+ $expr->left instanceof ClassConstFetch &&
+ $expr->left->class instanceof Expr &&
+ $expr->left->name instanceof Node\Identifier &&
+ $expr->right instanceof ClassConstFetch &&
+ $rightType instanceof ConstantStringType &&
+ strtolower($expr->left->name->toString()) === 'class'
+ ) {
+ return $this->specifyTypesInCondition(
+ $scope,
+ new Instanceof_(
+ $expr->left->class,
+ new Name($rightType->getValue()),
+ ),
+ $context,
+ );
+ }
+ if ($context->false()) {
$identicalType = $scope->getType($expr);
if ($identicalType instanceof ConstantBooleanType) {
$never = new NeverType();
@@ -236,34 +266,30 @@ public function specifyTypesInCondition(
$rightTypes = $this->create($expr->right, $never, $contextForTypes, false, $scope);
return $leftTypes->unionWith($rightTypes);
}
+ }
- $exprLeftType = $scope->getType($expr->left);
- $exprRightType = $scope->getType($expr->right);
-
- $types = null;
-
- if (
- $exprLeftType instanceof ConstantType
- && !$expr->right instanceof Node\Scalar
- ) {
+ $types = null;
+ $exprLeftType = $scope->getType($expr->left);
+ $exprRightType = $scope->getType($expr->right);
+ if ($exprLeftType instanceof ConstantType || $exprLeftType instanceof EnumCaseObjectType) {
+ if (!$expr->right instanceof Node\Scalar && !$expr->right instanceof Expr\Array_) {
$types = $this->create(
$expr->right,
$exprLeftType,
$context,
false,
- $scope
+ $scope,
);
}
- if (
- $exprRightType instanceof ConstantType
- && !$expr->left instanceof Node\Scalar
- ) {
+ }
+ if ($exprRightType instanceof ConstantType || $exprRightType instanceof EnumCaseObjectType) {
+ if ($types === null || (!$expr->left instanceof Node\Scalar && !$expr->left instanceof Expr\Array_)) {
$leftType = $this->create(
$expr->left,
$exprRightType,
$context,
false,
- $scope
+ $scope,
);
if ($types !== null) {
$types = $types->unionWith($leftType);
@@ -271,30 +297,48 @@ public function specifyTypesInCondition(
$types = $leftType;
}
}
+ }
+
+ if ($types !== null) {
+ return $types;
+ }
- if ($types !== null) {
- return $types;
+ $leftExprString = $this->printer->prettyPrintExpr($expr->left);
+ $rightExprString = $this->printer->prettyPrintExpr($expr->right);
+ if ($leftExprString === $rightExprString) {
+ if (!$expr->left instanceof Expr\Variable || !$expr->right instanceof Expr\Variable) {
+ return new SpecifiedTypes();
}
}
+ if ($context->true()) {
+ $type = TypeCombinator::intersect($scope->getType($expr->right), $scope->getType($expr->left));
+ $leftTypes = $this->create($expr->left, $type, $context, false, $scope);
+ $rightTypes = $this->create($expr->right, $type, $context, false, $scope);
+ return $leftTypes->unionWith($rightTypes);
+ } elseif ($context->false()) {
+ return $this->create($expr->left, $exprLeftType, $context, false, $scope)->normalize($scope)
+ ->intersectWith($this->create($expr->right, $exprRightType, $context, false, $scope)->normalize($scope));
+ }
+
} elseif ($expr instanceof Node\Expr\BinaryOp\NotIdentical) {
return $this->specifyTypesInCondition(
$scope,
new Node\Expr\BooleanNot(new Node\Expr\BinaryOp\Identical($expr->left, $expr->right)),
- $context
+ $context,
);
} elseif ($expr instanceof Node\Expr\BinaryOp\Equal) {
$expressions = $this->findTypeExpressionsFromBinaryOperation($scope, $expr);
if ($expressions !== null) {
/** @var Expr $exprNode */
$exprNode = $expressions[0];
- /** @var \PHPStan\Type\ConstantScalarType $constantType */
+ /** @var ConstantScalarType $constantType */
$constantType = $expressions[1];
if ($constantType->getValue() === false || $constantType->getValue() === null) {
return $this->specifyTypesInCondition(
$scope,
$exprNode,
- $context->true() ? TypeSpecifierContext::createFalsey() : TypeSpecifierContext::createFalsey()->negate()
+ $context->true() ? TypeSpecifierContext::createFalsey() : TypeSpecifierContext::createFalsey()->negate(),
);
}
@@ -302,22 +346,23 @@ public function specifyTypesInCondition(
return $this->specifyTypesInCondition(
$scope,
$exprNode,
- $context->true() ? TypeSpecifierContext::createTruthy() : TypeSpecifierContext::createTruthy()->negate()
+ $context->true() ? TypeSpecifierContext::createTruthy() : TypeSpecifierContext::createTruthy()->negate(),
);
}
}
$leftType = $scope->getType($expr->left);
- $leftBooleanType = $leftType->toBoolean();
$rightType = $scope->getType($expr->right);
+
+ $leftBooleanType = $leftType->toBoolean();
if ($leftBooleanType instanceof ConstantBooleanType && $rightType instanceof BooleanType) {
return $this->specifyTypesInCondition(
$scope,
new Expr\BinaryOp\Identical(
new ConstFetch(new Name($leftBooleanType->getValue() ? 'true' : 'false')),
- $expr->right
+ $expr->right,
),
- $context
+ $context,
);
}
@@ -327,26 +372,40 @@ public function specifyTypesInCondition(
$scope,
new Expr\BinaryOp\Identical(
$expr->left,
- new ConstFetch(new Name($rightBooleanType->getValue() ? 'true' : 'false'))
+ new ConstFetch(new Name($rightBooleanType->getValue() ? 'true' : 'false')),
),
- $context
+ $context,
);
}
+ if (
+ $rightType->isArray()->yes()
+ && $leftType instanceof ConstantArrayType && $leftType->isEmpty()
+ ) {
+ return $this->create($expr->right, new NonEmptyArrayType(), $context->negate(), false, $scope);
+ }
+
+ if (
+ $leftType->isArray()->yes()
+ && $rightType instanceof ConstantArrayType && $rightType->isEmpty()
+ ) {
+ return $this->create($expr->left, new NonEmptyArrayType(), $context->negate(), false, $scope);
+ }
+
if (
$expr->left instanceof FuncCall
&& $expr->left->name instanceof Name
&& strtolower($expr->left->name->toString()) === 'get_class'
- && isset($expr->left->args[0])
+ && isset($expr->left->getArgs()[0])
&& $rightType instanceof ConstantStringType
) {
return $this->specifyTypesInCondition(
$scope,
new Instanceof_(
- $expr->left->args[0]->value,
- new Name($rightType->getValue())
+ $expr->left->getArgs()[0]->value,
+ new Name($rightType->getValue()),
),
- $context
+ $context,
);
}
@@ -354,23 +413,49 @@ public function specifyTypesInCondition(
$expr->right instanceof FuncCall
&& $expr->right->name instanceof Name
&& strtolower($expr->right->name->toString()) === 'get_class'
- && isset($expr->right->args[0])
+ && isset($expr->right->getArgs()[0])
&& $leftType instanceof ConstantStringType
) {
return $this->specifyTypesInCondition(
$scope,
new Instanceof_(
- $expr->right->args[0]->value,
- new Name($leftType->getValue())
+ $expr->right->getArgs()[0]->value,
+ new Name($leftType->getValue()),
),
- $context
+ $context,
);
}
+
+ $stringType = new StringType();
+ $integerType = new IntegerType();
+ $floatType = new FloatType();
+ if (
+ ($stringType->isSuperTypeOf($leftType)->yes() && $stringType->isSuperTypeOf($rightType)->yes())
+ || ($integerType->isSuperTypeOf($leftType)->yes() && $integerType->isSuperTypeOf($rightType)->yes())
+ || ($floatType->isSuperTypeOf($leftType)->yes() && $floatType->isSuperTypeOf($rightType)->yes())
+ ) {
+ return $this->specifyTypesInCondition($scope, new Expr\BinaryOp\Identical($expr->left, $expr->right), $context);
+ }
+
+ $leftExprString = $this->printer->prettyPrintExpr($expr->left);
+ $rightExprString = $this->printer->prettyPrintExpr($expr->right);
+ if ($leftExprString === $rightExprString) {
+ if (!$expr->left instanceof Expr\Variable || !$expr->right instanceof Expr\Variable) {
+ return new SpecifiedTypes();
+ }
+ }
+
+ $leftTypes = $this->create($expr->left, $leftType, $context, false, $scope);
+ $rightTypes = $this->create($expr->right, $rightType, $context, false, $scope);
+
+ return $context->true()
+ ? $leftTypes->unionWith($rightTypes)
+ : $leftTypes->normalize($scope)->intersectWith($rightTypes->normalize($scope));
} elseif ($expr instanceof Node\Expr\BinaryOp\NotEqual) {
return $this->specifyTypesInCondition(
$scope,
new Node\Expr\BooleanNot(new Node\Expr\BinaryOp\Equal($expr->left, $expr->right)),
- $context
+ $context,
);
} elseif ($expr instanceof Node\Expr\BinaryOp\Smaller || $expr instanceof Node\Expr\BinaryOp\SmallerOrEqual) {
@@ -381,13 +466,13 @@ public function specifyTypesInCondition(
if (
$expr->left instanceof FuncCall
- && count($expr->left->args) === 1
+ && count($expr->left->getArgs()) === 1
&& $expr->left->name instanceof Name
- && strtolower((string) $expr->left->name) === 'count'
+ && in_array(strtolower((string) $expr->left->name), ['count', 'sizeof', 'strlen'], true)
&& (
!$expr->right instanceof FuncCall
|| !$expr->right->name instanceof Name
- || strtolower((string) $expr->right->name) !== 'count'
+ || !in_array(strtolower((string) $expr->right->name), ['count', 'sizeof', 'strlen'], true)
)
) {
$inverseOperator = $expr instanceof Node\Expr\BinaryOp\Smaller
@@ -397,7 +482,7 @@ public function specifyTypesInCondition(
return $this->specifyTypesInCondition(
$scope,
new Node\Expr\BooleanNot($inverseOperator),
- $context
+ $context,
);
}
@@ -406,18 +491,37 @@ public function specifyTypesInCondition(
if (
!$context->null()
&& $expr->right instanceof FuncCall
- && count($expr->right->args) === 1
+ && count($expr->right->getArgs()) === 1
&& $expr->right->name instanceof Name
- && strtolower((string) $expr->right->name) === 'count'
+ && in_array(strtolower((string) $expr->right->name), ['count', 'sizeof'], true)
&& (new IntegerType())->isSuperTypeOf($leftType)->yes()
) {
if (
$context->truthy() && (IntegerRangeType::createAllGreaterThanOrEqualTo(1 - $offset)->isSuperTypeOf($leftType)->yes())
|| ($context->falsey() && (new ConstantIntegerType(1 - $offset))->isSuperTypeOf($leftType)->yes())
) {
- $argType = $scope->getType($expr->right->args[0]->value);
+ $argType = $scope->getType($expr->right->getArgs()[0]->value);
if ($argType->isArray()->yes()) {
- $result = $result->unionWith($this->create($expr->right->args[0]->value, new NonEmptyArrayType(), $context, false, $scope));
+ $result = $result->unionWith($this->create($expr->right->getArgs()[0]->value, new NonEmptyArrayType(), $context, false, $scope));
+ }
+ }
+ }
+
+ if (
+ !$context->null()
+ && $expr->right instanceof FuncCall
+ && count($expr->right->getArgs()) === 1
+ && $expr->right->name instanceof Name
+ && strtolower((string) $expr->right->name) === 'strlen'
+ && (new IntegerType())->isSuperTypeOf($leftType)->yes()
+ ) {
+ if (
+ $context->truthy() && (IntegerRangeType::createAllGreaterThanOrEqualTo(1 - $offset)->isSuperTypeOf($leftType)->yes())
+ || ($context->falsey() && (new ConstantIntegerType(1 - $offset))->isSuperTypeOf($leftType)->yes())
+ ) {
+ $argType = $scope->getType($expr->right->getArgs()[0]->value);
+ if ($argType instanceof StringType) {
+ $result = $result->unionWith($this->create($expr->right->getArgs()[0]->value, new AccessoryNonEmptyStringType(), $context, false, $scope));
}
}
}
@@ -427,19 +531,19 @@ public function specifyTypesInCondition(
$result = $result->unionWith($this->createRangeTypes(
$expr->right->var,
IntegerRangeType::fromInterval($leftType->getValue(), null, $offset + 1),
- $context
+ $context,
));
} elseif ($expr->right instanceof Expr\PostDec) {
$result = $result->unionWith($this->createRangeTypes(
$expr->right->var,
IntegerRangeType::fromInterval($leftType->getValue(), null, $offset - 1),
- $context
+ $context,
));
} elseif ($expr->right instanceof Expr\PreInc || $expr->right instanceof Expr\PreDec) {
$result = $result->unionWith($this->createRangeTypes(
$expr->right->var,
IntegerRangeType::fromInterval($leftType->getValue(), null, $offset),
- $context
+ $context,
));
}
}
@@ -449,19 +553,19 @@ public function specifyTypesInCondition(
$result = $result->unionWith($this->createRangeTypes(
$expr->left->var,
IntegerRangeType::fromInterval(null, $rightType->getValue(), -$offset + 1),
- $context
+ $context,
));
} elseif ($expr->left instanceof Expr\PostDec) {
$result = $result->unionWith($this->createRangeTypes(
$expr->left->var,
IntegerRangeType::fromInterval(null, $rightType->getValue(), -$offset - 1),
- $context
+ $context,
));
} elseif ($expr->left instanceof Expr\PreInc || $expr->left instanceof Expr\PreDec) {
$result = $result->unionWith($this->createRangeTypes(
$expr->left->var,
IntegerRangeType::fromInterval(null, $rightType->getValue(), -$offset),
- $context
+ $context,
));
}
}
@@ -474,8 +578,8 @@ public function specifyTypesInCondition(
$orEqual ? $rightType->getSmallerOrEqualType() : $rightType->getSmallerType(),
TypeSpecifierContext::createTruthy(),
false,
- $scope
- )
+ $scope,
+ ),
);
}
if (!$expr->right instanceof Node\Scalar) {
@@ -485,8 +589,8 @@ public function specifyTypesInCondition(
$orEqual ? $leftType->getGreaterOrEqualType() : $leftType->getGreaterType(),
TypeSpecifierContext::createTruthy(),
false,
- $scope
- )
+ $scope,
+ ),
);
}
} elseif ($context->false()) {
@@ -497,8 +601,8 @@ public function specifyTypesInCondition(
$orEqual ? $rightType->getGreaterType() : $rightType->getGreaterOrEqualType(),
TypeSpecifierContext::createTruthy(),
false,
- $scope
- )
+ $scope,
+ ),
);
}
if (!$expr->right instanceof Node\Scalar) {
@@ -508,8 +612,8 @@ public function specifyTypesInCondition(
$orEqual ? $leftType->getSmallerType() : $leftType->getSmallerOrEqualType(),
TypeSpecifierContext::createTruthy(),
false,
- $scope
- )
+ $scope,
+ ),
);
}
}
@@ -534,9 +638,7 @@ public function specifyTypesInCondition(
}
}
- if ($this->rememberFunctionValues) {
- return $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
- }
+ return $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
} elseif ($expr instanceof MethodCall && $expr->name instanceof Node\Identifier) {
$methodCalledOnType = $scope->getType($expr->var);
$referencedClasses = TypeUtils::getDirectClassNames($methodCalledOnType);
@@ -557,9 +659,7 @@ public function specifyTypesInCondition(
}
}
- if ($this->rememberFunctionValues) {
- return $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
- }
+ return $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
} elseif ($expr instanceof StaticCall && $expr->name instanceof Node\Identifier) {
if ($expr->class instanceof Name) {
$calleeType = $scope->resolveTypeByName($expr->class);
@@ -585,13 +685,15 @@ public function specifyTypesInCondition(
}
}
- if ($this->rememberFunctionValues) {
- return $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
- }
+ return $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
} elseif ($expr instanceof BooleanAnd || $expr instanceof LogicalAnd) {
+ if (!$scope instanceof MutatingScope) {
+ throw new ShouldNotHappenException();
+ }
$leftTypes = $this->specifyTypesInCondition($scope, $expr->left, $context);
- $rightTypes = $this->specifyTypesInCondition($scope, $expr->right, $context);
- $types = $context->true() ? $leftTypes->unionWith($rightTypes) : $leftTypes->intersectWith($rightTypes);
+ $rightScope = $scope->filterByTruthyValue($expr->left);
+ $rightTypes = $this->specifyTypesInCondition($rightScope, $expr->right, $context);
+ $types = $context->true() ? $leftTypes->unionWith($rightTypes) : $leftTypes->normalize($scope)->intersectWith($rightTypes->normalize($rightScope));
if ($context->false()) {
return new SpecifiedTypes(
$types->getSureTypes(),
@@ -599,16 +701,20 @@ public function specifyTypesInCondition(
false,
array_merge(
$this->processBooleanConditionalTypes($scope, $leftTypes, $rightTypes),
- $this->processBooleanConditionalTypes($scope, $rightTypes, $leftTypes)
- )
+ $this->processBooleanConditionalTypes($scope, $rightTypes, $leftTypes),
+ ),
);
}
return $types;
} elseif ($expr instanceof BooleanOr || $expr instanceof LogicalOr) {
+ if (!$scope instanceof MutatingScope) {
+ throw new ShouldNotHappenException();
+ }
$leftTypes = $this->specifyTypesInCondition($scope, $expr->left, $context);
- $rightTypes = $this->specifyTypesInCondition($scope, $expr->right, $context);
- $types = $context->true() ? $leftTypes->intersectWith($rightTypes) : $leftTypes->unionWith($rightTypes);
+ $rightScope = $scope->filterByFalseyValue($expr->left);
+ $rightTypes = $this->specifyTypesInCondition($rightScope, $expr->right, $context);
+ $types = $context->true() ? $leftTypes->normalize($scope)->intersectWith($rightTypes->normalize($rightScope)) : $leftTypes->unionWith($rightTypes);
if ($context->true()) {
return new SpecifiedTypes(
$types->getSureTypes(),
@@ -616,8 +722,8 @@ public function specifyTypesInCondition(
false,
array_merge(
$this->processBooleanConditionalTypes($scope, $leftTypes, $rightTypes),
- $this->processBooleanConditionalTypes($scope, $rightTypes, $leftTypes)
- )
+ $this->processBooleanConditionalTypes($scope, $rightTypes, $leftTypes),
+ ),
);
}
@@ -626,7 +732,7 @@ public function specifyTypesInCondition(
return $this->specifyTypesInCondition($scope, $expr->expr, $context->negate());
} elseif ($expr instanceof Node\Expr\Assign) {
if (!$scope instanceof MutatingScope) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if ($context->null()) {
return $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->expr, $context);
@@ -634,20 +740,12 @@ public function specifyTypesInCondition(
return $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->var, $context);
} elseif (
- (
- $expr instanceof Expr\Isset_
- && count($expr->vars) > 0
- && $context->true()
- )
- || ($expr instanceof Expr\Empty_ && $context->false())
+ $expr instanceof Expr\Isset_
+ && count($expr->vars) > 0
+ && $context->true()
) {
$vars = [];
- if ($expr instanceof Expr\Isset_) {
- $varsToIterate = $expr->vars;
- } else {
- $varsToIterate = [$expr->expr];
- }
- foreach ($varsToIterate as $var) {
+ foreach ($expr->vars as $var) {
$tmpVars = [$var];
while (
@@ -671,7 +769,7 @@ public function specifyTypesInCondition(
}
if (count($vars) === 0) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$types = null;
@@ -681,35 +779,22 @@ public function specifyTypesInCondition(
return new SpecifiedTypes([], []);
}
}
- if ($expr instanceof Expr\Isset_) {
- if (
- $var instanceof ArrayDimFetch
- && $var->dim !== null
- && !$scope->getType($var->var) instanceof MixedType
- ) {
- $type = $this->create(
- $var->var,
- new HasOffsetType($scope->getType($var->dim)),
- $context,
- false,
- $scope
- )->unionWith(
- $this->create($var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope)
- );
- } else {
- $type = $this->create($var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope);
- }
- } else {
+ if (
+ $var instanceof ArrayDimFetch
+ && $var->dim !== null
+ && !$scope->getType($var->var) instanceof MixedType
+ ) {
$type = $this->create(
- $var,
- new UnionType([
- new NullType(),
- new ConstantBooleanType(false),
- ]),
- TypeSpecifierContext::createFalse(),
+ $var->var,
+ new HasOffsetType($scope->getType($var->dim)),
+ $context,
false,
- $scope
+ $scope,
+ )->unionWith(
+ $this->create($var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope),
);
+ } else {
+ $type = $this->create($var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope);
}
if (
@@ -738,14 +823,6 @@ public function specifyTypesInCondition(
}
}
- if (
- $expr instanceof Expr\Empty_
- && (new ArrayType(new MixedType(), new MixedType()))->isSuperTypeOf($scope->getType($expr->expr))->yes()) {
- $types = $types->unionWith(
- $this->create($expr->expr, new NonEmptyArrayType(), $context->negate(), false, $scope)
- );
- }
-
return $types;
} elseif (
$expr instanceof Expr\BinaryOp\Coalesce
@@ -757,13 +834,15 @@ public function specifyTypesInCondition(
new NullType(),
TypeSpecifierContext::createFalse(),
false,
- $scope
+ $scope,
);
} elseif (
- $expr instanceof Expr\Empty_ && $context->truthy()
- && (new ArrayType(new MixedType(), new MixedType()))->isSuperTypeOf($scope->getType($expr->expr))->yes()
+ $expr instanceof Expr\Empty_
) {
- return $this->create($expr->expr, new NonEmptyArrayType(), $context->negate(), false, $scope);
+ return $this->specifyTypesInCondition($scope, new BooleanOr(
+ new Expr\BooleanNot(new Expr\Isset_([$expr->expr])),
+ new Expr\BooleanNot($expr->expr),
+ ), $context);
} elseif ($expr instanceof Expr\ErrorSuppress) {
return $this->specifyTypesInCondition($scope, $expr->expr, $context);
} elseif (
@@ -783,25 +862,25 @@ public function specifyTypesInCondition(
$scope,
new BooleanAnd(
new Expr\BinaryOp\NotIdentical($expr->var, new ConstFetch(new Name('null'))),
- new PropertyFetch($expr->var, $expr->name)
+ new PropertyFetch($expr->var, $expr->name),
),
- $context
+ $context,
);
$nullSafeTypes = $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
- return $context->true() ? $types->unionWith($nullSafeTypes) : $types->intersectWith($nullSafeTypes);
+ return $context->true() ? $types->unionWith($nullSafeTypes) : $types->normalize($scope)->intersectWith($nullSafeTypes->normalize($scope));
} elseif ($expr instanceof Expr\NullsafeMethodCall && !$context->null()) {
$types = $this->specifyTypesInCondition(
$scope,
new BooleanAnd(
new Expr\BinaryOp\NotIdentical($expr->var, new ConstFetch(new Name('null'))),
- new MethodCall($expr->var, $expr->name, $expr->args)
+ new MethodCall($expr->var, $expr->name, $expr->args),
),
- $context
+ $context,
);
$nullSafeTypes = $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
- return $context->true() ? $types->unionWith($nullSafeTypes) : $types->intersectWith($nullSafeTypes);
+ return $context->true() ? $types->unionWith($nullSafeTypes) : $types->normalize($scope)->intersectWith($nullSafeTypes->normalize($scope));
} elseif (!$context->null()) {
return $this->handleDefaultTruthyOrFalseyContext($context, $expr, $scope);
}
@@ -826,9 +905,6 @@ private function handleDefaultTruthyOrFalseyContext(TypeSpecifierContext $contex
}
/**
- * @param Scope $scope
- * @param SpecifiedTypes $leftTypes
- * @param SpecifiedTypes $rightTypes
* @return array
*/
private function processBooleanConditionalTypes(Scope $scope, SpecifiedTypes $leftTypes, SpecifiedTypes $rightTypes): array
@@ -861,7 +937,7 @@ private function processBooleanConditionalTypes(Scope $scope, SpecifiedTypes $le
$holders[$exprString][] = new ConditionalExpressionHolder(
$conditionExpressionTypes,
- new VariableTypeHolder(TypeCombinator::remove($scope->getType($expr), $type), TrinaryLogic::createYes())
+ new VariableTypeHolder(TypeCombinator::remove($scope->getType($expr), $type), TrinaryLogic::createYes()),
);
}
@@ -872,24 +948,22 @@ private function processBooleanConditionalTypes(Scope $scope, SpecifiedTypes $le
}
/**
- * @param \PHPStan\Analyser\Scope $scope
- * @param \PhpParser\Node\Expr\BinaryOp $binaryOperation
- * @return (Expr|\PHPStan\Type\ConstantScalarType)[]|null
+ * @return (Expr|ConstantScalarType)[]|null
*/
private function findTypeExpressionsFromBinaryOperation(Scope $scope, Node\Expr\BinaryOp $binaryOperation): ?array
{
$leftType = $scope->getType($binaryOperation->left);
$rightType = $scope->getType($binaryOperation->right);
if (
- $leftType instanceof \PHPStan\Type\ConstantScalarType
+ $leftType instanceof ConstantScalarType
&& !$binaryOperation->right instanceof ConstFetch
- && !$binaryOperation->right instanceof Expr\ClassConstFetch
+ && !$binaryOperation->right instanceof ClassConstFetch
) {
return [$binaryOperation->right, $leftType];
} elseif (
- $rightType instanceof \PHPStan\Type\ConstantScalarType
+ $rightType instanceof ConstantScalarType
&& !$binaryOperation->left instanceof ConstFetch
- && !$binaryOperation->left instanceof Expr\ClassConstFetch
+ && !$binaryOperation->left instanceof ClassConstFetch
) {
return [$binaryOperation->left, $rightType];
}
@@ -903,10 +977,10 @@ public function create(
Type $type,
TypeSpecifierContext $context,
bool $overwrite = false,
- ?Scope $scope = null
+ ?Scope $scope = null,
): SpecifiedTypes
{
- if ($expr instanceof New_ || $expr instanceof Instanceof_) {
+ if ($expr instanceof Instanceof_ || $expr instanceof Expr\List_ || $expr instanceof VirtualNode) {
return new SpecifiedTypes();
}
@@ -987,7 +1061,7 @@ private function createNullsafeTypes(Expr $expr, Scope $scope, TypeSpecifierCont
}
return $propertyFetchTypes->unionWith(
- $this->create($expr->var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope)
+ $this->create($expr->var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope),
);
}
@@ -999,7 +1073,7 @@ private function createNullsafeTypes(Expr $expr, Scope $scope, TypeSpecifierCont
}
return $methodCallTypes->unionWith(
- $this->create($expr->var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope)
+ $this->create($expr->var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope),
);
}
@@ -1044,7 +1118,7 @@ private function createRangeTypes(Expr $expr, Type $type, TypeSpecifierContext $
}
/**
- * @return \PHPStan\Type\FunctionTypeSpecifyingExtension[]
+ * @return FunctionTypeSpecifyingExtension[]
*/
private function getFunctionTypeSpecifyingExtensions(): array
{
@@ -1052,8 +1126,7 @@ private function getFunctionTypeSpecifyingExtensions(): array
}
/**
- * @param string $className
- * @return \PHPStan\Type\MethodTypeSpecifyingExtension[]
+ * @return MethodTypeSpecifyingExtension[]
*/
private function getMethodTypeSpecifyingExtensionsForClass(string $className): array
{
@@ -1069,8 +1142,7 @@ private function getMethodTypeSpecifyingExtensionsForClass(string $className): a
}
/**
- * @param string $className
- * @return \PHPStan\Type\StaticMethodTypeSpecifyingExtension[]
+ * @return StaticMethodTypeSpecifyingExtension[]
*/
private function getStaticMethodTypeSpecifyingExtensionsForClass(string $className): array
{
@@ -1086,8 +1158,7 @@ private function getStaticMethodTypeSpecifyingExtensionsForClass(string $classNa
}
/**
- * @param \PHPStan\Type\MethodTypeSpecifyingExtension[][]|\PHPStan\Type\StaticMethodTypeSpecifyingExtension[][] $extensions
- * @param string $className
+ * @param MethodTypeSpecifyingExtension[][]|StaticMethodTypeSpecifyingExtension[][] $extensions
* @return mixed[]
*/
private function getTypeSpecifyingExtensionsForType(array $extensions, string $className): array
diff --git a/src/Analyser/TypeSpecifierContext.php b/src/Analyser/TypeSpecifierContext.php
index 7a5feba27e..a14aba7112 100644
--- a/src/Analyser/TypeSpecifierContext.php
+++ b/src/Analyser/TypeSpecifierContext.php
@@ -2,6 +2,8 @@
namespace PHPStan\Analyser;
+use PHPStan\ShouldNotHappenException;
+
/** @api */
class TypeSpecifierContext
{
@@ -12,20 +14,18 @@ class TypeSpecifierContext
public const CONTEXT_FALSE = 0b0100;
public const CONTEXT_FALSEY_BUT_NOT_FALSE = 0b1000;
public const CONTEXT_FALSEY = self::CONTEXT_FALSE | self::CONTEXT_FALSEY_BUT_NOT_FALSE;
-
- private ?int $value;
+ public const CONTEXT_BITMASK = 0b1111;
/** @var self[] */
private static array $registry;
- private function __construct(?int $value)
+ private function __construct(private ?int $value)
{
- $this->value = $value;
}
private static function create(?int $value): self
{
- self::$registry[$value] = self::$registry[$value] ?? new self($value);
+ self::$registry[$value] ??= new self($value);
return self::$registry[$value];
}
@@ -57,9 +57,9 @@ public static function createNull(): self
public function negate(): self
{
if ($this->value === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
- return self::create(~$this->value);
+ return self::create(~$this->value & self::CONTEXT_BITMASK);
}
public function true(): bool
diff --git a/src/Analyser/TypeSpecifierFactory.php b/src/Analyser/TypeSpecifierFactory.php
index dc022b914d..17d638f457 100644
--- a/src/Analyser/TypeSpecifierFactory.php
+++ b/src/Analyser/TypeSpecifierFactory.php
@@ -6,6 +6,7 @@
use PHPStan\Broker\BrokerFactory;
use PHPStan\DependencyInjection\Container;
use PHPStan\Reflection\ReflectionProvider;
+use function array_merge;
class TypeSpecifierFactory
{
@@ -14,11 +15,8 @@ class TypeSpecifierFactory
public const METHOD_TYPE_SPECIFYING_EXTENSION_TAG = 'phpstan.typeSpecifier.methodTypeSpecifyingExtension';
public const STATIC_METHOD_TYPE_SPECIFYING_EXTENSION_TAG = 'phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension';
- private \PHPStan\DependencyInjection\Container $container;
-
- public function __construct(Container $container)
+ public function __construct(private Container $container)
{
- $this->container = $container;
}
public function create(): TypeSpecifier
@@ -26,10 +24,9 @@ public function create(): TypeSpecifier
$typeSpecifier = new TypeSpecifier(
$this->container->getByType(Standard::class),
$this->container->getByType(ReflectionProvider::class),
- $this->container->getParameter('featureToggles')['rememberFunctionValues'],
$this->container->getServicesByTag(self::FUNCTION_TYPE_SPECIFYING_EXTENSION_TAG),
$this->container->getServicesByTag(self::METHOD_TYPE_SPECIFYING_EXTENSION_TAG),
- $this->container->getServicesByTag(self::STATIC_METHOD_TYPE_SPECIFYING_EXTENSION_TAG)
+ $this->container->getServicesByTag(self::STATIC_METHOD_TYPE_SPECIFYING_EXTENSION_TAG),
);
foreach (array_merge(
@@ -37,7 +34,7 @@ public function create(): TypeSpecifier
$this->container->getServicesByTag(BrokerFactory::METHODS_CLASS_REFLECTION_EXTENSION_TAG),
$this->container->getServicesByTag(BrokerFactory::DYNAMIC_METHOD_RETURN_TYPE_EXTENSION_TAG),
$this->container->getServicesByTag(BrokerFactory::DYNAMIC_STATIC_METHOD_RETURN_TYPE_EXTENSION_TAG),
- $this->container->getServicesByTag(BrokerFactory::DYNAMIC_FUNCTION_RETURN_TYPE_EXTENSION_TAG)
+ $this->container->getServicesByTag(BrokerFactory::DYNAMIC_FUNCTION_RETURN_TYPE_EXTENSION_TAG),
) as $extension) {
if (!($extension instanceof TypeSpecifierAwareExtension)) {
continue;
diff --git a/src/Analyser/UndefinedVariableException.php b/src/Analyser/UndefinedVariableException.php
index 55be8974c0..6719de349c 100644
--- a/src/Analyser/UndefinedVariableException.php
+++ b/src/Analyser/UndefinedVariableException.php
@@ -2,18 +2,15 @@
namespace PHPStan\Analyser;
-class UndefinedVariableException extends \PHPStan\AnalysedCodeException
-{
-
- private \PHPStan\Analyser\Scope $scope;
+use PHPStan\AnalysedCodeException;
+use function sprintf;
- private string $variableName;
+class UndefinedVariableException extends AnalysedCodeException
+{
- public function __construct(Scope $scope, string $variableName)
+ public function __construct(private Scope $scope, private string $variableName)
{
parent::__construct(sprintf('Undefined variable: $%s', $variableName));
- $this->scope = $scope;
- $this->variableName = $variableName;
}
public function getScope(): Scope
diff --git a/src/Analyser/VariableTypeHolder.php b/src/Analyser/VariableTypeHolder.php
index aba3d385d7..70273de965 100644
--- a/src/Analyser/VariableTypeHolder.php
+++ b/src/Analyser/VariableTypeHolder.php
@@ -9,14 +9,8 @@
class VariableTypeHolder
{
- private \PHPStan\Type\Type $type;
-
- private \PHPStan\TrinaryLogic $certainty;
-
- public function __construct(Type $type, TrinaryLogic $certainty)
+ public function __construct(private Type $type, private TrinaryLogic $certainty)
{
- $this->type = $type;
- $this->certainty = $certainty;
}
public static function createYes(Type $type): self
@@ -47,7 +41,7 @@ public function and(self $other): self
}
return new self(
$type,
- $this->getCertainty()->and($other->getCertainty())
+ $this->getCertainty()->and($other->getCertainty()),
);
}
diff --git a/src/Broker/AnonymousClassNameHelper.php b/src/Broker/AnonymousClassNameHelper.php
index b7040689a2..b0e9bdfb40 100644
--- a/src/Broker/AnonymousClassNameHelper.php
+++ b/src/Broker/AnonymousClassNameHelper.php
@@ -2,41 +2,39 @@
namespace PHPStan\Broker;
+use PhpParser\Node;
use PHPStan\File\FileHelper;
use PHPStan\File\RelativePathHelper;
+use PHPStan\ShouldNotHappenException;
+use function md5;
+use function sprintf;
class AnonymousClassNameHelper
{
- private FileHelper $fileHelper;
-
- private RelativePathHelper $relativePathHelper;
-
public function __construct(
- FileHelper $fileHelper,
- RelativePathHelper $relativePathHelper
+ private FileHelper $fileHelper,
+ private RelativePathHelper $relativePathHelper,
)
{
- $this->fileHelper = $fileHelper;
- $this->relativePathHelper = $relativePathHelper;
}
public function getAnonymousClassName(
- \PhpParser\Node\Stmt\Class_ $classNode,
- string $filename
+ Node\Stmt\Class_ $classNode,
+ string $filename,
): string
{
if (isset($classNode->namespacedName)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$filename = $this->relativePathHelper->getRelativePath(
- $this->fileHelper->normalizePath($filename, '/')
+ $this->fileHelper->normalizePath($filename, '/'),
);
return sprintf(
'AnonymousClass%s',
- md5(sprintf('%s:%s', $filename, $classNode->getLine()))
+ md5(sprintf('%s:%s', $filename, $classNode->getLine())),
);
}
diff --git a/src/Broker/Broker.php b/src/Broker/Broker.php
index 4422e22e05..871078e13d 100644
--- a/src/Broker/Broker.php
+++ b/src/Broker/Broker.php
@@ -2,176 +2,142 @@
namespace PHPStan\Broker;
+use PhpParser\Node;
use PHPStan\Analyser\Scope;
-use PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider;
-use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\GlobalConstantReflection;
use PHPStan\Reflection\ReflectionProvider;
-use PHPStan\Type\OperatorTypeSpecifyingExtension;
-use PHPStan\Type\Type;
+use PHPStan\ShouldNotHappenException;
/** @api */
class Broker implements ReflectionProvider
{
- private ReflectionProvider $reflectionProvider;
-
- private DynamicReturnTypeExtensionRegistryProvider $dynamicReturnTypeExtensionRegistryProvider;
-
- private \PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider $operatorTypeSpecifyingExtensionRegistryProvider;
-
- /** @var string[] */
- private array $universalObjectCratesClasses;
-
- private static ?\PHPStan\Broker\Broker $instance = null;
+ private static ?Broker $instance = null;
/**
- * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider
- * @param \PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider $dynamicReturnTypeExtensionRegistryProvider
- * @param \PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider $operatorTypeSpecifyingExtensionRegistryProvider
* @param string[] $universalObjectCratesClasses
*/
public function __construct(
- ReflectionProvider $reflectionProvider,
- DynamicReturnTypeExtensionRegistryProvider $dynamicReturnTypeExtensionRegistryProvider,
- OperatorTypeSpecifyingExtensionRegistryProvider $operatorTypeSpecifyingExtensionRegistryProvider,
- array $universalObjectCratesClasses
+ private ReflectionProvider $reflectionProvider,
+ private array $universalObjectCratesClasses,
)
{
- $this->reflectionProvider = $reflectionProvider;
- $this->dynamicReturnTypeExtensionRegistryProvider = $dynamicReturnTypeExtensionRegistryProvider;
- $this->operatorTypeSpecifyingExtensionRegistryProvider = $operatorTypeSpecifyingExtensionRegistryProvider;
- $this->universalObjectCratesClasses = $universalObjectCratesClasses;
}
- public static function registerInstance(Broker $reflectionProvider): void
+ public static function registerInstance(Broker $broker): void
{
- self::$instance = $reflectionProvider;
+ self::$instance = $broker;
}
+ /**
+ * @deprecated Use PHPStan\Reflection\ReflectionProviderStaticAccessor instead
+ */
public static function getInstance(): Broker
{
if (self::$instance === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return self::$instance;
}
+ /**
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
+ */
public function hasClass(string $className): bool
{
return $this->reflectionProvider->hasClass($className);
}
+ /**
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
+ */
public function getClass(string $className): ClassReflection
{
return $this->reflectionProvider->getClass($className);
}
+ /**
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
+ */
public function getClassName(string $className): string
{
return $this->reflectionProvider->getClassName($className);
}
+ /**
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
+ */
public function supportsAnonymousClasses(): bool
{
return $this->reflectionProvider->supportsAnonymousClasses();
}
- public function getAnonymousClassReflection(\PhpParser\Node\Stmt\Class_ $classNode, Scope $scope): ClassReflection
+ /**
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
+ */
+ public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $scope): ClassReflection
{
return $this->reflectionProvider->getAnonymousClassReflection($classNode, $scope);
}
- public function hasFunction(\PhpParser\Node\Name $nameNode, ?Scope $scope): bool
- {
- return $this->reflectionProvider->hasFunction($nameNode, $scope);
- }
-
- public function getFunction(\PhpParser\Node\Name $nameNode, ?Scope $scope): FunctionReflection
- {
- return $this->reflectionProvider->getFunction($nameNode, $scope);
- }
-
- public function resolveFunctionName(\PhpParser\Node\Name $nameNode, ?Scope $scope): ?string
- {
- return $this->reflectionProvider->resolveFunctionName($nameNode, $scope);
- }
-
- public function hasConstant(\PhpParser\Node\Name $nameNode, ?Scope $scope): bool
- {
- return $this->reflectionProvider->hasConstant($nameNode, $scope);
- }
-
- public function getConstant(\PhpParser\Node\Name $nameNode, ?Scope $scope): GlobalConstantReflection
- {
- return $this->reflectionProvider->getConstant($nameNode, $scope);
- }
-
- public function resolveConstantName(\PhpParser\Node\Name $nameNode, ?Scope $scope): ?string
- {
- return $this->reflectionProvider->resolveConstantName($nameNode, $scope);
- }
-
/**
- * @return string[]
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
*/
- public function getUniversalObjectCratesClasses(): array
+ public function hasFunction(Node\Name $nameNode, ?Scope $scope): bool
{
- return $this->universalObjectCratesClasses;
+ return $this->reflectionProvider->hasFunction($nameNode, $scope);
}
/**
- * @param string $className
- * @return \PHPStan\Type\DynamicMethodReturnTypeExtension[]
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
*/
- public function getDynamicMethodReturnTypeExtensionsForClass(string $className): array
+ public function getFunction(Node\Name $nameNode, ?Scope $scope): FunctionReflection
{
- return $this->dynamicReturnTypeExtensionRegistryProvider->getRegistry()->getDynamicMethodReturnTypeExtensionsForClass($className);
+ return $this->reflectionProvider->getFunction($nameNode, $scope);
}
/**
- * @param string $className
- * @return \PHPStan\Type\DynamicStaticMethodReturnTypeExtension[]
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
*/
- public function getDynamicStaticMethodReturnTypeExtensionsForClass(string $className): array
+ public function resolveFunctionName(Node\Name $nameNode, ?Scope $scope): ?string
{
- return $this->dynamicReturnTypeExtensionRegistryProvider->getRegistry()->getDynamicStaticMethodReturnTypeExtensionsForClass($className);
+ return $this->reflectionProvider->resolveFunctionName($nameNode, $scope);
}
/**
- * @return OperatorTypeSpecifyingExtension[]
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
*/
- public function getOperatorTypeSpecifyingExtensions(string $operator, Type $leftType, Type $rightType): array
+ public function hasConstant(Node\Name $nameNode, ?Scope $scope): bool
{
- return $this->operatorTypeSpecifyingExtensionRegistryProvider->getRegistry()->getOperatorTypeSpecifyingExtensions($operator, $leftType, $rightType);
+ return $this->reflectionProvider->hasConstant($nameNode, $scope);
}
/**
- * @return \PHPStan\Type\DynamicFunctionReturnTypeExtension[]
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
*/
- public function getDynamicFunctionReturnTypeExtensions(): array
+ public function getConstant(Node\Name $nameNode, ?Scope $scope): GlobalConstantReflection
{
- return $this->dynamicReturnTypeExtensionRegistryProvider->getRegistry()->getDynamicFunctionReturnTypeExtensions();
+ return $this->reflectionProvider->getConstant($nameNode, $scope);
}
/**
- * @internal
- * @return DynamicReturnTypeExtensionRegistryProvider
+ * @deprecated Use PHPStan\Reflection\ReflectionProvider instead
*/
- public function getDynamicReturnTypeExtensionRegistryProvider(): DynamicReturnTypeExtensionRegistryProvider
+ public function resolveConstantName(Node\Name $nameNode, ?Scope $scope): ?string
{
- return $this->dynamicReturnTypeExtensionRegistryProvider;
+ return $this->reflectionProvider->resolveConstantName($nameNode, $scope);
}
/**
- * @internal
- * @return \PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider
+ * @deprecated Inject %universalObjectCratesClasses% parameter instead.
+ *
+ * @return string[]
*/
- public function getOperatorTypeSpecifyingExtensionRegistryProvider(): OperatorTypeSpecifyingExtensionRegistryProvider
+ public function getUniversalObjectCratesClasses(): array
{
- return $this->operatorTypeSpecifyingExtensionRegistryProvider;
+ return $this->universalObjectCratesClasses;
}
}
diff --git a/src/Broker/BrokerFactory.php b/src/Broker/BrokerFactory.php
index 18eba1875c..182b0ca3ca 100644
--- a/src/Broker/BrokerFactory.php
+++ b/src/Broker/BrokerFactory.php
@@ -3,8 +3,6 @@
namespace PHPStan\Broker;
use PHPStan\DependencyInjection\Container;
-use PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider;
-use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
use PHPStan\Reflection\ReflectionProvider;
class BrokerFactory
@@ -17,20 +15,15 @@ class BrokerFactory
public const DYNAMIC_FUNCTION_RETURN_TYPE_EXTENSION_TAG = 'phpstan.broker.dynamicFunctionReturnTypeExtension';
public const OPERATOR_TYPE_SPECIFYING_EXTENSION_TAG = 'phpstan.broker.operatorTypeSpecifyingExtension';
- private \PHPStan\DependencyInjection\Container $container;
-
- public function __construct(Container $container)
+ public function __construct(private Container $container)
{
- $this->container = $container;
}
public function create(): Broker
{
return new Broker(
$this->container->getByType(ReflectionProvider::class),
- $this->container->getByType(DynamicReturnTypeExtensionRegistryProvider::class),
- $this->container->getByType(OperatorTypeSpecifyingExtensionRegistryProvider::class),
- $this->container->getParameter('universalObjectCratesClasses')
+ $this->container->getParameter('universalObjectCratesClasses'),
);
}
diff --git a/src/Broker/ClassAutoloadingException.php b/src/Broker/ClassAutoloadingException.php
index 9898953138..23e99fe5a5 100644
--- a/src/Broker/ClassAutoloadingException.php
+++ b/src/Broker/ClassAutoloadingException.php
@@ -2,14 +2,19 @@
namespace PHPStan\Broker;
-class ClassAutoloadingException extends \PHPStan\AnalysedCodeException
+use PHPStan\AnalysedCodeException;
+use Throwable;
+use function get_class;
+use function sprintf;
+
+class ClassAutoloadingException extends AnalysedCodeException
{
private string $className;
public function __construct(
string $functionName,
- ?\Throwable $previous = null
+ ?Throwable $previous = null,
)
{
if ($previous !== null) {
@@ -17,12 +22,12 @@ public function __construct(
'%s (%s) thrown while looking for class %s.',
get_class($previous),
$previous->getMessage(),
- $functionName
+ $functionName,
), 0, $previous);
} else {
parent::__construct(sprintf(
'Class %s not found.',
- $functionName
+ $functionName,
), 0);
}
diff --git a/src/Broker/ClassNotFoundException.php b/src/Broker/ClassNotFoundException.php
index 1afb8d7458..d86a1bcb08 100644
--- a/src/Broker/ClassNotFoundException.php
+++ b/src/Broker/ClassNotFoundException.php
@@ -2,15 +2,15 @@
namespace PHPStan\Broker;
-class ClassNotFoundException extends \PHPStan\AnalysedCodeException
-{
+use PHPStan\AnalysedCodeException;
+use function sprintf;
- private string $className;
+class ClassNotFoundException extends AnalysedCodeException
+{
- public function __construct(string $functionName)
+ public function __construct(private string $className)
{
- parent::__construct(sprintf('Class %s was not found while trying to analyse it - discovering symbols is probably not configured properly.', $functionName));
- $this->className = $functionName;
+ parent::__construct(sprintf('Class %s was not found while trying to analyse it - discovering symbols is probably not configured properly.', $className));
}
public function getClassName(): string
diff --git a/src/Broker/ConstantNotFoundException.php b/src/Broker/ConstantNotFoundException.php
index 25a3f7b775..2c490e3653 100644
--- a/src/Broker/ConstantNotFoundException.php
+++ b/src/Broker/ConstantNotFoundException.php
@@ -2,15 +2,15 @@
namespace PHPStan\Broker;
-class ConstantNotFoundException extends \PHPStan\AnalysedCodeException
-{
+use PHPStan\AnalysedCodeException;
+use function sprintf;
- private string $constantName;
+class ConstantNotFoundException extends AnalysedCodeException
+{
- public function __construct(string $constantName)
+ public function __construct(private string $constantName)
{
parent::__construct(sprintf('Constant %s not found.', $constantName));
- $this->constantName = $constantName;
}
public function getConstantName(): string
diff --git a/src/Broker/FunctionNotFoundException.php b/src/Broker/FunctionNotFoundException.php
index f3966b1ef8..de2728001f 100644
--- a/src/Broker/FunctionNotFoundException.php
+++ b/src/Broker/FunctionNotFoundException.php
@@ -2,15 +2,15 @@
namespace PHPStan\Broker;
-class FunctionNotFoundException extends \PHPStan\AnalysedCodeException
-{
+use PHPStan\AnalysedCodeException;
+use function sprintf;
- private string $functionName;
+class FunctionNotFoundException extends AnalysedCodeException
+{
- public function __construct(string $functionName)
+ public function __construct(private string $functionName)
{
parent::__construct(sprintf('Function %s not found while trying to analyse it - discovering symbols is probably not configured properly.', $functionName));
- $this->functionName = $functionName;
}
public function getFunctionName(): string
diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php
index 1447662c58..0180ab6610 100644
--- a/src/Cache/Cache.php
+++ b/src/Cache/Cache.php
@@ -5,15 +5,11 @@
class Cache
{
- private \PHPStan\Cache\CacheStorage $storage;
-
- public function __construct(CacheStorage $storage)
+ public function __construct(private CacheStorage $storage)
{
- $this->storage = $storage;
}
/**
- * @param string $key
* @return mixed|null
*/
public function load(string $key, string $variableKey)
@@ -22,10 +18,7 @@ public function load(string $key, string $variableKey)
}
/**
- * @param string $key
- * @param string $variableKey
* @param mixed $data
- * @return void
*/
public function save(string $key, string $variableKey, $data): void
{
diff --git a/src/Cache/CacheItem.php b/src/Cache/CacheItem.php
index 17114f5fc5..61d9946c90 100644
--- a/src/Cache/CacheItem.php
+++ b/src/Cache/CacheItem.php
@@ -5,19 +5,11 @@
class CacheItem
{
- private string $variableKey;
-
- /** @var mixed */
- private $data;
-
/**
- * @param string $variableKey
* @param mixed $data
*/
- public function __construct(string $variableKey, $data)
+ public function __construct(private string $variableKey, private $data)
{
- $this->variableKey = $variableKey;
- $this->data = $data;
}
public function isVariableKeyValid(string $variableKey): bool
@@ -35,7 +27,6 @@ public function getData()
/**
* @param mixed[] $properties
- * @return self
*/
public static function __set_state(array $properties): self
{
diff --git a/src/Cache/CacheStorage.php b/src/Cache/CacheStorage.php
index a9227b0eca..c3a645eb2b 100644
--- a/src/Cache/CacheStorage.php
+++ b/src/Cache/CacheStorage.php
@@ -6,17 +6,12 @@ interface CacheStorage
{
/**
- * @param string $key
- * @param string $variableKey
* @return mixed|null
*/
public function load(string $key, string $variableKey);
/**
- * @param string $key
- * @param string $variableKey
* @param mixed $data
- * @return void
*/
public function save(string $key, string $variableKey, $data): void;
diff --git a/src/Cache/FileCacheStorage.php b/src/Cache/FileCacheStorage.php
index 1ef9950590..fbf0bb359e 100644
--- a/src/Cache/FileCacheStorage.php
+++ b/src/Cache/FileCacheStorage.php
@@ -2,17 +2,28 @@
namespace PHPStan\Cache;
+use InvalidArgumentException;
use Nette\Utils\Random;
use PHPStan\File\FileWriter;
+use PHPStan\ShouldNotHappenException;
+use function clearstatcache;
+use function error_get_last;
+use function is_dir;
+use function is_file;
+use function mkdir;
+use function rename;
+use function sha1;
+use function sprintf;
+use function substr;
+use function unlink;
+use function var_export;
+use const DIRECTORY_SEPARATOR;
class FileCacheStorage implements CacheStorage
{
- private string $directory;
-
- public function __construct(string $directory)
+ public function __construct(private string $directory)
{
- $this->directory = $directory;
}
private function makeDir(string $directory): void
@@ -29,19 +40,18 @@ private function makeDir(string $directory): void
}
$error = error_get_last();
- throw new \InvalidArgumentException(sprintf('Failed to create directory "%s" (%s).', $this->directory, $error !== null ? $error['message'] : 'unknown cause'));
+ throw new InvalidArgumentException(sprintf('Failed to create directory "%s" (%s).', $this->directory, $error !== null ? $error['message'] : 'unknown cause'));
}
}
/**
- * @param string $key
- * @param string $variableKey
* @return mixed|null
*/
public function load(string $key, string $variableKey)
{
- return (function (string $key, string $variableKey) {
- [,, $filePath] = $this->getFilePaths($key);
+ [,, $filePath] = $this->getFilePaths($key);
+
+ return (static function () use ($variableKey, $filePath) {
if (!is_file($filePath)) {
return null;
}
@@ -55,14 +65,11 @@ public function load(string $key, string $variableKey)
}
return $cacheItem->getData();
- })($key, $variableKey);
+ })();
}
/**
- * @param string $key
- * @param string $variableKey
* @param mixed $data
- * @return void
*/
public function save(string $key, string $variableKey, $data): void
{
@@ -76,14 +83,14 @@ public function save(string $key, string $variableKey, $data): void
$exported = @var_export(new CacheItem($variableKey, $data), true);
$errorAfter = error_get_last();
if ($errorAfter !== null && $errorBefore !== $errorAfter) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Error occurred while saving item %s (%s) to cache: %s', $key, $variableKey, $errorAfter['message']));
+ throw new ShouldNotHappenException(sprintf('Error occurred while saving item %s (%s) to cache: %s', $key, $variableKey, $errorAfter['message']));
}
FileWriter::write(
$tmpPath,
sprintf(
" */
+ /** @var array */
private array $storage = [];
/**
- * @param string $key
- * @param string $variableKey
* @return mixed|null
*/
public function load(string $key, string $variableKey)
@@ -28,10 +26,7 @@ public function load(string $key, string $variableKey)
}
/**
- * @param string $key
- * @param string $variableKey
* @param mixed $data
- * @return void
*/
public function save(string $key, string $variableKey, $data): void
{
diff --git a/src/Command/AnalyseApplication.php b/src/Command/AnalyseApplication.php
index 4d1656b0c6..73aab3de3e 100644
--- a/src/Command/AnalyseApplication.php
+++ b/src/Command/AnalyseApplication.php
@@ -7,50 +7,31 @@
use PHPStan\Analyser\ResultCache\ResultCacheManagerFactory;
use PHPStan\Internal\BytesHelper;
use PHPStan\PhpDoc\StubValidator;
+use PHPStan\ShouldNotHappenException;
use Symfony\Component\Console\Input\InputInterface;
+use function array_merge;
+use function count;
+use function is_string;
+use function memory_get_peak_usage;
+use function microtime;
+use function sprintf;
class AnalyseApplication
{
- private AnalyserRunner $analyserRunner;
-
- private \PHPStan\PhpDoc\StubValidator $stubValidator;
-
- private \PHPStan\Analyser\ResultCache\ResultCacheManagerFactory $resultCacheManagerFactory;
-
- private IgnoredErrorHelper $ignoredErrorHelper;
-
- private string $memoryLimitFile;
-
- private int $internalErrorsCountLimit;
-
public function __construct(
- AnalyserRunner $analyserRunner,
- StubValidator $stubValidator,
- ResultCacheManagerFactory $resultCacheManagerFactory,
- IgnoredErrorHelper $ignoredErrorHelper,
- string $memoryLimitFile,
- int $internalErrorsCountLimit
+ private AnalyserRunner $analyserRunner,
+ private StubValidator $stubValidator,
+ private ResultCacheManagerFactory $resultCacheManagerFactory,
+ private IgnoredErrorHelper $ignoredErrorHelper,
+ private int $internalErrorsCountLimit,
)
{
- $this->analyserRunner = $analyserRunner;
- $this->stubValidator = $stubValidator;
- $this->resultCacheManagerFactory = $resultCacheManagerFactory;
- $this->ignoredErrorHelper = $ignoredErrorHelper;
- $this->memoryLimitFile = $memoryLimitFile;
- $this->internalErrorsCountLimit = $internalErrorsCountLimit;
}
/**
* @param string[] $files
- * @param bool $onlyFiles
- * @param \PHPStan\Command\Output $stdOutput
- * @param \PHPStan\Command\Output $errorOutput
- * @param bool $defaultLevelUsed
- * @param bool $debug
- * @param string|null $projectConfigFile
* @param mixed[]|null $projectConfigArray
- * @return AnalysisResult
*/
public function analyse(
array $files,
@@ -61,38 +42,14 @@ public function analyse(
bool $debug,
?string $projectConfigFile,
?array $projectConfigArray,
- InputInterface $input
+ InputInterface $input,
): AnalysisResult
{
- $this->updateMemoryLimitFile();
- $projectStubFiles = [];
- if ($projectConfigArray !== null) {
- $projectStubFiles = $projectConfigArray['parameters']['stubFiles'] ?? [];
- }
- $stubErrors = $this->stubValidator->validate($projectStubFiles, $debug);
-
- register_shutdown_function(function (): void {
- $error = error_get_last();
- if ($error === null) {
- return;
- }
- if ($error['type'] !== E_ERROR) {
- return;
- }
-
- if (strpos($error['message'], 'Allowed memory size') !== false) {
- return;
- }
-
- @unlink($this->memoryLimitFile);
- });
-
$resultCacheManager = $this->resultCacheManagerFactory->create([]);
$ignoredErrorHelperResult = $this->ignoredErrorHelper->initialize();
if (count($ignoredErrorHelperResult->getErrors()) > 0) {
$errors = $ignoredErrorHelperResult->getErrors();
- $warnings = [];
$internalErrors = [];
$savedResultCache = false;
if ($errorOutput->isDebug()) {
@@ -107,13 +64,28 @@ public function analyse(
$projectConfigFile,
$stdOutput,
$errorOutput,
- $input
+ $input,
);
+
+ $projectStubFiles = [];
+ if ($projectConfigArray !== null) {
+ $projectStubFiles = $projectConfigArray['parameters']['stubFiles'] ?? [];
+ }
+ if ($resultCache->isFullAnalysis() && count($projectStubFiles) !== 0) {
+ $stubErrors = $this->stubValidator->validate($projectStubFiles, $debug);
+ $intermediateAnalyserResult = new AnalyserResult(
+ array_merge($intermediateAnalyserResult->getErrors(), $stubErrors),
+ $intermediateAnalyserResult->getInternalErrors(),
+ $intermediateAnalyserResult->getDependencies(),
+ $intermediateAnalyserResult->getExportedNodes(),
+ $intermediateAnalyserResult->hasReachedInternalErrorsCountLimit(),
+ );
+ }
+
$resultCacheResult = $resultCacheManager->process($intermediateAnalyserResult, $resultCache, $errorOutput, $onlyFiles, true);
$analyserResult = $resultCacheResult->getAnalyserResult();
$internalErrors = $analyserResult->getInternalErrors();
$errors = $ignoredErrorHelperResult->process($analyserResult->getErrors(), $onlyFiles, $files, count($internalErrors) > 0 || $analyserResult->hasReachedInternalErrorsCountLimit());
- $warnings = $ignoredErrorHelperResult->getWarnings();
$savedResultCache = $resultCacheResult->isSaved();
if ($analyserResult->hasReachedInternalErrorsCountLimit()) {
$errors[] = sprintf('Reached internal errors count limit of %d, exiting...', $this->internalErrorsCountLimit);
@@ -121,8 +93,6 @@ public function analyse(
$errors = array_merge($errors, $internalErrors);
}
- $errors = array_merge($stubErrors, $errors);
-
$fileSpecificErrors = [];
$notFileSpecificErrors = [];
foreach ($errors as $error) {
@@ -138,10 +108,10 @@ public function analyse(
$fileSpecificErrors,
$notFileSpecificErrors,
$internalErrors,
- $warnings,
+ [],
$defaultLevelUsed,
$projectConfigFile,
- $savedResultCache
+ $savedResultCache,
);
}
@@ -156,7 +126,7 @@ private function runAnalyser(
?string $projectConfigFile,
Output $stdOutput,
Output $errorOutput,
- InputInterface $input
+ InputInterface $input,
): AnalyserResult
{
$filesCount = count($files);
@@ -170,32 +140,31 @@ private function runAnalyser(
if (!$debug) {
$progressStarted = false;
- $fileOrder = 0;
$preFileCallback = null;
- $postFileCallback = function (int $step) use ($errorOutput, &$progressStarted, $allAnalysedFilesCount, $filesCount, &$fileOrder): void {
+ $postFileCallback = static function (int $step) use ($errorOutput, &$progressStarted, $allAnalysedFilesCount, $filesCount): void {
if (!$progressStarted) {
$errorOutput->getStyle()->progressStart($allAnalysedFilesCount);
$errorOutput->getStyle()->progressAdvance($allAnalysedFilesCount - $filesCount);
$progressStarted = true;
}
$errorOutput->getStyle()->progressAdvance($step);
-
- if ($fileOrder >= 100) {
- $this->updateMemoryLimitFile();
- $fileOrder = 0;
- }
- $fileOrder += $step;
};
} else {
- $preFileCallback = static function (string $file) use ($stdOutput): void {
+ $startTime = null;
+ $preFileCallback = static function (string $file) use ($stdOutput, &$startTime): void {
$stdOutput->writeLineFormatted($file);
+ $startTime = microtime(true);
};
$postFileCallback = null;
if ($stdOutput->isDebug()) {
$previousMemory = memory_get_peak_usage(true);
- $postFileCallback = static function () use ($stdOutput, &$previousMemory): void {
+ $postFileCallback = static function () use ($stdOutput, &$previousMemory, &$startTime): void {
+ if ($startTime === null) {
+ throw new ShouldNotHappenException();
+ }
$currentTotalMemory = memory_get_peak_usage(true);
- $stdOutput->writeLineFormatted(sprintf('--- consumed %s, total %s', BytesHelper::bytes($currentTotalMemory - $previousMemory), BytesHelper::bytes($currentTotalMemory)));
+ $elapsedTime = microtime(true) - $startTime;
+ $stdOutput->writeLineFormatted(sprintf('--- consumed %s, total %s, took %.2f s', BytesHelper::bytes($currentTotalMemory - $previousMemory), BytesHelper::bytes($currentTotalMemory), $elapsedTime));
$previousMemory = $currentTotalMemory;
};
}
@@ -210,11 +179,4 @@ private function runAnalyser(
return $analyserResult;
}
- private function updateMemoryLimitFile(): void
- {
- $bytes = memory_get_peak_usage(true);
- $megabytes = ceil($bytes / 1024 / 1024);
- file_put_contents($this->memoryLimitFile, sprintf('%d MB', $megabytes));
- }
-
}
diff --git a/src/Command/AnalyseCommand.php b/src/Command/AnalyseCommand.php
index 4a110d3bdc..cf441654c6 100644
--- a/src/Command/AnalyseCommand.php
+++ b/src/Command/AnalyseCommand.php
@@ -3,23 +3,48 @@
namespace PHPStan\Command;
use OndraM\CiDetector\CiDetector;
+use OndraM\CiDetector\Exception\CiNotDetectedException;
use PHPStan\Analyser\ResultCache\ResultCacheClearer;
use PHPStan\Command\ErrorFormatter\BaselineNeonErrorFormatter;
use PHPStan\Command\ErrorFormatter\ErrorFormatter;
use PHPStan\Command\ErrorFormatter\TableErrorFormatter;
use PHPStan\Command\Symfony\SymfonyOutput;
use PHPStan\Command\Symfony\SymfonyStyle;
+use PHPStan\File\CouldNotWriteFileException;
use PHPStan\File\FileWriter;
use PHPStan\File\ParentDirectoryRelativePathHelper;
+use PHPStan\File\PathNotFoundException;
+use PHPStan\ShouldNotHappenException;
+use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StringInput;
+use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\StreamOutput;
+use Throwable;
+use function array_map;
+use function count;
+use function dirname;
+use function fopen;
+use function get_class;
+use function implode;
+use function is_array;
+use function is_bool;
+use function is_dir;
+use function is_string;
+use function mkdir;
+use function pathinfo;
+use function rewind;
+use function sprintf;
use function stream_get_contents;
+use function strlen;
+use function substr;
+use const PATHINFO_BASENAME;
+use const PATHINFO_EXTENSION;
-class AnalyseCommand extends \Symfony\Component\Console\Command\Command
+class AnalyseCommand extends Command
{
private const NAME = 'analyse';
@@ -28,18 +53,14 @@ class AnalyseCommand extends \Symfony\Component\Console\Command\Command
public const DEFAULT_LEVEL = CommandHelper::DEFAULT_LEVEL;
- /** @var string[] */
- private array $composerAutoloaderProjectPaths;
-
/**
* @param string[] $composerAutoloaderProjectPaths
*/
public function __construct(
- array $composerAutoloaderProjectPaths
+ private array $composerAutoloaderProjectPaths,
)
{
parent::__construct();
- $this->composerAutoloaderProjectPaths = $composerAutoloaderProjectPaths;
}
protected function configure(): void
@@ -48,14 +69,14 @@ protected function configure(): void
->setDescription('Analyses source code')
->setDefinition([
new InputArgument('paths', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Paths with source code to run analysis on'),
- new InputOption('paths-file', null, InputOption::VALUE_REQUIRED, 'Path to a file with a list of paths to run analysis on'),
new InputOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Path to project configuration file'),
new InputOption(self::OPTION_LEVEL, 'l', InputOption::VALUE_REQUIRED, 'Level of rule options - the higher the stricter'),
new InputOption(ErrorsConsoleStyle::OPTION_NO_PROGRESS, null, InputOption::VALUE_NONE, 'Do not show progress bar, only results'),
new InputOption('debug', null, InputOption::VALUE_NONE, 'Show debug information - which file is analysed, do not catch internal errors'),
new InputOption('autoload-file', 'a', InputOption::VALUE_REQUIRED, 'Project\'s additional autoload file path'),
new InputOption('error-format', null, InputOption::VALUE_REQUIRED, 'Format in which to print the result of the analysis', null),
- new InputOption('generate-baseline', null, InputOption::VALUE_OPTIONAL, 'Path to a file where the baseline should be saved', false),
+ new InputOption('generate-baseline', 'b', InputOption::VALUE_OPTIONAL, 'Path to a file where the baseline should be saved', false),
+ new InputOption('allow-empty-baseline', null, InputOption::VALUE_NONE, 'Do not error out when the generated baseline is empty'),
new InputOption('memory-limit', null, InputOption::VALUE_REQUIRED, 'Memory limit for analysis'),
new InputOption('xdebug', null, InputOption::VALUE_NONE, 'Allow running with XDebug for debugging purposes'),
new InputOption('fix', null, InputOption::VALUE_NONE, 'Launch PHPStan Pro'),
@@ -77,7 +98,7 @@ protected function initialize(InputInterface $input, OutputInterface $output): v
if ((bool) $input->getOption('debug')) {
$application = $this->getApplication();
if ($application === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$application->setCatchExceptions(false);
return;
@@ -86,12 +107,23 @@ protected function initialize(InputInterface $input, OutputInterface $output): v
protected function execute(InputInterface $input, OutputInterface $output): int
{
+ if ($output instanceof ConsoleOutputInterface) {
+ $errorOutput = $output->getErrorOutput();
+ $errorOutput->writeln('');
+ $errorOutput->writeln("⚠️ You're running a dev-master alias of phpstan/phpstan.️");
+ $errorOutput->writeln('');
+ $errorOutput->writeln('This alias is no longer updated. It\'s recommended to switch');
+ $errorOutput->writeln('to stable releases by using the caret ^1.4 version range.');
+ $errorOutput->writeln('');
+ $errorOutput->writeln('See latest releases at:');
+ $errorOutput->writeln('https://github.com/phpstan/phpstan/releases>');
+ $errorOutput->writeln('');
+ }
$paths = $input->getArgument('paths');
$memoryLimit = $input->getOption('memory-limit');
$autoloadFile = $input->getOption('autoload-file');
$configuration = $input->getOption('configuration');
$level = $input->getOption(self::OPTION_LEVEL);
- $pathsFile = $input->getOption('paths-file');
$allowXdebug = $input->getOption('xdebug');
$debugEnabled = (bool) $input->getOption('debug');
$fix = (bool) $input->getOption('fix') || (bool) $input->getOption('watch') || (bool) $input->getOption('pro');
@@ -104,16 +136,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$generateBaselineFile = 'phpstan-baseline.neon';
}
+ $allowEmptyBaseline = (bool) $input->getOption('allow-empty-baseline');
+
if (
!is_array($paths)
|| (!is_string($memoryLimit) && $memoryLimit !== null)
|| (!is_string($autoloadFile) && $autoloadFile !== null)
|| (!is_string($configuration) && $configuration !== null)
|| (!is_string($level) && $level !== null)
- || (!is_string($pathsFile) && $pathsFile !== null)
|| (!is_bool($allowXdebug))
) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
try {
@@ -121,7 +154,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$input,
$output,
$paths,
- $pathsFile,
$memoryLimit,
$autoloadFile,
$this->composerAutoloaderProjectPaths,
@@ -129,13 +161,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$generateBaselineFile,
$level,
$allowXdebug,
- true,
- $debugEnabled
+ $debugEnabled,
);
- } catch (\PHPStan\Command\InceptionNotSuccessfulException $e) {
+ } catch (InceptionNotSuccessfulException $e) {
return 1;
}
+ if ($generateBaselineFile === null && $allowEmptyBaseline) {
+ $inceptionResult->getStdOutput()->getStyle()->error('You must pass the --generate-baseline option alongside --allow-empty-baseline.');
+ return $inceptionResult->handleReturn(1);
+ }
+
$errorOutput = $inceptionResult->getErrorOutput();
$obsoleteDockerImage = $_SERVER['PHPSTAN_OBSOLETE_DOCKER_IMAGE'] ?? 'false';
if ($obsoleteDockerImage === 'true') {
@@ -149,7 +185,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$errorFormat = $input->getOption('error-format');
if (!is_string($errorFormat) && $errorFormat !== null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if ($errorFormat === null) {
@@ -163,7 +199,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
} elseif ($ci->getCiName() === CiDetector::CI_TEAMCITY) {
$errorFormat = 'teamcity';
}
- } catch (\OndraM\CiDetector\Exception\CiNotDetectedException $e) {
+ } catch (CiNotDetectedException) {
// pass
}
}
@@ -174,26 +210,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$errorOutput->writeLineFormatted(sprintf(
'Error formatter "%s" not found. Available error formatters are: %s',
$errorFormat,
- implode(', ', array_map(static function (string $name): string {
- return substr($name, strlen('errorFormatter.'));
- }, $container->findServiceNamesByType(ErrorFormatter::class)))
+ implode(', ', array_map(static fn (string $name): string => substr($name, strlen('errorFormatter.')), $container->findServiceNamesByType(ErrorFormatter::class))),
));
return 1;
}
- if ($errorFormat === 'baselineNeon') {
- $errorOutput = $inceptionResult->getErrorOutput();
- $errorOutput->writeLineFormatted('⚠️ You\'re using an obsolete option --error-format baselineNeon>. ⚠️️');
- $errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted(' There\'s a new and much better option --generate-baseline>. Here are the advantages:');
- $errorOutput->writeLineFormatted(' 1) The current baseline file does not have to be commented-out');
- $errorOutput->writeLineFormatted(' nor emptied when generating the new baseline. It\'s excluded automatically.');
- $errorOutput->writeLineFormatted(' 2) Output no longer has to be redirected to a file, PHPStan saves the baseline');
- $errorOutput->writeLineFormatted(' to a specified path (defaults to phpstan-baseline.neon>).');
- $errorOutput->writeLineFormatted(' 3) Baseline contains correct relative paths if saved to a subdirectory.');
- $errorOutput->writeLineFormatted('');
- }
-
$generateBaselineFile = $inceptionResult->getGenerateBaselineFile();
if ($generateBaselineFile !== null) {
$baselineExtension = pathinfo($generateBaselineFile, PATHINFO_EXTENSION);
@@ -211,7 +232,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
try {
[$files, $onlyFiles] = $inceptionResult->getFiles();
- } catch (\PHPStan\File\PathNotFoundException $e) {
+ } catch (PathNotFoundException $e) {
$inceptionResult->getErrorOutput()->writeLineFormatted(sprintf('%s', $e->getMessage()));
return 1;
}
@@ -221,24 +242,44 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$debug = $input->getOption('debug');
if (!is_bool($debug)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
- $analysisResult = $application->analyse(
- $files,
- $onlyFiles,
- $inceptionResult->getStdOutput(),
- $inceptionResult->getErrorOutput(),
- $inceptionResult->isDefaultLevelUsed(),
- $debug,
- $inceptionResult->getProjectConfigFile(),
- $inceptionResult->getProjectConfigArray(),
- $input
- );
+ try {
+ $analysisResult = $application->analyse(
+ $files,
+ $onlyFiles,
+ $inceptionResult->getStdOutput(),
+ $inceptionResult->getErrorOutput(),
+ $inceptionResult->isDefaultLevelUsed(),
+ $debug,
+ $inceptionResult->getProjectConfigFile(),
+ $inceptionResult->getProjectConfigArray(),
+ $input,
+ );
+ } catch (Throwable $t) {
+ if ($debug) {
+ $inceptionResult->getStdOutput()->writeRaw(sprintf(
+ 'Uncaught %s: %s in %s:%d',
+ get_class($t),
+ $t->getMessage(),
+ $t->getFile(),
+ $t->getLine(),
+ ));
+ $inceptionResult->getStdOutput()->writeLineFormatted('');
+ $inceptionResult->getStdOutput()->writeRaw($t->getTraceAsString());
+ $inceptionResult->getStdOutput()->writeLineFormatted('');
+
+ return $inceptionResult->handleReturn(1);
+ }
+
+ throw $t;
+ }
if ($generateBaselineFile !== null) {
- if (!$analysisResult->hasErrors()) {
+ if (!$allowEmptyBaseline && !$analysisResult->hasErrors()) {
$inceptionResult->getStdOutput()->getStyle()->error('No errors were found during the analysis. Baseline could not be generated.');
+ $inceptionResult->getStdOutput()->writeLineFormatted('To allow generating empty baselines, pass --allow-empty-baseline> option.');
return $inceptionResult->handleReturn(1);
}
@@ -260,7 +301,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
rewind($stream);
$baselineContents = stream_get_contents($stream);
if ($baselineContents === false) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if (!is_dir($baselineFileDirectory)) {
@@ -274,7 +315,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
try {
FileWriter::write($generateBaselineFile, $baselineContents);
- } catch (\PHPStan\File\CouldNotWriteFileException $e) {
+ } catch (CouldNotWriteFileException $e) {
$inceptionResult->getStdOutput()->writeLineFormatted($e->getMessage());
return $inceptionResult->handleReturn(1);
@@ -337,7 +378,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
[],
$analysisResult->isDefaultLevelUsed(),
$analysisResult->getProjectConfigFile(),
- $analysisResult->isResultCacheSaved()
+ $analysisResult->isResultCacheSaved(),
);
$stdOutput = $inceptionResult->getStdOutput();
@@ -380,7 +421,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return $inceptionResult->handleReturn(1);
}
- $inceptionResult->handleReturn(0); // delete memory limit file
+ $inceptionResult->handleReturn(0);
/** @var FixerApplication $fixerApplication */
$fixerApplication = $container->getByType(FixerApplication::class);
@@ -393,7 +434,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$analysisResult->getFileSpecificErrors(),
$analysisResult->getNotFileSpecificErrors(),
count($files),
- $_SERVER['argv'][0]
+ $_SERVER['argv'][0],
);
}
@@ -401,7 +442,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$errorFormatter = $container->getService($errorFormatterServiceName);
return $inceptionResult->handleReturn(
- $errorFormatter->formatErrors($analysisResult, $inceptionResult->getStdOutput())
+ $errorFormatter->formatErrors($analysisResult, $inceptionResult->getStdOutput()),
);
}
@@ -409,7 +450,7 @@ private function createStreamOutput(): StreamOutput
{
$resource = fopen('php://memory', 'w', false);
if ($resource === false) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return new StreamOutput($resource);
}
diff --git a/src/Command/AnalyserRunner.php b/src/Command/AnalyserRunner.php
index e52904d869..633b11e344 100644
--- a/src/Command/AnalyserRunner.php
+++ b/src/Command/AnalyserRunner.php
@@ -2,61 +2,47 @@
namespace PHPStan\Command;
+use Closure;
use PHPStan\Analyser\Analyser;
use PHPStan\Analyser\AnalyserResult;
use PHPStan\Parallel\ParallelAnalyser;
use PHPStan\Parallel\Scheduler;
use PHPStan\Process\CpuCoreCounter;
use Symfony\Component\Console\Input\InputInterface;
+use function array_filter;
+use function array_values;
+use function count;
+use function is_file;
class AnalyserRunner
{
- private Scheduler $scheduler;
-
- private Analyser $analyser;
-
- private ParallelAnalyser $parallelAnalyser;
-
- private CpuCoreCounter $cpuCoreCounter;
-
public function __construct(
- Scheduler $scheduler,
- Analyser $analyser,
- ParallelAnalyser $parallelAnalyser,
- CpuCoreCounter $cpuCoreCounter
+ private Scheduler $scheduler,
+ private Analyser $analyser,
+ private ParallelAnalyser $parallelAnalyser,
+ private CpuCoreCounter $cpuCoreCounter,
)
{
- $this->scheduler = $scheduler;
- $this->analyser = $analyser;
- $this->parallelAnalyser = $parallelAnalyser;
- $this->cpuCoreCounter = $cpuCoreCounter;
}
/**
* @param string[] $files
* @param string[] $allAnalysedFiles
- * @param (\Closure(string $file): void)|null $preFileCallback
- * @param (\Closure(int): void)|null $postFileCallback
- * @param bool $debug
- * @param bool $allowParallel
- * @param string|null $projectConfigFile
- * @param string|null $tmpFile
- * @param string|null $insteadOfFile
- * @param InputInterface $input
- * @return AnalyserResult
+ * @param Closure(string $file): void|null $preFileCallback
+ * @param Closure(int ): void|null $postFileCallback
*/
public function runAnalyser(
array $files,
array $allAnalysedFiles,
- ?\Closure $preFileCallback,
- ?\Closure $postFileCallback,
+ ?Closure $preFileCallback,
+ ?Closure $postFileCallback,
bool $debug,
bool $allowParallel,
?string $projectConfigFile,
?string $tmpFile,
?string $insteadOfFile,
- InputInterface $input
+ InputInterface $input,
): AnalyserResult
{
$filesCount = count($files);
@@ -66,7 +52,7 @@ public function runAnalyser(
$schedule = $this->scheduler->scheduleWork($this->cpuCoreCounter->getNumberOfCpuCores(), $files);
$mainScript = null;
- if (isset($_SERVER['argv'][0]) && file_exists($_SERVER['argv'][0])) {
+ if (isset($_SERVER['argv'][0]) && is_file($_SERVER['argv'][0])) {
$mainScript = $_SERVER['argv'][0];
}
@@ -74,7 +60,7 @@ public function runAnalyser(
!$debug
&& $allowParallel
&& $mainScript !== null
- && $schedule->getNumberOfProcesses() > 1
+ && $schedule->getNumberOfProcesses() > 0
) {
return $this->parallelAnalyser->analyse($schedule, $mainScript, $postFileCallback, $projectConfigFile, $tmpFile, $insteadOfFile, $input);
}
@@ -84,20 +70,18 @@ public function runAnalyser(
$preFileCallback,
$postFileCallback,
$debug,
- $this->switchTmpFile($allAnalysedFiles, $insteadOfFile, $tmpFile)
+ $this->switchTmpFile($allAnalysedFiles, $insteadOfFile, $tmpFile),
);
}
/**
* @param string[] $analysedFiles
- * @param string|null $insteadOfFile
- * @param string|null $tmpFile
* @return string[]
*/
private function switchTmpFile(
array $analysedFiles,
?string $insteadOfFile,
- ?string $tmpFile
+ ?string $tmpFile,
): array
{
$analysedFiles = array_values(array_filter($analysedFiles, static function (string $file) use ($insteadOfFile): bool {
diff --git a/src/Command/AnalysisResult.php b/src/Command/AnalysisResult.php
index 2398b0833c..92cb217e21 100644
--- a/src/Command/AnalysisResult.php
+++ b/src/Command/AnalysisResult.php
@@ -3,69 +3,46 @@
namespace PHPStan\Command;
use PHPStan\Analyser\Error;
+use function count;
+use function usort;
+/** @api */
class AnalysisResult
{
- /** @var \PHPStan\Analyser\Error[] sorted by their file name, line number and message */
+ /** @var Error[] sorted by their file name, line number and message */
private array $fileSpecificErrors;
- /** @var string[] */
- private array $notFileSpecificErrors;
-
- /** @var string[] */
- private array $internalErrors;
-
- /** @var string[] */
- private array $warnings;
-
- private bool $defaultLevelUsed;
-
- private ?string $projectConfigFile;
-
- private bool $savedResultCache;
-
/**
- * @param \PHPStan\Analyser\Error[] $fileSpecificErrors
+ * @param Error[] $fileSpecificErrors
* @param string[] $notFileSpecificErrors
* @param string[] $internalErrors
* @param string[] $warnings
- * @param bool $defaultLevelUsed
- * @param string|null $projectConfigFile
- * @param bool $savedResultCache
*/
public function __construct(
array $fileSpecificErrors,
- array $notFileSpecificErrors,
- array $internalErrors,
- array $warnings,
- bool $defaultLevelUsed,
- ?string $projectConfigFile,
- bool $savedResultCache
+ private array $notFileSpecificErrors,
+ private array $internalErrors,
+ private array $warnings,
+ private bool $defaultLevelUsed,
+ private ?string $projectConfigFile,
+ private bool $savedResultCache,
)
{
usort(
$fileSpecificErrors,
- static function (Error $a, Error $b): int {
- return [
- $a->getFile(),
- $a->getLine(),
- $a->getMessage(),
- ] <=> [
- $b->getFile(),
- $b->getLine(),
- $b->getMessage(),
- ];
- }
+ static fn (Error $a, Error $b): int => [
+ $a->getFile(),
+ $a->getLine(),
+ $a->getMessage(),
+ ] <=> [
+ $b->getFile(),
+ $b->getLine(),
+ $b->getMessage(),
+ ],
);
$this->fileSpecificErrors = $fileSpecificErrors;
- $this->notFileSpecificErrors = $notFileSpecificErrors;
- $this->internalErrors = $internalErrors;
- $this->warnings = $warnings;
- $this->defaultLevelUsed = $defaultLevelUsed;
- $this->projectConfigFile = $projectConfigFile;
- $this->savedResultCache = $savedResultCache;
}
public function hasErrors(): bool
@@ -79,7 +56,7 @@ public function getTotalErrorsCount(): int
}
/**
- * @return \PHPStan\Analyser\Error[] sorted by their file name, line number and message
+ * @return Error[] sorted by their file name, line number and message
*/
public function getFileSpecificErrors(): array
{
diff --git a/src/Command/ClearResultCacheCommand.php b/src/Command/ClearResultCacheCommand.php
index 6b5198c4dc..9730b74325 100644
--- a/src/Command/ClearResultCacheCommand.php
+++ b/src/Command/ClearResultCacheCommand.php
@@ -3,28 +3,26 @@
namespace PHPStan\Command;
use PHPStan\Analyser\ResultCache\ResultCacheClearer;
+use PHPStan\ShouldNotHappenException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use function is_string;
class ClearResultCacheCommand extends Command
{
private const NAME = 'clear-result-cache';
- /** @var string[] */
- private array $composerAutoloaderProjectPaths;
-
/**
* @param string[] $composerAutoloaderProjectPaths
*/
public function __construct(
- array $composerAutoloaderProjectPaths
+ private array $composerAutoloaderProjectPaths,
)
{
parent::__construct();
- $this->composerAutoloaderProjectPaths = $composerAutoloaderProjectPaths;
}
protected function configure(): void
@@ -34,6 +32,7 @@ protected function configure(): void
->setDefinition([
new InputOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Path to project configuration file'),
new InputOption('autoload-file', 'a', InputOption::VALUE_REQUIRED, 'Project\'s additional autoload file path'),
+ new InputOption('memory-limit', null, InputOption::VALUE_REQUIRED, 'Memory limit for clearing result cache'),
]);
}
@@ -41,12 +40,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$autoloadFile = $input->getOption('autoload-file');
$configuration = $input->getOption('configuration');
+ $memoryLimit = $input->getOption('memory-limit');
if (
(!is_string($autoloadFile) && $autoloadFile !== null)
|| (!is_string($configuration) && $configuration !== null)
+ || (!is_string($memoryLimit) && $memoryLimit !== null)
) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
try {
@@ -54,17 +55,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$input,
$output,
['.'],
- null,
- null,
+ $memoryLimit,
$autoloadFile,
$this->composerAutoloaderProjectPaths,
$configuration,
null,
'0',
false,
- false
);
- } catch (\PHPStan\Command\InceptionNotSuccessfulException $e) {
+ } catch (InceptionNotSuccessfulException) {
return 1;
}
diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php
index 5194ab3f3a..c3de7b2e84 100644
--- a/src/Command/CommandHelper.php
+++ b/src/Command/CommandHelper.php
@@ -2,42 +2,85 @@
namespace PHPStan\Command;
+use Closure;
use Composer\XdebugHandler\XdebugHandler;
use Nette\DI\Config\Adapters\PhpAdapter;
use Nette\DI\Helpers;
-use Nette\Schema\Context as SchemaContext;
-use Nette\Schema\Processor;
+use Nette\DI\InvalidConfigurationException;
+use Nette\DI\ServiceCreationException;
+use Nette\FileNotFoundException;
+use Nette\InvalidStateException;
+use Nette\Schema\ValidationException;
+use Nette\Utils\AssertionException;
use Nette\Utils\Strings;
use Nette\Utils\Validators;
use PHPStan\Command\Symfony\SymfonyOutput;
use PHPStan\Command\Symfony\SymfonyStyle;
use PHPStan\DependencyInjection\Container;
use PHPStan\DependencyInjection\ContainerFactory;
+use PHPStan\DependencyInjection\InvalidIgnoredErrorPatternsException;
use PHPStan\DependencyInjection\LoaderFactory;
use PHPStan\DependencyInjection\NeonAdapter;
+use PHPStan\ExtensionInstaller\GeneratedConfig;
use PHPStan\File\FileFinder;
use PHPStan\File\FileHelper;
-use PHPStan\File\FileReader;
+use PHPStan\ShouldNotHappenException;
+use ReflectionClass;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
+use Throwable;
+use function array_diff_key;
+use function array_key_exists;
+use function array_map;
+use function array_merge;
+use function array_unique;
+use function class_exists;
+use function count;
+use function dirname;
+use function error_get_last;
+use function function_exists;
+use function get_class;
+use function getcwd;
+use function gettype;
+use function implode;
+use function ini_get;
+use function ini_set;
+use function is_dir;
+use function is_file;
+use function is_readable;
+use function is_string;
+use function mkdir;
+use function pcntl_async_signals;
+use function pcntl_signal;
+use function register_shutdown_function;
+use function sprintf;
+use function str_ends_with;
+use function str_repeat;
+use function strpos;
+use function sys_get_temp_dir;
+use const DIRECTORY_SEPARATOR;
+use const E_ERROR;
+use const PHP_VERSION_ID;
+use const SIGINT;
class CommandHelper
{
public const DEFAULT_LEVEL = '0';
+ private static ?string $reservedMemory = null;
+
/**
* @param string[] $paths
* @param string[] $composerAutoloaderProjectPaths
*
- * @throws \PHPStan\Command\InceptionNotSuccessfulException
+ * @throws InceptionNotSuccessfulException
*/
public static function begin(
InputInterface $input,
OutputInterface $output,
array $paths,
- ?string $pathsFile,
?string $memoryLimit,
?string $autoloadFile,
array $composerAutoloaderProjectPaths,
@@ -45,38 +88,60 @@ public static function begin(
?string $generateBaselineFile,
?string $level,
bool $allowXdebug,
- bool $manageMemoryLimitFile = true,
bool $debugEnabled = false,
?string $singleReflectionFile = null,
- ?string $singleReflectionInsteadOfFile = null
+ ?string $singleReflectionInsteadOfFile = null,
+ bool $cleanupContainerCache = true,
): InceptionResult
{
if (!$allowXdebug) {
$xdebug = new XdebugHandler('phpstan');
+ $xdebug->setPersistent();
$xdebug->check();
unset($xdebug);
}
+
$stdOutput = new SymfonyOutput($output, new SymfonyStyle(new ErrorsConsoleStyle($input, $output)));
- /** @var \PHPStan\Command\Output $errorOutput */
+ /** @var Output $errorOutput */
$errorOutput = (static function () use ($input, $output): Output {
$symfonyErrorOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
return new SymfonyOutput($symfonyErrorOutput, new SymfonyStyle(new ErrorsConsoleStyle($input, $symfonyErrorOutput)));
})();
if ($memoryLimit !== null) {
- if (\Nette\Utils\Strings::match($memoryLimit, '#^-?\d+[kMG]?$#i') === null) {
+ if (Strings::match($memoryLimit, '#^-?\d+[kMG]?$#i') === null) {
$errorOutput->writeLineFormatted(sprintf('Invalid memory limit format "%s".', $memoryLimit));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
if (ini_set('memory_limit', $memoryLimit) === false) {
$errorOutput->writeLineFormatted(sprintf('Memory limit "%s" cannot be set.', $memoryLimit));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
}
+ self::$reservedMemory = str_repeat('PHPStan', 1463); // reserve 10 kB of space
+ register_shutdown_function(static function () use ($errorOutput): void {
+ self::$reservedMemory = null;
+ $error = error_get_last();
+ if ($error === null) {
+ return;
+ }
+ if ($error['type'] !== E_ERROR) {
+ return;
+ }
+
+ if (strpos($error['message'], 'Allowed memory size') === false) {
+ return;
+ }
+
+ $errorOutput->writeLineFormatted('');
+ $errorOutput->writeLineFormatted(sprintf('PHPStan process crashed because it reached configured PHP memory limit: %s', ini_get('memory_limit')));
+ $errorOutput->writeLineFormatted('Increase your memory limit in php.ini or run PHPStan with --memory-limit CLI option.');
+ });
+
$currentWorkingDirectory = getcwd();
if ($currentWorkingDirectory === false) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$currentWorkingDirectoryFileHelper = new FileHelper($currentWorkingDirectory);
$currentWorkingDirectory = $currentWorkingDirectoryFileHelper->getWorkingDirectory();
@@ -84,7 +149,7 @@ public static function begin(
$autoloadFile = $currentWorkingDirectoryFileHelper->absolutizePath($autoloadFile);
if (!is_file($autoloadFile)) {
$errorOutput->writeLineFormatted(sprintf('Autoload file "%s" not found.', $autoloadFile));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
(static function (string $file): void {
@@ -92,7 +157,7 @@ public static function begin(
})($autoloadFile);
}
if ($projectConfigFile === null) {
- foreach (['phpstan.neon', 'phpstan.neon.dist'] as $discoverableConfigName) {
+ foreach (['phpstan.neon', 'phpstan.neon.dist', 'phpstan.dist.neon'] as $discoverableConfigName) {
$discoverableConfigFile = $currentWorkingDirectory . DIRECTORY_SEPARATOR . $discoverableConfigName;
if (is_file($discoverableConfigFile)) {
$projectConfigFile = $discoverableConfigFile;
@@ -114,33 +179,7 @@ public static function begin(
$defaultLevelUsed = true;
}
- $paths = array_map(static function (string $path) use ($currentWorkingDirectoryFileHelper): string {
- return $currentWorkingDirectoryFileHelper->normalizePath($currentWorkingDirectoryFileHelper->absolutizePath($path));
- }, $paths);
-
- if (count($paths) === 0 && $pathsFile !== null) {
- $pathsFile = $currentWorkingDirectoryFileHelper->absolutizePath($pathsFile);
- if (!file_exists($pathsFile)) {
- $errorOutput->writeLineFormatted(sprintf('Paths file %s does not exist.', $pathsFile));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
- }
-
- try {
- $pathsString = FileReader::read($pathsFile);
- } catch (\PHPStan\File\CouldNotReadFileException $e) {
- $errorOutput->writeLineFormatted($e->getMessage());
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
- }
-
- $paths = array_values(array_filter(explode("\n", $pathsString), static function (string $path): bool {
- return trim($path) !== '';
- }));
-
- $pathsFileFileHelper = new FileHelper(dirname($pathsFile));
- $paths = array_map(static function (string $path) use ($pathsFileFileHelper): string {
- return $pathsFileFileHelper->normalizePath($pathsFileFileHelper->absolutizePath($path));
- }, $paths);
- }
+ $paths = array_map(static fn (string $path): string => $currentWorkingDirectoryFileHelper->normalizePath($currentWorkingDirectoryFileHelper->absolutizePath($path)), $paths);
$analysedPathsFromConfig = [];
$containerFactory = new ContainerFactory($currentWorkingDirectory);
@@ -148,21 +187,21 @@ public static function begin(
if ($projectConfigFile !== null) {
if (!is_file($projectConfigFile)) {
$errorOutput->writeLineFormatted(sprintf('Project config file at path %s does not exist.', $projectConfigFile));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
$loader = (new LoaderFactory(
$currentWorkingDirectoryFileHelper,
$containerFactory->getRootDirectory(),
$containerFactory->getCurrentWorkingDirectory(),
- $generateBaselineFile
+ $generateBaselineFile,
))->createLoader();
try {
$projectConfig = $loader->load($projectConfigFile, null);
- } catch (\Nette\InvalidStateException | \Nette\FileNotFoundException $e) {
+ } catch (InvalidStateException | FileNotFoundException $e) {
$errorOutput->writeLineFormatted($e->getMessage());
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
$defaultParameters = [
'rootDir' => $containerFactory->getRootDirectory(),
@@ -175,8 +214,10 @@ public static function begin(
if ($level === null && isset($projectConfig['parameters']['level'])) {
$level = (string) $projectConfig['parameters']['level'];
}
- if (count($paths) === 0 && isset($projectConfig['parameters']['paths'])) {
+ if (isset($projectConfig['parameters']['paths'])) {
$analysedPathsFromConfig = Helpers::expand($projectConfig['parameters']['paths'], $defaultParameters);
+ }
+ if (count($paths) === 0) {
$paths = $analysedPathsFromConfig;
}
}
@@ -186,25 +227,25 @@ public static function begin(
$levelConfigFile = sprintf('%s/config.level%s.neon', $containerFactory->getConfigDirectory(), $level);
if (!is_file($levelConfigFile)) {
$errorOutput->writeLineFormatted(sprintf('Level config file %s was not found.', $levelConfigFile));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
$additionalConfigFiles[] = $levelConfigFile;
}
if (class_exists('PHPStan\ExtensionInstaller\GeneratedConfig')) {
- $generatedConfigReflection = new \ReflectionClass('PHPStan\ExtensionInstaller\GeneratedConfig');
+ $generatedConfigReflection = new ReflectionClass('PHPStan\ExtensionInstaller\GeneratedConfig');
$generatedConfigDirectory = dirname($generatedConfigReflection->getFileName());
- foreach (\PHPStan\ExtensionInstaller\GeneratedConfig::EXTENSIONS as $name => $extensionConfig) {
+ foreach (GeneratedConfig::EXTENSIONS as $name => $extensionConfig) {
foreach ($extensionConfig['extra']['includes'] ?? [] as $includedFile) {
if (!is_string($includedFile)) {
$errorOutput->writeLineFormatted(sprintf('Cannot include config from package %s, expecting string file path but got %s', $name, gettype($includedFile)));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
$includedFilePath = null;
if (isset($extensionConfig['relative_install_path'])) {
$includedFilePath = sprintf('%s/%s/%s', $generatedConfigDirectory, $extensionConfig['relative_install_path'], $includedFile);
- if (!file_exists($includedFilePath) || !is_readable($includedFilePath)) {
+ if (!is_file($includedFilePath) || !is_readable($includedFilePath)) {
$includedFilePath = null;
}
}
@@ -212,16 +253,19 @@ public static function begin(
if ($includedFilePath === null) {
$includedFilePath = sprintf('%s/%s', $extensionConfig['install_path'], $includedFile);
}
- if (!file_exists($includedFilePath) || !is_readable($includedFilePath)) {
+ if (!is_file($includedFilePath) || !is_readable($includedFilePath)) {
$errorOutput->writeLineFormatted(sprintf('Config file %s does not exist or isn\'t readable', $includedFilePath));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
$additionalConfigFiles[] = $includedFilePath;
}
}
}
- if ($projectConfigFile !== null) {
+ if (
+ $projectConfigFile !== null
+ && $currentWorkingDirectoryFileHelper->normalizePath($projectConfigFile, '/') !== $currentWorkingDirectoryFileHelper->normalizePath(__DIR__ . '/../../conf/config.stubFiles.neon', '/')
+ ) {
$additionalConfigFiles[] = $projectConfigFile;
}
@@ -234,13 +278,13 @@ public static function begin(
$errorOutput,
$currentWorkingDirectoryFileHelper,
$additionalConfigFiles,
- $loaderParameters
+ $loaderParameters,
);
$createDir = static function (string $path) use ($errorOutput): void {
- if (!@mkdir($path, 0777) && !is_dir($path)) {
+ if (!is_dir($path) && !@mkdir($path, 0777) && !is_dir($path)) {
$errorOutput->writeLineFormatted(sprintf('Cannot create a temp directory %s', $path));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
};
@@ -251,32 +295,69 @@ public static function begin(
try {
$container = $containerFactory->create($tmpDir, $additionalConfigFiles, $paths, $composerAutoloaderProjectPaths, $analysedPathsFromConfig, $level ?? self::DEFAULT_LEVEL, $generateBaselineFile, $autoloadFile, $singleReflectionFile, $singleReflectionInsteadOfFile);
- } catch (\Nette\DI\InvalidConfigurationException | \Nette\Utils\AssertionException $e) {
+ } catch (InvalidConfigurationException | AssertionException $e) {
$errorOutput->writeLineFormatted('Invalid configuration:');
$errorOutput->writeLineFormatted($e->getMessage());
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
- }
+ throw new InceptionNotSuccessfulException();
+ } catch (InvalidIgnoredErrorPatternsException $e) {
+ $errorOutput->writeLineFormatted(sprintf('Invalid %s in ignoreErrors:', count($e->getErrors()) === 1 ? 'entry' : 'entries'));
+ foreach ($e->getErrors() as $error) {
+ $errorOutput->writeLineFormatted($error);
+ $errorOutput->writeLineFormatted('');
+ }
+ throw new InceptionNotSuccessfulException();
+ } catch (ValidationException $e) {
+ foreach ($e->getMessages() as $message) {
+ $errorOutput->writeLineFormatted('Invalid configuration:');
+ $errorOutput->writeLineFormatted($message);
+ }
+ throw new InceptionNotSuccessfulException();
+ } catch (ServiceCreationException $e) {
+ $matches = Strings::match($e->getMessage(), '#Service of type (?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\\\\]*[a-zA-Z0-9_\x7f-\xff]): Service of type (?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\\\\]*[a-zA-Z0-9_\x7f-\xff]) needed by \$(?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*) in (?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*)\(\)#');
+ if ($matches === null) {
+ throw $e;
+ }
- $containerFactory->clearOldContainers($tmpDir);
+ if ($matches['parserServiceType'] !== 'PHPStan\\Parser\\Parser') {
+ throw $e;
+ }
- if (count($paths) === 0) {
- $errorOutput->writeLineFormatted('At least one path must be specified to analyse.');
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
- }
+ if ($matches['methodName'] !== '__construct') {
+ throw $e;
+ }
+
+ $errorOutput->writeLineFormatted('Invalid configuration:');
+ $errorOutput->writeLineFormatted(sprintf("Service of type %s> is no longer autowired.\n", $matches['parserServiceType']));
+ $errorOutput->writeLineFormatted('You need to choose one of the following services');
+ $errorOutput->writeLineFormatted(sprintf('and use it in the %s argument of your service %s>:', $matches['parameterName'], $matches['serviceType']));
+ $errorOutput->writeLineFormatted('* defaultAnalysisParser> (if you\'re parsing files from analysed paths)');
+ $errorOutput->writeLineFormatted('* currentPhpVersionSimpleDirectParser> (in most other situations)');
- $memoryLimitFile = $container->getParameter('memoryLimitFile');
- if ($manageMemoryLimitFile && file_exists($memoryLimitFile)) {
- $memoryLimitFileContents = FileReader::read($memoryLimitFile);
- $errorOutput->writeLineFormatted('PHPStan crashed in the previous run probably because of excessive memory consumption.');
- $errorOutput->writeLineFormatted(sprintf('It consumed around %s of memory.', $memoryLimitFileContents));
$errorOutput->writeLineFormatted('');
+ $errorOutput->writeLineFormatted('After fixing this problem, your configuration will look something like this:');
+ $errorOutput->writeLineFormatted('');
+ $errorOutput->writeLineFormatted('-');
+ $errorOutput->writeLineFormatted(sprintf("\tclass: %s", $matches['serviceType']));
+ $errorOutput->writeLineFormatted(sprintf("\targuments:"));
+ $errorOutput->writeLineFormatted(sprintf("\t\t%s: @defaultAnalysisParser", $matches['parameterName']));
$errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted('To avoid this issue, allow to use more memory with the --memory-limit option.');
- @unlink($memoryLimitFile);
+
+ throw new InceptionNotSuccessfulException();
}
- self::setUpSignalHandler($errorOutput, $manageMemoryLimitFile ? $memoryLimitFile : null);
- if (!$container->hasParameter('customRulesetUsed')) {
+ if ($cleanupContainerCache) {
+ $containerFactory->clearOldContainers($tmpDir);
+ }
+
+ if (count($paths) === 0) {
+ $errorOutput->writeLineFormatted('At least one path must be specified to analyse.');
+ throw new InceptionNotSuccessfulException();
+ }
+
+ self::setUpSignalHandler($errorOutput);
+ /** @var bool|null $customRulesetUsed */
+ $customRulesetUsed = $container->getParameter('customRulesetUsed');
+ if ($customRulesetUsed === null) {
$errorOutput->writeLineFormatted('');
$errorOutput->writeLineFormatted('No rules detected');
$errorOutput->writeLineFormatted('');
@@ -287,87 +368,23 @@ public static function begin(
$errorOutput->writeLineFormatted(sprintf('* create your own custom ruleset by selecting which rules you want to check by copying the service definitions from the built-in config level files in %s>.', $currentWorkingDirectoryFileHelper->normalizePath(__DIR__ . '/../../conf')));
$errorOutput->writeLineFormatted(' * in this case, don\'t forget to define parameter customRulesetUsed> in your config file.');
$errorOutput->writeLineFormatted('');
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
- } elseif ((bool) $container->getParameter('customRulesetUsed')) {
+ throw new InceptionNotSuccessfulException();
+ } elseif ($customRulesetUsed) {
$defaultLevelUsed = false;
}
- $schema = $container->getParameter('__parametersSchema');
- $processor = new Processor();
- $processor->onNewContext[] = static function (SchemaContext $context): void {
- $context->path = ['parameters'];
- };
-
- try {
- $processor->process($schema, $container->getParameters());
- } catch (\Nette\Schema\ValidationException $e) {
- foreach ($e->getMessages() as $message) {
- $errorOutput->writeLineFormatted('Invalid configuration:');
- $errorOutput->writeLineFormatted($message);
- }
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
- }
-
- $autoloadFiles = $container->getParameter('autoload_files');
- if ($manageMemoryLimitFile && count($autoloadFiles) > 0) {
- $errorOutput->writeLineFormatted('⚠️ You\'re using a deprecated config option autoload_files>. ⚠️️');
- $errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted('You might not need it anymore - try removing it from your');
- $errorOutput->writeLineFormatted('configuration file and run PHPStan again.');
- $errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted('If the analysis fails, there are now two distinct options');
- $errorOutput->writeLineFormatted('to choose from to replace autoload_files>:');
- $errorOutput->writeLineFormatted('1) scanFiles> - PHPStan will scan those for classes and functions');
- $errorOutput->writeLineFormatted(' definitions. PHPStan will not execute those files.');
- $errorOutput->writeLineFormatted('2) bootstrapFiles> - PHPStan will execute these files to prepare');
- $errorOutput->writeLineFormatted(' the PHP runtime environment for the analysis.');
- $errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted('Read more about this in PHPStan\'s documentation:');
- $errorOutput->writeLineFormatted('https://phpstan.org/user-guide/discovering-symbols');
- $errorOutput->writeLineFormatted('');
- }
-
- $autoloadDirectories = $container->getParameter('autoload_directories');
- if (count($autoloadDirectories) > 0 && $manageMemoryLimitFile) {
- $errorOutput->writeLineFormatted('⚠️ You\'re using a deprecated config option autoload_directories>. ⚠️️');
- $errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted('You might not need it anymore - try removing it from your');
- $errorOutput->writeLineFormatted('configuration file and run PHPStan again.');
- $errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted('If the analysis fails, replace it with scanDirectories>.');
- $errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted('Read more about this in PHPStan\'s documentation:');
- $errorOutput->writeLineFormatted('https://phpstan.org/user-guide/discovering-symbols');
- $errorOutput->writeLineFormatted('');
- }
-
- foreach ($autoloadFiles as $parameterAutoloadFile) {
- if (!file_exists($parameterAutoloadFile)) {
- $errorOutput->writeLineFormatted(sprintf('Autoload file %s does not exist.', $parameterAutoloadFile));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
- }
- (static function (string $file) use ($container): void {
- require_once $file;
- })($parameterAutoloadFile);
- }
-
- $bootstrapFile = $container->getParameter('bootstrap');
- if ($bootstrapFile !== null) {
- if ($manageMemoryLimitFile) {
- $errorOutput->writeLineFormatted('⚠️ You\'re using a deprecated config option bootstrap>. ⚠️️');
- $errorOutput->writeLineFormatted('');
- $errorOutput->writeLineFormatted('This option has been replaced with bootstrapFiles> which accepts a list of files');
- $errorOutput->writeLineFormatted('to execute before the analysis.');
- $errorOutput->writeLineFormatted('');
- }
-
- self::executeBootstrapFile($bootstrapFile, $container, $errorOutput, $debugEnabled);
- }
-
foreach ($container->getParameter('bootstrapFiles') as $bootstrapFileFromArray) {
self::executeBootstrapFile($bootstrapFileFromArray, $container, $errorOutput, $debugEnabled);
}
+ if (PHP_VERSION_ID >= 80000) {
+ require_once __DIR__ . '/../../stubs/runtime/Enum/UnitEnum.php';
+ require_once __DIR__ . '/../../stubs/runtime/Enum/BackedEnum.php';
+ require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnum.php';
+ require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnumUnitCase.php';
+ require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnumBackedCase.php';
+ }
+
foreach ($container->getParameter('scanFiles') as $scannedFile) {
if (is_file($scannedFile)) {
continue;
@@ -375,7 +392,7 @@ public static function begin(
$errorOutput->writeLineFormatted(sprintf('Scanned file %s does not exist.', $scannedFile));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
foreach ($container->getParameter('scanDirectories') as $scannedDirectory) {
@@ -385,18 +402,15 @@ public static function begin(
$errorOutput->writeLineFormatted(sprintf('Scanned directory %s does not exist.', $scannedDirectory));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
$alreadyAddedStubFiles = [];
foreach ($container->getParameter('stubFiles') as $stubFile) {
- if (
- $container->getParameter('featureToggles')['detectDuplicateStubFiles']
- && array_key_exists($stubFile, $alreadyAddedStubFiles)
- ) {
+ if (array_key_exists($stubFile, $alreadyAddedStubFiles)) {
$errorOutput->writeLineFormatted(sprintf('Stub file %s is added multiple times.', $stubFile));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
$alreadyAddedStubFiles[$stubFile] = true;
@@ -407,7 +421,7 @@ public static function begin(
$errorOutput->writeLineFormatted(sprintf('Stub file %s does not exist.', $stubFile));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
$excludesAnalyse = $container->getParameter('excludes_analyse');
@@ -418,7 +432,11 @@ public static function begin(
$errorOutput->writeLineFormatted(sprintf('Parameter excludes_analyse> has been deprecated so use excludePaths> only from now on.'));
$errorOutput->writeLineFormatted('');
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
+ } elseif (count($excludesAnalyse) > 0) {
+ $errorOutput->writeLineFormatted('⚠️ You\'re using a deprecated config option excludes_analyse>. ⚠️️');
+ $errorOutput->writeLineFormatted('');
+ $errorOutput->writeLineFormatted(sprintf('Parameter excludes_analyse> has been deprecated so use excludePaths> only from now on.'));
}
$tempResultCachePath = $container->getParameter('tempResultCachePath');
@@ -427,11 +445,16 @@ public static function begin(
/** @var FileFinder $fileFinder */
$fileFinder = $container->getService('fileFinderAnalyse');
- /** @var \Closure(): (array{string[], bool}) $filesCallback */
- $filesCallback = static function () use ($fileFinder, $paths): array {
+ $pathRoutingParser = $container->getService('pathRoutingParser');
+
+ /** @var Closure(): array{string[], bool} $filesCallback */
+ $filesCallback = static function () use ($fileFinder, $pathRoutingParser, $paths): array {
$fileFinderResult = $fileFinder->findFiles($paths);
+ $files = $fileFinderResult->getFiles();
- return [$fileFinderResult->getFiles(), $fileFinderResult->isOnlyFiles()];
+ $pathRoutingParser->setAnalysedFiles($files);
+
+ return [$files, $fileFinderResult->isOnlyFiles()];
};
return new InceptionResult(
@@ -440,10 +463,9 @@ public static function begin(
$errorOutput,
$container,
$defaultLevelUsed,
- $memoryLimitFile,
$projectConfigFile,
$projectConfig,
- $generateBaselineFile
+ $generateBaselineFile,
);
}
@@ -454,56 +476,51 @@ private static function executeBootstrapFile(
string $file,
Container $container,
Output $errorOutput,
- bool $debugEnabled
+ bool $debugEnabled,
): void
{
if (!is_file($file)) {
$errorOutput->writeLineFormatted(sprintf('Bootstrap file %s does not exist.', $file));
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
try {
(static function (string $file) use ($container): void {
require_once $file;
})($file);
- } catch (\Throwable $e) {
+ } catch (Throwable $e) {
$errorOutput->writeLineFormatted(sprintf('%s thrown in %s on line %d while loading bootstrap file %s: %s', get_class($e), $e->getFile(), $e->getLine(), $file, $e->getMessage()));
if ($debugEnabled) {
$errorOutput->writeLineFormatted($e->getTraceAsString());
}
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
}
- private static function setUpSignalHandler(Output $output, ?string $memoryLimitFile): void
+ private static function setUpSignalHandler(Output $output): void
{
if (!function_exists('pcntl_signal')) {
return;
}
pcntl_async_signals(true);
- pcntl_signal(SIGINT, static function () use ($output, $memoryLimitFile): void {
- if ($memoryLimitFile !== null && file_exists($memoryLimitFile)) {
- @unlink($memoryLimitFile);
- }
+ pcntl_signal(SIGINT, static function () use ($output): void {
$output->writeLineFormatted('');
exit(1);
});
}
/**
- * @param \PHPStan\Command\Output $output
- * @param \PHPStan\File\FileHelper $fileHelper
* @param string[] $configFiles
* @param array $loaderParameters
- * @throws \PHPStan\Command\InceptionNotSuccessfulException
+ * @throws InceptionNotSuccessfulException
*/
private static function detectDuplicateIncludedFiles(
Output $output,
FileHelper $fileHelper,
array $configFiles,
- array $loaderParameters
+ array $loaderParameters,
): void
{
$neonAdapter = new NeonAdapter();
@@ -513,9 +530,7 @@ private static function detectDuplicateIncludedFiles(
$allConfigFiles = array_merge($allConfigFiles, self::getConfigFiles($fileHelper, $neonAdapter, $phpAdapter, $configFile, $loaderParameters, null));
}
- $normalized = array_map(static function (string $file) use ($fileHelper): string {
- return $fileHelper->normalizePath($file);
- }, $allConfigFiles);
+ $normalized = array_map(static fn (string $file): string => $fileHelper->normalizePath($file), $allConfigFiles);
$deduplicated = array_unique($normalized);
if (count($normalized) <= count($deduplicated)) {
@@ -534,15 +549,11 @@ private static function detectDuplicateIncludedFiles(
$output->writeLineFormatted('');
$output->writeLineFormatted('It can lead to unexpected results. If you\'re using phpstan/extension-installer, make sure you have removed corresponding neon files from your project config file.');
}
- throw new \PHPStan\Command\InceptionNotSuccessfulException();
+ throw new InceptionNotSuccessfulException();
}
/**
- * @param \PHPStan\DependencyInjection\NeonAdapter $neonAdapter
- * @param \Nette\DI\Config\Adapters\PhpAdapter $phpAdapter
- * @param string $configFile
* @param array $loaderParameters
- * @param string|null $generateBaselineFile
* @return string[]
*/
private static function getConfigFiles(
@@ -551,7 +562,7 @@ private static function getConfigFiles(
PhpAdapter $phpAdapter,
string $configFile,
array $loaderParameters,
- ?string $generateBaselineFile
+ ?string $generateBaselineFile,
): array
{
if ($generateBaselineFile === $fileHelper->normalizePath($configFile)) {
@@ -561,7 +572,7 @@ private static function getConfigFiles(
return [];
}
- if (Strings::endsWith($configFile, '.php')) {
+ if (str_ends_with($configFile, '.php')) {
$data = $phpAdapter->load($configFile);
} else {
$data = $neonAdapter->load($configFile);
diff --git a/src/Command/DumpDependenciesCommand.php b/src/Command/DumpDependenciesCommand.php
deleted file mode 100644
index aa69b5900d..0000000000
--- a/src/Command/DumpDependenciesCommand.php
+++ /dev/null
@@ -1,130 +0,0 @@
-composerAutoloaderProjectPaths = $composerAutoloaderProjectPaths;
- }
-
- protected function configure(): void
- {
- $this->setName(self::NAME)
- ->setDescription('Dumps files dependency tree')
- ->setDefinition([
- new InputArgument('paths', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Paths with source code to run dump on'),
- new InputOption('paths-file', null, InputOption::VALUE_REQUIRED, 'Path to a file with a list of paths to run analysis on'),
- new InputOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Path to project configuration file'),
- new InputOption(ErrorsConsoleStyle::OPTION_NO_PROGRESS, null, InputOption::VALUE_NONE, 'Do not show progress bar, only results'),
- new InputOption('autoload-file', 'a', InputOption::VALUE_REQUIRED, 'Project\'s additional autoload file path'),
- new InputOption('memory-limit', null, InputOption::VALUE_REQUIRED, 'Memory limit for the run'),
- new InputOption('analysed-paths', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Project-scope paths'),
- new InputOption('xdebug', null, InputOption::VALUE_NONE, 'Allow running with XDebug for debugging purposes'),
- ]);
- }
-
- protected function execute(InputInterface $input, OutputInterface $output): int
- {
- try {
- /** @var string[] $paths */
- $paths = $input->getArgument('paths');
-
- /** @var string|null $memoryLimit */
- $memoryLimit = $input->getOption('memory-limit');
-
- /** @var string|null $autoloadFile */
- $autoloadFile = $input->getOption('autoload-file');
-
- /** @var string|null $configurationFile */
- $configurationFile = $input->getOption('configuration');
-
- /** @var string|null $pathsFile */
- $pathsFile = $input->getOption('paths-file');
-
- /** @var bool $allowXdebug */
- $allowXdebug = $input->getOption('xdebug');
-
- $inceptionResult = CommandHelper::begin(
- $input,
- $output,
- $paths,
- $pathsFile,
- $memoryLimit,
- $autoloadFile,
- $this->composerAutoloaderProjectPaths,
- $configurationFile,
- null,
- '0', // irrelevant but prevents an error when a config file is passed
- $allowXdebug,
- true
- );
- } catch (\PHPStan\Command\InceptionNotSuccessfulException $e) {
- return 1;
- }
-
- try {
- [$files] = $inceptionResult->getFiles();
- } catch (\PHPStan\File\PathNotFoundException $e) {
- $inceptionResult->getErrorOutput()->writeLineFormatted(sprintf('%s', $e->getMessage()));
- return 1;
- }
-
- $stdOutput = $inceptionResult->getStdOutput();
- $stdOutputStyole = $stdOutput->getStyle();
-
- /** @var DependencyDumper $dependencyDumper */
- $dependencyDumper = $inceptionResult->getContainer()->getByType(DependencyDumper::class);
-
- /** @var FileHelper $fileHelper */
- $fileHelper = $inceptionResult->getContainer()->getByType(FileHelper::class);
-
- /** @var string[] $analysedPaths */
- $analysedPaths = $input->getOption('analysed-paths');
- $analysedPaths = array_map(static function (string $path) use ($fileHelper): string {
- return $fileHelper->absolutizePath($path);
- }, $analysedPaths);
- $dependencies = $dependencyDumper->dumpDependencies(
- $files,
- static function (int $count) use ($stdOutputStyole): void {
- $stdOutputStyole->progressStart($count);
- },
- static function () use ($stdOutputStyole): void {
- $stdOutputStyole->progressAdvance();
- },
- count($analysedPaths) > 0 ? $analysedPaths : null
- );
- $stdOutputStyole->progressFinish();
-
- try {
- $stdOutput->writeLineFormatted(Json::encode($dependencies, Json::PRETTY));
- } catch (\Nette\Utils\JsonException $e) {
- $inceptionResult->getErrorOutput()->writeLineFormatted(sprintf('%s', $e->getMessage()));
- return 1;
- }
-
- return $inceptionResult->handleReturn(0);
- }
-
-}
diff --git a/src/Command/ErrorFormatter/BaselineNeonErrorFormatter.php b/src/Command/ErrorFormatter/BaselineNeonErrorFormatter.php
index f819dd664f..244f978551 100644
--- a/src/Command/ErrorFormatter/BaselineNeonErrorFormatter.php
+++ b/src/Command/ErrorFormatter/BaselineNeonErrorFormatter.php
@@ -7,23 +7,20 @@
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
use PHPStan\File\RelativePathHelper;
-use const SORT_STRING;
use function ksort;
use function preg_quote;
+use const SORT_STRING;
class BaselineNeonErrorFormatter implements ErrorFormatter
{
- private \PHPStan\File\RelativePathHelper $relativePathHelper;
-
- public function __construct(RelativePathHelper $relativePathHelper)
+ public function __construct(private RelativePathHelper $relativePathHelper)
{
- $this->relativePathHelper = $relativePathHelper;
}
public function formatErrors(
AnalysisResult $analysisResult,
- Output $output
+ Output $output,
): int
{
if (!$analysisResult->hasErrors()) {
@@ -40,7 +37,7 @@ public function formatErrors(
if (!$fileSpecificError->canBeIgnored()) {
continue;
}
- $fileErrors[$fileSpecificError->getFilePath()][] = $fileSpecificError->getMessage();
+ $fileErrors[$this->relativePathHelper->getRelativePath($fileSpecificError->getFilePath())][] = $fileSpecificError->getMessage();
}
ksort($fileErrors, SORT_STRING);
@@ -61,7 +58,7 @@ public function formatErrors(
$errorsToOutput[] = [
'message' => Helpers::escape('#^' . preg_quote($message, '#') . '$#'),
'count' => $count,
- 'path' => Helpers::escape($this->relativePathHelper->getRelativePath($file)),
+ 'path' => Helpers::escape($file),
];
}
}
diff --git a/src/Command/ErrorFormatter/CheckstyleErrorFormatter.php b/src/Command/ErrorFormatter/CheckstyleErrorFormatter.php
index b2cbb6ade0..33eb65bb37 100644
--- a/src/Command/ErrorFormatter/CheckstyleErrorFormatter.php
+++ b/src/Command/ErrorFormatter/CheckstyleErrorFormatter.php
@@ -2,23 +2,26 @@
namespace PHPStan\Command\ErrorFormatter;
+use PHPStan\Analyser\Error;
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
use PHPStan\File\RelativePathHelper;
+use function count;
+use function htmlspecialchars;
+use function sprintf;
+use const ENT_COMPAT;
+use const ENT_XML1;
class CheckstyleErrorFormatter implements ErrorFormatter
{
- private RelativePathHelper $relativePathHelper;
-
- public function __construct(RelativePathHelper $relativePathHelper)
+ public function __construct(private RelativePathHelper $relativePathHelper)
{
- $this->relativePathHelper = $relativePathHelper;
}
public function formatErrors(
AnalysisResult $analysisResult,
- Output $output
+ Output $output,
): int
{
$output->writeRaw('');
@@ -29,7 +32,7 @@ public function formatErrors(
foreach ($this->groupByFile($analysisResult) as $relativeFilePath => $errors) {
$output->writeRaw(sprintf(
'',
- $this->escape($relativeFilePath)
+ $this->escape($relativeFilePath),
));
$output->writeLineFormatted('');
@@ -37,7 +40,7 @@ public function formatErrors(
$output->writeRaw(sprintf(
' ',
$this->escape((string) $error->getLine()),
- $this->escape($error->getMessage())
+ $this->escape($error->getMessage()),
));
$output->writeLineFormatted('');
}
@@ -82,8 +85,6 @@ public function formatErrors(
/**
* Escapes values for using in XML
*
- * @param string $string
- * @return string
*/
private function escape(string $string): string
{
@@ -93,22 +94,21 @@ private function escape(string $string): string
/**
* Group errors by file
*
- * @param AnalysisResult $analysisResult
- * @return array> Array that have as key the relative path of file
- * and as value an array with occurred errors.
+ * @return array> Array that have as key the relative path of file
+ * and as value an array with occurred errors.
*/
private function groupByFile(AnalysisResult $analysisResult): array
{
$files = [];
- /** @var \PHPStan\Analyser\Error $fileSpecificError */
+ /** @var Error $fileSpecificError */
foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
$absolutePath = $fileSpecificError->getFilePath();
if ($fileSpecificError->getTraitFilePath() !== null) {
$absolutePath = $fileSpecificError->getTraitFilePath();
}
$relativeFilePath = $this->relativePathHelper->getRelativePath(
- $absolutePath
+ $absolutePath,
);
$files[$relativeFilePath][] = $fileSpecificError;
diff --git a/src/Command/ErrorFormatter/ErrorFormatter.php b/src/Command/ErrorFormatter/ErrorFormatter.php
index 3c2663efe7..cc4b48df3a 100644
--- a/src/Command/ErrorFormatter/ErrorFormatter.php
+++ b/src/Command/ErrorFormatter/ErrorFormatter.php
@@ -12,13 +12,11 @@ interface ErrorFormatter
/**
* Formats the errors and outputs them to the console.
*
- * @param \PHPStan\Command\AnalysisResult $analysisResult
- * @param \PHPStan\Command\Output $output
* @return int Error code.
*/
public function formatErrors(
AnalysisResult $analysisResult,
- Output $output
+ Output $output,
): int;
}
diff --git a/src/Command/ErrorFormatter/GithubErrorFormatter.php b/src/Command/ErrorFormatter/GithubErrorFormatter.php
index 0743b26d3a..33ee0f3b8e 100644
--- a/src/Command/ErrorFormatter/GithubErrorFormatter.php
+++ b/src/Command/ErrorFormatter/GithubErrorFormatter.php
@@ -5,6 +5,10 @@
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
use PHPStan\File\RelativePathHelper;
+use function array_walk;
+use function implode;
+use function sprintf;
+use function str_replace;
/**
* Allow errors to be reported in pull-requests diff when run in a GitHub Action
@@ -13,22 +17,16 @@
class GithubErrorFormatter implements ErrorFormatter
{
- private RelativePathHelper $relativePathHelper;
-
- private TableErrorFormatter $tableErrorformatter;
-
public function __construct(
- RelativePathHelper $relativePathHelper,
- TableErrorFormatter $tableErrorformatter
+ private RelativePathHelper $relativePathHelper,
+ private ErrorFormatter $errorFormatter,
)
{
- $this->relativePathHelper = $relativePathHelper;
- $this->tableErrorformatter = $tableErrorformatter;
}
public function formatErrors(AnalysisResult $analysisResult, Output $output): int
{
- $this->tableErrorformatter->formatErrors($analysisResult, $output);
+ $this->errorFormatter->formatErrors($analysisResult, $output);
foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
$metas = [
diff --git a/src/Command/ErrorFormatter/GitlabErrorFormatter.php b/src/Command/ErrorFormatter/GitlabErrorFormatter.php
index 16713dd779..6aa4b61bef 100644
--- a/src/Command/ErrorFormatter/GitlabErrorFormatter.php
+++ b/src/Command/ErrorFormatter/GitlabErrorFormatter.php
@@ -6,6 +6,8 @@
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
use PHPStan\File\RelativePathHelper;
+use function hash;
+use function implode;
/**
* @see https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html#implementing-a-custom-tool
@@ -13,11 +15,8 @@
class GitlabErrorFormatter implements ErrorFormatter
{
- private RelativePathHelper $relativePathHelper;
-
- public function __construct(RelativePathHelper $relativePathHelper)
+ public function __construct(private RelativePathHelper $relativePathHelper)
{
- $this->relativePathHelper = $relativePathHelper;
}
public function formatErrors(AnalysisResult $analysisResult, Output $output): int
@@ -34,14 +33,14 @@ public function formatErrors(AnalysisResult $analysisResult, Output $output): in
$fileSpecificError->getFile(),
$fileSpecificError->getLine(),
$fileSpecificError->getMessage(),
- ]
- )
+ ],
+ ),
),
'severity' => $fileSpecificError->canBeIgnored() ? 'major' : 'blocker',
'location' => [
'path' => $this->relativePathHelper->getRelativePath($fileSpecificError->getFile()),
'lines' => [
- 'begin' => $fileSpecificError->getLine(),
+ 'begin' => $fileSpecificError->getLine() ?? 0,
],
],
];
diff --git a/src/Command/ErrorFormatter/JsonErrorFormatter.php b/src/Command/ErrorFormatter/JsonErrorFormatter.php
index c2ea2f6f56..4c075f0ac7 100644
--- a/src/Command/ErrorFormatter/JsonErrorFormatter.php
+++ b/src/Command/ErrorFormatter/JsonErrorFormatter.php
@@ -5,15 +5,14 @@
use Nette\Utils\Json;
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
+use function array_key_exists;
+use function count;
class JsonErrorFormatter implements ErrorFormatter
{
- private bool $pretty;
-
- public function __construct(bool $pretty)
+ public function __construct(private bool $pretty)
{
- $this->pretty = $pretty;
}
public function formatErrors(AnalysisResult $analysisResult, Output $output): int
diff --git a/src/Command/ErrorFormatter/JunitErrorFormatter.php b/src/Command/ErrorFormatter/JunitErrorFormatter.php
index 3476afed65..072f10483f 100644
--- a/src/Command/ErrorFormatter/JunitErrorFormatter.php
+++ b/src/Command/ErrorFormatter/JunitErrorFormatter.php
@@ -5,28 +5,28 @@
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
use PHPStan\File\RelativePathHelper;
+use function htmlspecialchars;
use function sprintf;
+use const ENT_COMPAT;
+use const ENT_XML1;
class JunitErrorFormatter implements ErrorFormatter
{
- private \PHPStan\File\RelativePathHelper $relativePathHelper;
-
- public function __construct(RelativePathHelper $relativePathHelper)
+ public function __construct(private RelativePathHelper $relativePathHelper)
{
- $this->relativePathHelper = $relativePathHelper;
}
public function formatErrors(
AnalysisResult $analysisResult,
- Output $output
+ Output $output,
): int
{
$result = '';
$result .= sprintf(
'',
$analysisResult->getTotalErrorsCount(),
- $analysisResult->getTotalErrorsCount()
+ $analysisResult->getTotalErrorsCount(),
);
foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
@@ -34,7 +34,7 @@ public function formatErrors(
$result .= $this->createTestCase(
sprintf('%s:%s', $fileName, (string) $fileSpecificError->getLine()),
'ERROR',
- $this->escape($fileSpecificError->getMessage())
+ $this->escape($fileSpecificError->getMessage()),
);
}
@@ -60,10 +60,7 @@ public function formatErrors(
/**
* Format a single test case
*
- * @param string $reference
- * @param string|null $message
*
- * @return string
*/
private function createTestCase(string $reference, string $type, ?string $message = null): string
{
@@ -81,8 +78,6 @@ private function createTestCase(string $reference, string $type, ?string $messag
/**
* Escapes values for using in XML
*
- * @param string $string
- * @return string
*/
private function escape(string $string): string
{
diff --git a/src/Command/ErrorFormatter/RawErrorFormatter.php b/src/Command/ErrorFormatter/RawErrorFormatter.php
index 7d926c3b7c..4625983275 100644
--- a/src/Command/ErrorFormatter/RawErrorFormatter.php
+++ b/src/Command/ErrorFormatter/RawErrorFormatter.php
@@ -4,13 +4,14 @@
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
+use function sprintf;
class RawErrorFormatter implements ErrorFormatter
{
public function formatErrors(
AnalysisResult $analysisResult,
- Output $output
+ Output $output,
): int
{
foreach ($analysisResult->getNotFileSpecificErrors() as $notFileSpecificError) {
@@ -24,8 +25,8 @@ public function formatErrors(
'%s:%d:%s',
$fileSpecificError->getFile(),
$fileSpecificError->getLine() ?? '?',
- $fileSpecificError->getMessage()
- )
+ $fileSpecificError->getMessage(),
+ ),
);
$output->writeLineFormatted('');
}
diff --git a/src/Command/ErrorFormatter/TableErrorFormatter.php b/src/Command/ErrorFormatter/TableErrorFormatter.php
index 1ef40d97d1..7a7504f7cf 100644
--- a/src/Command/ErrorFormatter/TableErrorFormatter.php
+++ b/src/Command/ErrorFormatter/TableErrorFormatter.php
@@ -2,35 +2,33 @@
namespace PHPStan\Command\ErrorFormatter;
+use PHPStan\Analyser\Error;
use PHPStan\Command\AnalyseCommand;
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
use PHPStan\File\RelativePathHelper;
+use Symfony\Component\Console\Formatter\OutputFormatter;
+use function array_map;
+use function count;
+use function is_string;
+use function sprintf;
+use function str_replace;
class TableErrorFormatter implements ErrorFormatter
{
- private RelativePathHelper $relativePathHelper;
-
- private bool $showTipsOfTheDay;
-
- private ?string $editorUrl;
-
public function __construct(
- RelativePathHelper $relativePathHelper,
- bool $showTipsOfTheDay,
- ?string $editorUrl = null
+ private RelativePathHelper $relativePathHelper,
+ private bool $showTipsOfTheDay,
+ private ?string $editorUrl,
)
{
- $this->relativePathHelper = $relativePathHelper;
- $this->showTipsOfTheDay = $showTipsOfTheDay;
- $this->editorUrl = $editorUrl;
}
/** @api */
public function formatErrors(
AnalysisResult $analysisResult,
- Output $output
+ Output $output,
): int
{
$projectConfigFile = 'phpstan.neon';
@@ -48,7 +46,7 @@ public function formatErrors(
$output->writeLineFormatted(sprintf(
"PHPStan is performing only the most basic checks.\nYou can pass a higher rule level through the --%s> option\n(the default and current level is %d) to analyse code more thoroughly.",
AnalyseCommand::OPTION_LEVEL,
- AnalyseCommand::DEFAULT_LEVEL
+ AnalyseCommand::DEFAULT_LEVEL,
));
$output->writeLineFormatted('');
}
@@ -57,7 +55,7 @@ public function formatErrors(
return 0;
}
- /** @var array $fileErrors */
+ /** @var array $fileErrors */
$fileErrors = [];
foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
if (!isset($fileErrors[$fileSpecificError->getFile()])) {
@@ -77,7 +75,9 @@ public function formatErrors(
$message .= "\n💡 " . $tip;
}
if (is_string($this->editorUrl)) {
- $message .= "\n✏️ " . str_replace(['%file%', '%line%'], [$error->getTraitFilePath() ?? $error->getFilePath(), (string) $error->getLine()], $this->editorUrl);
+ $editorFile = $error->getTraitFilePath() ?? $error->getFilePath();
+ $url = str_replace(['%file%', '%line%'], [$editorFile, (string) $error->getLine()], $this->editorUrl);
+ $message .= "\n✏️ ' . $this->relativePathHelper->getRelativePath($editorFile) . '>';
}
$rows[] = [
(string) $error->getLine(),
@@ -85,22 +85,16 @@ public function formatErrors(
];
}
- $relativeFilePath = $this->relativePathHelper->getRelativePath($file);
-
- $style->table(['Line', $relativeFilePath], $rows);
+ $style->table(['Line', $this->relativePathHelper->getRelativePath($file)], $rows);
}
if (count($analysisResult->getNotFileSpecificErrors()) > 0) {
- $style->table(['', 'Error'], array_map(static function (string $error): array {
- return ['', $error];
- }, $analysisResult->getNotFileSpecificErrors()));
+ $style->table(['', 'Error'], array_map(static fn (string $error): array => ['', $error], $analysisResult->getNotFileSpecificErrors()));
}
$warningsCount = count($analysisResult->getWarnings());
if ($warningsCount > 0) {
- $style->table(['', 'Warning'], array_map(static function (string $warning): array {
- return ['', $warning];
- }, $analysisResult->getWarnings()));
+ $style->table(['', 'Warning'], array_map(static fn (string $warning): array => ['', $warning], $analysisResult->getWarnings()));
}
$finalMessage = sprintf($analysisResult->getTotalErrorsCount() === 1 ? 'Found %d error' : 'Found %d errors', $analysisResult->getTotalErrorsCount());
diff --git a/src/Command/ErrorFormatter/TeamcityErrorFormatter.php b/src/Command/ErrorFormatter/TeamcityErrorFormatter.php
index 17bbb080af..a6e6e2cf32 100644
--- a/src/Command/ErrorFormatter/TeamcityErrorFormatter.php
+++ b/src/Command/ErrorFormatter/TeamcityErrorFormatter.php
@@ -5,6 +5,12 @@
use PHPStan\Command\AnalysisResult;
use PHPStan\Command\Output;
use PHPStan\File\RelativePathHelper;
+use function array_keys;
+use function array_values;
+use function count;
+use function is_string;
+use function preg_replace;
+use const PHP_EOL;
/**
* @see https://www.jetbrains.com/help/teamcity/build-script-interaction-with-teamcity.html#Reporting+Inspections
@@ -12,11 +18,8 @@
class TeamcityErrorFormatter implements ErrorFormatter
{
- private RelativePathHelper $relativePathHelper;
-
- public function __construct(RelativePathHelper $relativePathHelper)
+ public function __construct(private RelativePathHelper $relativePathHelper)
{
- $this->relativePathHelper = $relativePathHelper;
}
public function formatErrors(AnalysisResult $analysisResult, Output $output): int
diff --git a/src/Command/ErrorsConsoleStyle.php b/src/Command/ErrorsConsoleStyle.php
index f106573a69..76954e735f 100644
--- a/src/Command/ErrorsConsoleStyle.php
+++ b/src/Command/ErrorsConsoleStyle.php
@@ -4,17 +4,28 @@
use OndraM\CiDetector\CiDetector;
use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-
-class ErrorsConsoleStyle extends \Symfony\Component\Console\Style\SymfonyStyle
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\Console\Terminal;
+use function array_unshift;
+use function explode;
+use function implode;
+use function sprintf;
+use function str_starts_with;
+use function strlen;
+use function wordwrap;
+use const DIRECTORY_SEPARATOR;
+
+class ErrorsConsoleStyle extends SymfonyStyle
{
public const OPTION_NO_PROGRESS = 'no-progress';
private bool $showProgress;
- private \Symfony\Component\Console\Helper\ProgressBar $progressBar;
+ private ProgressBar $progressBar;
private ?bool $isCiDetected = null;
@@ -41,7 +52,7 @@ private function isCiDetected(): bool
public function table(array $headers, array $rows): void
{
/** @var int $terminalWidth */
- $terminalWidth = (new \Symfony\Component\Console\Terminal())->getWidth() - 2;
+ $terminalWidth = (new Terminal())->getWidth() - 2;
$maxHeaderWidth = strlen($headers[0]);
foreach ($rows as $row) {
$length = strlen($row[0]);
@@ -52,42 +63,80 @@ public function table(array $headers, array $rows): void
$maxHeaderWidth = $length;
}
- $wrap = static function ($rows) use ($terminalWidth, $maxHeaderWidth): array {
- return array_map(static function ($row) use ($terminalWidth, $maxHeaderWidth): array {
- return array_map(static function ($s) use ($terminalWidth, $maxHeaderWidth) {
- if ($terminalWidth > $maxHeaderWidth + 5) {
- return wordwrap(
- $s,
- $terminalWidth - $maxHeaderWidth - 5,
- "\n",
- true
- );
- }
-
- return $s;
- }, $row);
- }, $rows);
- };
-
- parent::table($headers, $wrap($rows));
+ // manual wrapping could be replaced with $table->setColumnMaxWidth()
+ // but it's buggy for lines
+ // https://github.com/symfony/symfony/issues/45520
+ // https://github.com/symfony/symfony/issues/45521
+ $headers = $this->wrap($headers, $terminalWidth, $maxHeaderWidth);
+ foreach ($headers as $i => $header) {
+ $newHeader = [];
+ foreach (explode("\n", $header) as $h) {
+ $newHeader[] = sprintf('%s', $h);
+ }
+
+ $headers[$i] = implode("\n", $newHeader);
+ }
+
+ foreach ($rows as $i => $row) {
+ $rows[$i] = $this->wrap($row, $terminalWidth, $maxHeaderWidth);
+ }
+
+ $table = $this->createTable();
+ array_unshift($rows, $headers, new TableSeparator());
+ $table->setRows($rows);
+
+ $table->render();
+ $this->newLine();
}
/**
- * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
- * @param int $max
+ * @param string[] $rows
+ * @return string[]
*/
- public function createProgressBar($max = 0): ProgressBar
+ private function wrap(array $rows, int $terminalWidth, int $maxHeaderWidth): array
+ {
+ foreach ($rows as $i => $column) {
+ $columnRows = explode("\n", $column);
+ foreach ($columnRows as $k => $columnRow) {
+ if (str_starts_with($columnRow, '✏️')) {
+ continue;
+ }
+ $columnRows[$k] = wordwrap(
+ $columnRow,
+ $terminalWidth - $maxHeaderWidth - 5,
+ "\n",
+ true,
+ );
+ }
+
+ $rows[$i] = implode("\n", $columnRows);
+ }
+
+ return $rows;
+ }
+
+ public function createProgressBar(int $max = 0): ProgressBar
{
$this->progressBar = parent::createProgressBar($max);
- $this->progressBar->setOverwrite(!$this->isCiDetected());
+
+ $ci = $this->isCiDetected();
+ $this->progressBar->setOverwrite(!$ci);
+
+ if ($ci) {
+ $this->progressBar->minSecondsBetweenRedraws(15);
+ $this->progressBar->maxSecondsBetweenRedraws(30);
+ } elseif (DIRECTORY_SEPARATOR === '\\') {
+ $this->progressBar->minSecondsBetweenRedraws(0.5);
+ $this->progressBar->maxSecondsBetweenRedraws(2);
+ } else {
+ $this->progressBar->minSecondsBetweenRedraws(0.1);
+ $this->progressBar->maxSecondsBetweenRedraws(0.5);
+ }
+
return $this->progressBar;
}
- /**
- * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
- * @param int $max
- */
- public function progressStart($max = 0): void
+ public function progressStart(int $max = 0): void
{
if (!$this->showProgress) {
return;
@@ -95,25 +144,12 @@ public function progressStart($max = 0): void
parent::progressStart($max);
}
- /**
- * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
- * @param int $step
- */
- public function progressAdvance($step = 1): void
+ public function progressAdvance(int $step = 1): void
{
if (!$this->showProgress) {
return;
}
- if (!$this->isCiDetected() && $step > 0) {
- $stepTime = (time() - $this->progressBar->getStartTime()) / $step;
- if ($stepTime > 0 && $stepTime < 1) {
- $this->progressBar->setRedrawFrequency((int) (1 / $stepTime));
- } else {
- $this->progressBar->setRedrawFrequency(1);
- }
- }
-
parent::progressAdvance($step);
}
diff --git a/src/Command/FixerApplication.php b/src/Command/FixerApplication.php
index 957dc82bb0..9d01812a39 100644
--- a/src/Command/FixerApplication.php
+++ b/src/Command/FixerApplication.php
@@ -5,22 +5,32 @@
use Clue\React\NDJson\Decoder;
use Clue\React\NDJson\Encoder;
use Composer\CaBundle\CaBundle;
+use DateTime;
+use DateTimeImmutable;
+use DateTimeZone;
+use Jean85\PrettyVersions;
use Nette\Utils\Json;
+use OutOfBoundsException;
use Phar;
use PHPStan\Analyser\AnalyserResult;
+use PHPStan\Analyser\Error;
use PHPStan\Analyser\IgnoredErrorHelper;
use PHPStan\Analyser\ResultCache\ResultCacheClearer;
use PHPStan\Analyser\ResultCache\ResultCacheManagerFactory;
+use PHPStan\File\CouldNotReadFileException;
use PHPStan\File\FileMonitor;
use PHPStan\File\FileMonitorResult;
use PHPStan\File\FileReader;
use PHPStan\File\FileWriter;
use PHPStan\Parallel\Scheduler;
use PHPStan\Process\CpuCoreCounter;
+use PHPStan\Process\ProcessCanceledException;
+use PHPStan\Process\ProcessCrashedException;
use PHPStan\Process\ProcessHelper;
use PHPStan\Process\ProcessPromise;
use PHPStan\Process\Runnable\RunnableQueue;
use PHPStan\Process\Runnable\RunnableQueueLogger;
+use PHPStan\ShouldNotHappenException;
use Psr\Http\Message\ResponseInterface;
use React\ChildProcess\Process;
use React\EventLoop\LoopInterface;
@@ -31,88 +41,65 @@
use React\Promise\PromiseInterface;
use React\Socket\ConnectionInterface;
use React\Socket\Connector;
+use React\Socket\TcpServer;
+use React\Stream\ReadableStreamInterface;
+use RuntimeException;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use const PHP_BINARY;
+use Throwable;
use function Clue\React\Block\await;
+use function count;
+use function defined;
use function escapeshellarg;
-use function file_exists;
+use function fclose;
+use function fopen;
+use function fwrite;
+use function getenv;
+use function ini_get;
+use function is_dir;
+use function is_file;
+use function is_string;
+use function min;
+use function mkdir;
+use function parse_url;
use function React\Promise\resolve;
+use function sprintf;
+use function strlen;
+use function strpos;
+use function unlink;
+use const PHP_BINARY;
+use const PHP_URL_PORT;
class FixerApplication
{
- /** @var FileMonitor */
- private $fileMonitor;
-
- /** @var ResultCacheManagerFactory */
- private $resultCacheManagerFactory;
-
- /** @var ResultCacheClearer */
- private $resultCacheClearer;
-
- /** @var IgnoredErrorHelper */
- private $ignoredErrorHelper;
-
- /** @var CpuCoreCounter */
- private $cpuCoreCounter;
-
- /** @var Scheduler */
- private $scheduler;
-
- /** @var string[] */
- private $analysedPaths;
-
/** @var (ExtendedPromiseInterface&CancellablePromiseInterface)|null */
private $processInProgress;
- /** @var string */
- private $currentWorkingDirectory;
-
- /** @var string */
- private $fixerTmpDir;
-
- private int $maximumNumberOfProcesses;
-
- /** @var string|null */
- private $fixerSuggestionId;
+ private ?string $fixerSuggestionId = null;
/**
- * @param FileMonitor $fileMonitor
- * @param ResultCacheManagerFactory $resultCacheManagerFactory
* @param string[] $analysedPaths
*/
public function __construct(
- FileMonitor $fileMonitor,
- ResultCacheManagerFactory $resultCacheManagerFactory,
- ResultCacheClearer $resultCacheClearer,
- IgnoredErrorHelper $ignoredErrorHelper,
- CpuCoreCounter $cpuCoreCounter,
- Scheduler $scheduler,
- array $analysedPaths,
- string $currentWorkingDirectory,
- string $fixerTmpDir,
- int $maximumNumberOfProcesses
+ private FileMonitor $fileMonitor,
+ private ResultCacheManagerFactory $resultCacheManagerFactory,
+ private ResultCacheClearer $resultCacheClearer,
+ private IgnoredErrorHelper $ignoredErrorHelper,
+ private CpuCoreCounter $cpuCoreCounter,
+ private Scheduler $scheduler,
+ private array $analysedPaths,
+ private string $currentWorkingDirectory,
+ private string $fixerTmpDir,
+ private int $maximumNumberOfProcesses,
)
{
- $this->fileMonitor = $fileMonitor;
- $this->resultCacheManagerFactory = $resultCacheManagerFactory;
- $this->resultCacheClearer = $resultCacheClearer;
- $this->ignoredErrorHelper = $ignoredErrorHelper;
- $this->cpuCoreCounter = $cpuCoreCounter;
- $this->scheduler = $scheduler;
- $this->analysedPaths = $analysedPaths;
- $this->currentWorkingDirectory = $currentWorkingDirectory;
- $this->fixerTmpDir = $fixerTmpDir;
- $this->maximumNumberOfProcesses = $maximumNumberOfProcesses;
}
/**
- * @param \Symfony\Component\Console\Output\OutputInterface $output
- * @param \PHPStan\Analyser\Error[] $fileSpecificErrors
+ * @param Error[] $fileSpecificErrors
* @param string[] $notFileSpecificErrors
- * @return int
*/
public function run(
?string $projectConfigFile,
@@ -122,11 +109,11 @@ public function run(
array $fileSpecificErrors,
array $notFileSpecificErrors,
int $filesCount,
- string $mainScript
+ string $mainScript,
): int
{
$loop = new StreamSelectLoop();
- $server = new \React\Socket\TcpServer('127.0.0.1:0', $loop);
+ $server = new TcpServer('127.0.0.1:0', $loop);
/** @var string $serverAddress */
$serverAddress = $server->getAddress();
@@ -141,12 +128,15 @@ public function log(string $message): void
}
},
- min($this->cpuCoreCounter->getNumberOfCpuCores(), $this->maximumNumberOfProcesses)
+ min($this->cpuCoreCounter->getNumberOfCpuCores(), $this->maximumNumberOfProcesses),
);
$server->on('connection', function (ConnectionInterface $connection) use ($loop, $projectConfigFile, $input, $output, $fileSpecificErrors, $notFileSpecificErrors, $mainScript, $filesCount, $reanalyseProcessQueue, $inceptionResult): void {
- $decoder = new Decoder($connection, true, 512, defined('JSON_INVALID_UTF8_IGNORE') ? JSON_INVALID_UTF8_IGNORE : 0, 128 * 1024 * 1024);
- $encoder = new Encoder($connection, defined('JSON_INVALID_UTF8_IGNORE') ? JSON_INVALID_UTF8_IGNORE : 0);
+ // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly
+ $jsonInvalidUtf8Ignore = defined('JSON_INVALID_UTF8_IGNORE') ? JSON_INVALID_UTF8_IGNORE : 0;
+ // phpcs:enable
+ $decoder = new Decoder($connection, true, 512, $jsonInvalidUtf8Ignore, 128 * 1024 * 1024);
+ $encoder = new Encoder($connection, $jsonInvalidUtf8Ignore);
$encoder->write(['action' => 'initialData', 'data' => [
'fileSpecificErrors' => $fileSpecificErrors,
'notFileSpecificErrors' => $notFileSpecificErrors,
@@ -189,16 +179,16 @@ public function log(string $message): void
$data['data']['tmpFile'],
$data['data']['insteadOfFile'],
$data['data']['fixerSuggestionId'],
- $input
+ $input,
)->done(static function (string $output) use ($encoder, $id): void {
$encoder->write(['id' => $id, 'response' => Json::decode($output, Json::FORCE_ARRAY)]);
- }, static function (\Throwable $e) use ($encoder, $id, $output): void {
- if ($e instanceof \PHPStan\Process\ProcessCrashedException) {
+ }, static function (Throwable $e) use ($encoder, $id, $output): void {
+ if ($e instanceof ProcessCrashedException) {
$output->writeln('Worker process exited: ' . $e->getMessage() . '');
$encoder->write(['id' => $id, 'error' => $e->getMessage()]);
return;
}
- if ($e instanceof \PHPStan\Process\ProcessCanceledException) {
+ if ($e instanceof ProcessCanceledException) {
$encoder->write(['id' => $id, 'error' => $e->getMessage()]);
return;
}
@@ -224,7 +214,7 @@ public function log(string $message): void
$mainScript,
$projectConfigFile,
$this->fixerSuggestionId,
- $input
+ $input,
)->done(function (array $json) use ($encoder, $changes): void {
$this->processInProgress = null;
$this->fixerSuggestionId = null;
@@ -234,7 +224,7 @@ public function log(string $message): void
'filesCount' => $changes->getTotalFilesCount(),
]]);
$this->resultCacheClearer->clearTemporaryCaches();
- }, function (\Throwable $e) use ($encoder, $output): void {
+ }, function (Throwable $e) use ($encoder, $output): void {
$this->processInProgress = null;
$this->fixerSuggestionId = null;
$output->writeln('Worker process exited: ' . $e->getMessage() . '');
@@ -247,7 +237,7 @@ public function log(string $message): void
try {
$fixerProcess = $this->getFixerProcess($output, $serverPort);
- } catch (\PHPStan\Command\FixerProcessException $e) {
+ } catch (FixerProcessException) {
return 1;
}
@@ -275,7 +265,7 @@ private function getFixerProcess(OutputInterface $output, int $serverPort): Proc
{
if (!@mkdir($this->fixerTmpDir, 0777) && !is_dir($this->fixerTmpDir)) {
$output->writeln(sprintf('Cannot create a temp directory %s', $this->fixerTmpDir));
- throw new \PHPStan\Command\FixerProcessException();
+ throw new FixerProcessException();
}
$pharPath = $this->fixerTmpDir . '/phpstan-fixer.phar';
@@ -283,12 +273,12 @@ private function getFixerProcess(OutputInterface $output, int $serverPort): Proc
try {
$this->downloadPhar($output, $pharPath, $infoPath);
- } catch (\RuntimeException $e) {
- if (!file_exists($pharPath)) {
+ } catch (RuntimeException $e) {
+ if (!is_file($pharPath)) {
$output->writeln('Could not download the PHPStan Pro executable.>');
$output->writeln($e->getMessage());
- throw new \PHPStan\Command\FixerProcessException();
+ throw new FixerProcessException();
}
}
@@ -297,12 +287,12 @@ private function getFixerProcess(OutputInterface $output, int $serverPort): Proc
try {
$phar = new Phar($pharPath);
- } catch (\Throwable $e) {
+ } catch (Throwable) {
@unlink($pharPath);
@unlink($infoPath);
$output->writeln('PHPStan Pro PHAR signature is corrupted.>');
- throw new \PHPStan\Command\FixerProcessException();
+ throw new FixerProcessException();
}
if ($phar->getSignature()['hash_type'] !== 'OpenSSL') {
@@ -310,10 +300,10 @@ private function getFixerProcess(OutputInterface $output, int $serverPort): Proc
@unlink($infoPath);
$output->writeln('PHPStan Pro PHAR signature is corrupted.>');
- throw new \PHPStan\Command\FixerProcessException();
+ throw new FixerProcessException();
}
- $env = null;
+ $env = getenv();
$forcedPort = $_SERVER['PHPSTAN_PRO_WEB_PORT'] ?? null;
if ($forcedPort !== null) {
$env['PHPSTAN_PRO_WEB_PORT'] = $_SERVER['PHPSTAN_PRO_WEB_PORT'];
@@ -352,19 +342,19 @@ private function getFixerProcess(OutputInterface $output, int $serverPort): Proc
private function downloadPhar(
OutputInterface $output,
string $pharPath,
- string $infoPath
+ string $infoPath,
): void
{
$currentVersion = null;
- if (file_exists($pharPath) && file_exists($infoPath)) {
+ if (is_file($pharPath) && is_file($infoPath)) {
/** @var array{version: string, date: string} $currentInfo */
$currentInfo = Json::decode(FileReader::read($infoPath), Json::FORCE_ARRAY);
$currentVersion = $currentInfo['version'];
- $currentDate = \DateTime::createFromFormat(\DateTime::ATOM, $currentInfo['date']);
+ $currentDate = DateTime::createFromFormat(DateTime::ATOM, $currentInfo['date']);
if ($currentDate === false) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
- if ((new \DateTimeImmutable('', new \DateTimeZone('UTC'))) <= $currentDate->modify('+24 hours')) {
+ if ((new DateTimeImmutable('', new DateTimeZone('UTC'))) <= $currentDate->modify('+24 hours')) {
return;
}
@@ -382,12 +372,15 @@ private function downloadPhar(
'cafile' => CaBundle::getBundledCaBundlePath(),
],
'dns' => '1.1.1.1',
- ]
- )
+ ],
+ ),
);
- /** @var array{url: string, version: string} $latestInfo */
- $latestInfo = Json::decode((string) await($client->get('https://fixer-download-api.phpstan.com/latest'), $loop, 5.0)->getBody(), Json::FORCE_ARRAY); // @phpstan-ignore-line
+ /**
+ * @var array{url: string, version: string} $latestInfo
+ * @phpstan-ignore-next-line
+ */
+ $latestInfo = Json::decode((string) await($client->get('https://fixer-download-api.phpstan.com/latest'), $loop, 5.0)->getBody(), Json::FORCE_ARRAY);
if ($currentVersion !== null && $latestInfo['version'] === $currentVersion) {
$this->writeInfoFile($infoPath, $latestInfo['version']);
$output->writeln('You\'re running the latest PHPStan Pro!>');
@@ -398,13 +391,13 @@ private function downloadPhar(
$pharPathResource = fopen($pharPath, 'w');
if ($pharPathResource === false) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Could not open file %s for writing.', $pharPath));
+ throw new ShouldNotHappenException(sprintf('Could not open file %s for writing.', $pharPath));
}
$progressBar = new ProgressBar($output);
$client->requestStreaming('GET', $latestInfo['url'])->done(static function (ResponseInterface $response) use ($progressBar, $pharPathResource): void {
$body = $response->getBody();
- if (!$body instanceof \React\Stream\ReadableStreamInterface) {
- throw new \PHPStan\ShouldNotHappenException();
+ if (!$body instanceof ReadableStreamInterface) {
+ throw new ShouldNotHappenException();
}
$totalSize = (int) $response->getHeaderLine('Content-Length');
@@ -418,7 +411,7 @@ private function downloadPhar(
fwrite($pharPathResource, $chunk);
$progressBar->setProgress($bytes);
});
- }, static function (\Throwable $e) use ($output): void {
+ }, static function (Throwable $e) use ($output): void {
$output->writeln(sprintf('Could not download the PHPStan Pro executable:> %s', $e->getMessage()));
});
@@ -437,12 +430,11 @@ private function writeInfoFile(string $infoPath, string $version): void
{
FileWriter::write($infoPath, Json::encode([
'version' => $version,
- 'date' => (new \DateTimeImmutable('', new \DateTimeZone('UTC')))->format(\DateTime::ATOM),
+ 'date' => (new DateTimeImmutable('', new DateTimeZone('UTC')))->format(DateTime::ATOM),
]));
}
/**
- * @param LoopInterface $loop
* @param callable(FileMonitorResult): void $hasChangesCallback
*/
private function monitorFileChanges(LoopInterface $loop, callable $hasChangesCallback): void
@@ -468,7 +460,7 @@ private function reanalyseWithTmpFile(
string $tmpFile,
string $insteadOfFile,
string $fixerSuggestionId,
- InputInterface $input
+ InputInterface $input,
): PromiseInterface
{
$resultCacheManager = $this->resultCacheManagerFactory->create([$insteadOfFile => $tmpFile]);
@@ -489,7 +481,7 @@ private function reanalyseWithTmpFile(
escapeshellarg($fixerSuggestionId),
'--allow-parallel',
],
- $input
+ $input,
));
return $runnableQueue->queue($process, $schedule->getNumberOfProcesses());
@@ -501,12 +493,12 @@ private function reanalyseAfterFileChanges(
string $mainScript,
?string $projectConfigFile,
?string $fixerSuggestionId,
- InputInterface $input
+ InputInterface $input,
): PromiseInterface
{
$ignoredErrorHelperResult = $this->ignoredErrorHelper->initialize();
if (count($ignoredErrorHelperResult->getErrors()) > 0) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$projectConfigArray = $inceptionResult->getProjectConfigArray();
@@ -520,13 +512,13 @@ private function reanalyseAfterFileChanges(
$resultCache,
$inceptionResult->getErrorOutput(),
false,
- true
+ true,
)->getAnalyserResult();
$intermediateErrors = $ignoredErrorHelperResult->process(
$result->getErrors(),
$isOnlyFiles,
$inceptionFiles,
- count($result->getInternalErrors()) > 0 || $result->hasReachedInternalErrorsCountLimit()
+ count($result->getInternalErrors()) > 0 || $result->hasReachedInternalErrorsCountLimit(),
);
$finalFileSpecificErrors = [];
$finalNotFileSpecificErrors = [];
@@ -555,20 +547,18 @@ private function reanalyseAfterFileChanges(
'fixer:worker',
$projectConfigFile,
$options,
- $input
+ $input,
));
$this->processInProgress = $process->run();
- return $this->processInProgress->then(static function (string $output): array {
- return Json::decode($output, Json::FORCE_ARRAY);
- });
+ return $this->processInProgress->then(static fn (string $output): array => Json::decode($output, Json::FORCE_ARRAY));
}
private function getPhpstanVersion(): string
{
try {
- return \Jean85\PrettyVersions::getVersion('phpstan/phpstan')->getPrettyVersion();
- } catch (\OutOfBoundsException $e) {
+ return PrettyVersions::getVersion('phpstan/phpstan')->getPrettyVersion();
+ } catch (OutOfBoundsException) {
return 'Version unknown';
}
}
@@ -583,7 +573,7 @@ private function isDockerRunning(): bool
$contents = FileReader::read('/proc/1/cgroup');
return strpos($contents, 'docker') !== false;
- } catch (\PHPStan\File\CouldNotReadFileException $e) {
+ } catch (CouldNotReadFileException) {
return false;
}
}
diff --git a/src/Command/FixerProcessException.php b/src/Command/FixerProcessException.php
index 996000c6ad..9473f35716 100644
--- a/src/Command/FixerProcessException.php
+++ b/src/Command/FixerProcessException.php
@@ -2,7 +2,9 @@
namespace PHPStan\Command;
-class FixerProcessException extends \Exception
+use Exception;
+
+class FixerProcessException extends Exception
{
}
diff --git a/src/Command/FixerWorkerCommand.php b/src/Command/FixerWorkerCommand.php
index 3a82fac66f..72ec027415 100644
--- a/src/Command/FixerWorkerCommand.php
+++ b/src/Command/FixerWorkerCommand.php
@@ -7,29 +7,30 @@
use PHPStan\Analyser\IgnoredErrorHelper;
use PHPStan\Analyser\ResultCache\ResultCacheManager;
use PHPStan\Analyser\ResultCache\ResultCacheManagerFactory;
+use PHPStan\ShouldNotHappenException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use function count;
+use function is_array;
+use function is_bool;
+use function is_string;
class FixerWorkerCommand extends Command
{
private const NAME = 'fixer:worker';
- /** @var string[] */
- private $composerAutoloaderProjectPaths;
-
/**
* @param string[] $composerAutoloaderProjectPaths
*/
public function __construct(
- array $composerAutoloaderProjectPaths
+ private array $composerAutoloaderProjectPaths,
)
{
parent::__construct();
- $this->composerAutoloaderProjectPaths = $composerAutoloaderProjectPaths;
}
protected function configure(): void
@@ -38,7 +39,6 @@ protected function configure(): void
->setDescription('(Internal) Support for PHPStan Pro.')
->setDefinition([
new InputArgument('paths', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Paths with source code to run analysis on'),
- new InputOption('paths-file', null, InputOption::VALUE_REQUIRED, 'Path to a file with a list of paths to run analysis on'),
new InputOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Path to project configuration file'),
new InputOption(AnalyseCommand::OPTION_LEVEL, 'l', InputOption::VALUE_REQUIRED, 'Level of rule options - the higher the stricter'),
new InputOption('autoload-file', 'a', InputOption::VALUE_REQUIRED, 'Project\'s additional autoload file path'),
@@ -59,7 +59,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$autoloadFile = $input->getOption('autoload-file');
$configuration = $input->getOption('configuration');
$level = $input->getOption(AnalyseCommand::OPTION_LEVEL);
- $pathsFile = $input->getOption('paths-file');
$allowXdebug = $input->getOption('xdebug');
$allowParallel = $input->getOption('allow-parallel');
@@ -69,11 +68,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
|| (!is_string($autoloadFile) && $autoloadFile !== null)
|| (!is_string($configuration) && $configuration !== null)
|| (!is_string($level) && $level !== null)
- || (!is_string($pathsFile) && $pathsFile !== null)
|| (!is_bool($allowXdebug))
|| (!is_bool($allowParallel))
) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
/** @var string|null $tmpFile */
@@ -89,12 +87,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$restoreResultCache = $input->getOption('restore-result-cache');
if (is_string($tmpFile)) {
if (!is_string($insteadOfFile)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
} elseif (is_string($insteadOfFile)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
} elseif ($saveResultCache === false) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$singleReflectionFile = null;
@@ -107,7 +105,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$input,
$output,
$paths,
- $pathsFile,
$memoryLimit,
$autoloadFile,
$this->composerAutoloaderProjectPaths,
@@ -116,11 +113,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$level,
$allowXdebug,
false,
- false,
$singleReflectionFile,
- $insteadOfFile
+ $insteadOfFile,
+ false,
);
- } catch (\PHPStan\Command\InceptionNotSuccessfulException $e) {
+ } catch (InceptionNotSuccessfulException) {
return 1;
}
@@ -130,7 +127,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$ignoredErrorHelper = $container->getByType(IgnoredErrorHelper::class);
$ignoredErrorHelperResult = $ignoredErrorHelper->initialize();
if (count($ignoredErrorHelperResult->getErrors()) > 0) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
/** @var AnalyserRunner $analyserRunner */
@@ -156,21 +153,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$configuration,
$tmpFile,
$insteadOfFile,
- $input
+ $input,
);
$result = $resultCacheManager->process(
$this->switchTmpFileInAnalyserResult($intermediateAnalyserResult, $tmpFile, $insteadOfFile),
$resultCache,
$inceptionResult->getErrorOutput(),
false,
- is_string($saveResultCache) ? $saveResultCache : $saveResultCache === null
+ is_string($saveResultCache) ? $saveResultCache : $saveResultCache === null,
)->getAnalyserResult();
$intermediateErrors = $ignoredErrorHelperResult->process(
$result->getErrors(),
$isOnlyFiles,
$inceptionFiles,
- count($result->getInternalErrors()) > 0 || $result->hasReachedInternalErrorsCountLimit()
+ count($result->getInternalErrors()) > 0 || $result->hasReachedInternalErrorsCountLimit(),
);
$finalFileSpecificErrors = [];
$finalNotFileSpecificErrors = [];
@@ -194,7 +191,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
private function switchTmpFileInAnalyserResult(
AnalyserResult $analyserResult,
?string $insteadOfFile,
- ?string $tmpFile
+ ?string $tmpFile,
): AnalyserResult
{
$fileSpecificErrors = [];
@@ -255,7 +252,7 @@ private function switchTmpFileInAnalyserResult(
$analyserResult->getInternalErrors(),
$dependencies,
$exportedNodes,
- $analyserResult->hasReachedInternalErrorsCountLimit()
+ $analyserResult->hasReachedInternalErrorsCountLimit(),
);
}
diff --git a/src/Command/IgnoredRegexValidator.php b/src/Command/IgnoredRegexValidator.php
index 7e648f86e8..59efbf03b5 100644
--- a/src/Command/IgnoredRegexValidator.php
+++ b/src/Command/IgnoredRegexValidator.php
@@ -4,26 +4,26 @@
use Hoa\Compiler\Llk\Parser;
use Hoa\Compiler\Llk\TreeNode;
+use Hoa\Exception\Exception;
use Nette\Utils\Strings;
use PHPStan\PhpDoc\TypeStringResolver;
+use PHPStan\PhpDocParser\Parser\ParserException;
+use PHPStan\ShouldNotHappenException;
use PHPStan\Type\ObjectType;
use PHPStan\Type\VerbosityLevel;
+use function count;
+use function strpos;
+use function strrpos;
use function substr;
class IgnoredRegexValidator
{
- private Parser $parser;
-
- private \PHPStan\PhpDoc\TypeStringResolver $typeStringResolver;
-
public function __construct(
- Parser $parser,
- TypeStringResolver $typeStringResolver
+ private Parser $parser,
+ private TypeStringResolver $typeStringResolver,
)
{
- $this->parser = $parser;
- $this->typeStringResolver = $typeStringResolver;
}
public function validate(string $regex): IgnoredRegexValidatorResult
@@ -33,7 +33,7 @@ public function validate(string $regex): IgnoredRegexValidatorResult
try {
/** @var TreeNode $ast */
$ast = $this->parser->parse($regex);
- } catch (\Hoa\Exception\Exception $e) {
+ } catch (Exception $e) {
if (strpos($e->getMessage(), 'Unexpected token "|" (alternation) at line 1') === 0) {
return new IgnoredRegexValidatorResult([], false, true, '||', '\|\|');
}
@@ -49,12 +49,11 @@ public function validate(string $regex): IgnoredRegexValidatorResult
return new IgnoredRegexValidatorResult(
$this->getIgnoredTypes($ast),
$this->hasAnchorsInTheMiddle($ast),
- false
+ false,
);
}
/**
- * @param TreeNode $ast
* @return array
*/
private function getIgnoredTypes(TreeNode $ast): array
@@ -83,7 +82,7 @@ private function getIgnoredTypes(TreeNode $ast): array
try {
$type = $this->typeStringResolver->resolve($matches[1], null);
- } catch (\PHPStan\PhpDocParser\Parser\ParserException $e) {
+ } catch (ParserException) {
continue;
}
@@ -106,7 +105,7 @@ private function removeDelimiters(string $regex): string
$delimiter = substr($regex, 0, 1);
$endDelimiterPosition = strrpos($regex, $delimiter);
if ($endDelimiterPosition === false) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return substr($regex, 1, $endDelimiterPosition - 1);
diff --git a/src/Command/IgnoredRegexValidatorResult.php b/src/Command/IgnoredRegexValidatorResult.php
index 0acda5dd11..fcda6a015b 100644
--- a/src/Command/IgnoredRegexValidatorResult.php
+++ b/src/Command/IgnoredRegexValidatorResult.php
@@ -5,35 +5,17 @@
class IgnoredRegexValidatorResult
{
- /** @var array */
- private array $ignoredTypes;
-
- private bool $anchorsInTheMiddle;
-
- private bool $allErrorsIgnored;
-
- private ?string $wrongSequence;
-
- private ?string $escapedWrongSequence;
-
/**
* @param array $ignoredTypes
- * @param bool $anchorsInTheMiddle
- * @param bool $allErrorsIgnored
*/
public function __construct(
- array $ignoredTypes,
- bool $anchorsInTheMiddle,
- bool $allErrorsIgnored,
- ?string $wrongSequence = null,
- ?string $escapedWrongSequence = null
+ private array $ignoredTypes,
+ private bool $anchorsInTheMiddle,
+ private bool $allErrorsIgnored,
+ private ?string $wrongSequence = null,
+ private ?string $escapedWrongSequence = null,
)
{
- $this->ignoredTypes = $ignoredTypes;
- $this->anchorsInTheMiddle = $anchorsInTheMiddle;
- $this->allErrorsIgnored = $allErrorsIgnored;
- $this->wrongSequence = $wrongSequence;
- $this->escapedWrongSequence = $escapedWrongSequence;
}
/**
diff --git a/src/Command/InceptionNotSuccessfulException.php b/src/Command/InceptionNotSuccessfulException.php
index 872b71cfaa..f17c3e08ec 100644
--- a/src/Command/InceptionNotSuccessfulException.php
+++ b/src/Command/InceptionNotSuccessfulException.php
@@ -2,7 +2,9 @@
namespace PHPStan\Command;
-class InceptionNotSuccessfulException extends \Exception
+use Exception;
+
+class InceptionNotSuccessfulException extends Exception
{
}
diff --git a/src/Command/InceptionResult.php b/src/Command/InceptionResult.php
index d0b778a9d8..8a0010c8c2 100644
--- a/src/Command/InceptionResult.php
+++ b/src/Command/InceptionResult.php
@@ -5,6 +5,7 @@
use PHPStan\DependencyInjection\Container;
use PHPStan\Internal\BytesHelper;
use function memory_get_peak_usage;
+use function sprintf;
class InceptionResult
{
@@ -12,55 +13,22 @@ class InceptionResult
/** @var callable(): (array{string[], bool}) */
private $filesCallback;
- private Output $stdOutput;
-
- private Output $errorOutput;
-
- private \PHPStan\DependencyInjection\Container $container;
-
- private bool $isDefaultLevelUsed;
-
- private string $memoryLimitFile;
-
- private ?string $projectConfigFile;
-
- /** @var mixed[]|null */
- private ?array $projectConfigArray;
-
- private ?string $generateBaselineFile;
-
/**
* @param callable(): (array{string[], bool}) $filesCallback
- * @param Output $stdOutput
- * @param Output $errorOutput
- * @param \PHPStan\DependencyInjection\Container $container
- * @param bool $isDefaultLevelUsed
- * @param string $memoryLimitFile
- * @param string|null $projectConfigFile
- * @param mixed[] $projectConfigArray
- * @param string|null $generateBaselineFile
+ * @param mixed[]|null $projectConfigArray
*/
public function __construct(
callable $filesCallback,
- Output $stdOutput,
- Output $errorOutput,
- Container $container,
- bool $isDefaultLevelUsed,
- string $memoryLimitFile,
- ?string $projectConfigFile,
- ?array $projectConfigArray,
- ?string $generateBaselineFile
+ private Output $stdOutput,
+ private Output $errorOutput,
+ private Container $container,
+ private bool $isDefaultLevelUsed,
+ private ?string $projectConfigFile,
+ private ?array $projectConfigArray,
+ private ?string $generateBaselineFile,
)
{
$this->filesCallback = $filesCallback;
- $this->stdOutput = $stdOutput;
- $this->errorOutput = $errorOutput;
- $this->container = $container;
- $this->isDefaultLevelUsed = $isDefaultLevelUsed;
- $this->memoryLimitFile = $memoryLimitFile;
- $this->projectConfigFile = $projectConfigFile;
- $this->projectConfigArray = $projectConfigArray;
- $this->generateBaselineFile = $generateBaselineFile;
}
/**
@@ -117,7 +85,6 @@ public function handleReturn(int $exitCode): int
$this->getErrorOutput()->writeLineFormatted(sprintf('Used memory: %s', BytesHelper::bytes(memory_get_peak_usage(true))));
}
- @unlink($this->memoryLimitFile);
return $exitCode;
}
diff --git a/src/Command/Symfony/SymfonyOutput.php b/src/Command/Symfony/SymfonyOutput.php
index ba12d8206d..5e1a46273a 100644
--- a/src/Command/Symfony/SymfonyOutput.php
+++ b/src/Command/Symfony/SymfonyOutput.php
@@ -12,17 +12,11 @@
class SymfonyOutput implements Output
{
- private \Symfony\Component\Console\Output\OutputInterface $symfonyOutput;
-
- private OutputStyle $style;
-
public function __construct(
- OutputInterface $symfonyOutput,
- OutputStyle $style
+ private OutputInterface $symfonyOutput,
+ private OutputStyle $style,
)
{
- $this->symfonyOutput = $symfonyOutput;
- $this->style = $style;
}
public function writeFormatted(string $message): void
diff --git a/src/Command/Symfony/SymfonyStyle.php b/src/Command/Symfony/SymfonyStyle.php
index ba2f25ee16..99ed87ec33 100644
--- a/src/Command/Symfony/SymfonyStyle.php
+++ b/src/Command/Symfony/SymfonyStyle.php
@@ -11,14 +11,11 @@
class SymfonyStyle implements OutputStyle
{
- private \Symfony\Component\Console\Style\StyleInterface $symfonyStyle;
-
- public function __construct(StyleInterface $symfonyStyle)
+ public function __construct(private StyleInterface $symfonyStyle)
{
- $this->symfonyStyle = $symfonyStyle;
}
- public function getSymfonyStyle(): \Symfony\Component\Console\Style\StyleInterface
+ public function getSymfonyStyle(): StyleInterface
{
return $this->symfonyStyle;
}
diff --git a/src/Command/WorkerCommand.php b/src/Command/WorkerCommand.php
index e1154d3829..c55d30bd62 100644
--- a/src/Command/WorkerCommand.php
+++ b/src/Command/WorkerCommand.php
@@ -7,7 +7,9 @@
use PHPStan\Analyser\FileAnalyser;
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\DependencyInjection\Container;
+use PHPStan\File\PathNotFoundException;
use PHPStan\Rules\Registry;
+use PHPStan\ShouldNotHappenException;
use React\EventLoop\StreamSelectLoop;
use React\Socket\ConnectionInterface;
use React\Socket\TcpConnector;
@@ -18,26 +20,32 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use Throwable;
+use function array_fill_keys;
+use function array_filter;
+use function array_values;
+use function count;
+use function defined;
+use function is_array;
+use function is_bool;
+use function is_string;
+use function sprintf;
class WorkerCommand extends Command
{
private const NAME = 'worker';
- /** @var string[] */
- private array $composerAutoloaderProjectPaths;
-
private int $errorCount = 0;
/**
* @param string[] $composerAutoloaderProjectPaths
*/
public function __construct(
- array $composerAutoloaderProjectPaths
+ private array $composerAutoloaderProjectPaths,
)
{
parent::__construct();
- $this->composerAutoloaderProjectPaths = $composerAutoloaderProjectPaths;
}
protected function configure(): void
@@ -46,7 +54,6 @@ protected function configure(): void
->setDescription('(Internal) Support for parallel analysis.')
->setDefinition([
new InputArgument('paths', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Paths with source code to run analysis on'),
- new InputOption('paths-file', null, InputOption::VALUE_REQUIRED, 'Path to a file with a list of paths to run analysis on'),
new InputOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Path to project configuration file'),
new InputOption(AnalyseCommand::OPTION_LEVEL, 'l', InputOption::VALUE_REQUIRED, 'Level of rule options - the higher the stricter'),
new InputOption('autoload-file', 'a', InputOption::VALUE_REQUIRED, 'Project\'s additional autoload file path'),
@@ -66,7 +73,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$autoloadFile = $input->getOption('autoload-file');
$configuration = $input->getOption('configuration');
$level = $input->getOption(AnalyseCommand::OPTION_LEVEL);
- $pathsFile = $input->getOption('paths-file');
$allowXdebug = $input->getOption('xdebug');
$port = $input->getOption('port');
$identifier = $input->getOption('identifier');
@@ -77,12 +83,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
|| (!is_string($autoloadFile) && $autoloadFile !== null)
|| (!is_string($configuration) && $configuration !== null)
|| (!is_string($level) && $level !== null)
- || (!is_string($pathsFile) && $pathsFile !== null)
|| (!is_bool($allowXdebug))
|| !is_string($port)
|| !is_string($identifier)
) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
/** @var string|null $tmpFile */
@@ -101,7 +106,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$input,
$output,
$paths,
- $pathsFile,
$memoryLimit,
$autoloadFile,
$this->composerAutoloaderProjectPaths,
@@ -110,10 +114,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$level,
$allowXdebug,
false,
+ $singleReflectionFile,
+ null,
false,
- $singleReflectionFile
);
- } catch (\PHPStan\Command\InceptionNotSuccessfulException $e) {
+ } catch (InceptionNotSuccessfulException $e) {
return 1;
}
$loop = new StreamSelectLoop();
@@ -123,7 +128,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
try {
[$analysedFiles] = $inceptionResult->getFiles();
$analysedFiles = $this->switchTmpFile($analysedFiles, $insteadOfFile, $tmpFile);
- } catch (\PHPStan\File\PathNotFoundException $e) {
+ } catch (PathNotFoundException $e) {
$inceptionResult->getErrorOutput()->writeLineFormatted(sprintf('%s', $e->getMessage()));
return 1;
}
@@ -136,8 +141,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$tcpConector = new TcpConnector($loop);
$tcpConector->connect(sprintf('127.0.0.1:%d', $port))->done(function (ConnectionInterface $connection) use ($container, $identifier, $output, $analysedFiles, $tmpFile, $insteadOfFile): void {
- $out = new Encoder($connection, defined('JSON_INVALID_UTF8_IGNORE') ? JSON_INVALID_UTF8_IGNORE : 0);
- $in = new Decoder($connection, true, 512, defined('JSON_INVALID_UTF8_IGNORE') ? JSON_INVALID_UTF8_IGNORE : 0, $container->getParameter('parallel')['buffer']);
+ // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly
+ $jsonInvalidUtf8Ignore = defined('JSON_INVALID_UTF8_IGNORE') ? JSON_INVALID_UTF8_IGNORE : 0;
+ // phpcs:enable
+ $out = new Encoder($connection, $jsonInvalidUtf8Ignore);
+ $in = new Decoder($connection, true, 512, $jsonInvalidUtf8Ignore, $container->getParameter('parallel')['buffer']);
$out->write(['action' => 'hello', 'identifier' => $identifier]);
$this->runWorker($container, $out, $in, $output, $analysedFiles, $tmpFile, $insteadOfFile);
});
@@ -152,13 +160,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
/**
- * @param Container $container
- * @param WritableStreamInterface $out
- * @param ReadableStreamInterface $in
- * @param OutputInterface $output
* @param array $analysedFiles
- * @param string|null $tmpFile
- * @param string|null $insteadOfFile
*/
private function runWorker(
Container $container,
@@ -167,10 +169,10 @@ private function runWorker(
OutputInterface $output,
array $analysedFiles,
?string $tmpFile,
- ?string $insteadOfFile
+ ?string $insteadOfFile,
): void
{
- $handleError = function (\Throwable $error) use ($out, $output): void {
+ $handleError = function (Throwable $error) use ($out, $output): void {
$this->errorCount++;
$output->writeln(sprintf('Error: %s', $error->getMessage()));
$out->write([
@@ -185,14 +187,11 @@ private function runWorker(
$out->end();
};
$out->on('error', $handleError);
-
/** @var FileAnalyser $fileAnalyser */
$fileAnalyser = $container->getByType(FileAnalyser::class);
-
/** @var Registry $registry */
$registry = $container->getByType(Registry::class);
-
- $in->on('data', function (array $json) use ($fileAnalyser, $registry, $out, $analysedFiles, $tmpFile, $insteadOfFile): void {
+ $in->on('data', function (array $json) use ($fileAnalyser, $registry, $out, $analysedFiles, $tmpFile, $insteadOfFile, $output): void {
$action = $json['action'];
if ($action !== 'analyse') {
return;
@@ -215,16 +214,18 @@ private function runWorker(
foreach ($fileErrors as $fileError) {
$errors[] = $fileError;
}
- } catch (\Throwable $t) {
+ } catch (Throwable $t) {
$this->errorCount++;
$internalErrorsCount++;
$internalErrorMessage = sprintf('Internal error: %s in file %s', $t->getMessage(), $file);
- $internalErrorMessage .= sprintf(
- '%sRun PHPStan with --debug option and post the stack trace to:%s%s',
- "\n",
- "\n",
- 'https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md'
- );
+
+ $bugReportUrl = 'https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md';
+ if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
+ $internalErrorMessage .= sprintf('%sPost the following stack trace to %s: %s%s', "\n\n", $bugReportUrl, "\n", $t->getTraceAsString());
+ } else {
+ $internalErrorMessage .= sprintf('%sRun PHPStan with -v option and post the stack trace to:%s%s', "\n", "\n", $bugReportUrl);
+ }
+
$errors[] = $internalErrorMessage;
}
}
@@ -244,14 +245,12 @@ private function runWorker(
/**
* @param string[] $analysedFiles
- * @param string|null $insteadOfFile
- * @param string|null $tmpFile
* @return string[]
*/
private function switchTmpFile(
array $analysedFiles,
?string $insteadOfFile,
- ?string $tmpFile
+ ?string $tmpFile,
): array
{
$analysedFiles = array_values(array_filter($analysedFiles, static function (string $file) use ($insteadOfFile): bool {
diff --git a/src/Dependency/DependencyDumper.php b/src/Dependency/DependencyDumper.php
deleted file mode 100644
index c7480dc01a..0000000000
--- a/src/Dependency/DependencyDumper.php
+++ /dev/null
@@ -1,97 +0,0 @@
-dependencyResolver = $dependencyResolver;
- $this->nodeScopeResolver = $nodeScopeResolver;
- $this->parser = $parser;
- $this->scopeFactory = $scopeFactory;
- $this->fileFinder = $fileFinder;
- }
-
- /**
- * @param string[] $files
- * @param callable(int $count): void $countCallback
- * @param callable(): void $progressCallback
- * @param string[]|null $analysedPaths
- * @return string[][]
- */
- public function dumpDependencies(
- array $files,
- callable $countCallback,
- callable $progressCallback,
- ?array $analysedPaths
- ): array
- {
- $analysedFiles = $files;
- if ($analysedPaths !== null) {
- $analysedFiles = $this->fileFinder->findFiles($analysedPaths)->getFiles();
- }
- $this->nodeScopeResolver->setAnalysedFiles($analysedFiles);
- $analysedFiles = array_fill_keys($analysedFiles, true);
-
- $dependencies = [];
- $countCallback(count($files));
- foreach ($files as $file) {
- try {
- $parserNodes = $this->parser->parseFile($file);
- } catch (\PHPStan\Parser\ParserErrorsException $e) {
- continue;
- }
-
- $fileDependencies = [];
- try {
- $this->nodeScopeResolver->processNodes(
- $parserNodes,
- $this->scopeFactory->create(ScopeContext::create($file)),
- function (\PhpParser\Node $node, Scope $scope) use ($analysedFiles, &$fileDependencies): void {
- $dependencies = $this->dependencyResolver->resolveDependencies($node, $scope);
- $fileDependencies = array_merge(
- $fileDependencies,
- $dependencies->getFileDependencies($scope->getFile(), $analysedFiles)
- );
- }
- );
- } catch (\PHPStan\AnalysedCodeException $e) {
- // pass
- }
-
- foreach (array_unique($fileDependencies) as $fileDependency) {
- $dependencies[$fileDependency][] = $file;
- }
-
- $progressCallback();
- }
-
- return $dependencies;
- }
-
-}
diff --git a/src/Dependency/DependencyResolver.php b/src/Dependency/DependencyResolver.php
index 3580e529cd..8c650dd8b3 100644
--- a/src/Dependency/DependencyResolver.php
+++ b/src/Dependency/DependencyResolver.php
@@ -2,64 +2,65 @@
namespace PHPStan\Dependency;
+use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Foreach_;
use PHPStan\Analyser\Scope;
+use PHPStan\Broker\ClassNotFoundException;
+use PHPStan\Broker\FunctionNotFoundException;
use PHPStan\File\FileHelper;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Node\InFunctionNode;
+use PHPStan\Reflection\ClassReflection;
+use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\ParametersAcceptorWithPhpDocs;
use PHPStan\Reflection\ReflectionProvider;
-use PHPStan\Reflection\ReflectionWithFilename;
use PHPStan\Type\ClosureType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Type;
+use function array_merge;
+use function count;
class DependencyResolver
{
- private FileHelper $fileHelper;
-
- private \PHPStan\Reflection\ReflectionProvider $reflectionProvider;
-
- private ExportedNodeResolver $exportedNodeResolver;
-
public function __construct(
- FileHelper $fileHelper,
- ReflectionProvider $reflectionProvider,
- ExportedNodeResolver $exportedNodeResolver
+ private FileHelper $fileHelper,
+ private ReflectionProvider $reflectionProvider,
+ private ExportedNodeResolver $exportedNodeResolver,
)
{
- $this->fileHelper = $fileHelper;
- $this->reflectionProvider = $reflectionProvider;
- $this->exportedNodeResolver = $exportedNodeResolver;
}
- public function resolveDependencies(\PhpParser\Node $node, Scope $scope): NodeDependencies
+ public function resolveDependencies(Node $node, Scope $scope): NodeDependencies
{
$dependenciesReflections = [];
- if ($node instanceof \PhpParser\Node\Stmt\Class_) {
+ if ($node instanceof Node\Stmt\Class_) {
if ($node->extends !== null) {
$this->addClassToDependencies($node->extends->toString(), $dependenciesReflections);
}
foreach ($node->implements as $className) {
$this->addClassToDependencies($className->toString(), $dependenciesReflections);
}
- } elseif ($node instanceof \PhpParser\Node\Stmt\Interface_) {
+ } elseif ($node instanceof Node\Stmt\Interface_) {
foreach ($node->extends as $className) {
$this->addClassToDependencies($className->toString(), $dependenciesReflections);
}
+ } elseif ($node instanceof Node\Stmt\Enum_) {
+ foreach ($node->implements as $className) {
+ $this->addClassToDependencies($className->toString(), $dependenciesReflections);
+ }
} elseif ($node instanceof InClassMethodNode) {
$nativeMethod = $scope->getFunction();
if ($nativeMethod !== null) {
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($nativeMethod->getVariants());
$this->extractThrowType($nativeMethod->getThrowType(), $dependenciesReflections);
- if ($parametersAcceptor instanceof \PHPStan\Reflection\ParametersAcceptorWithPhpDocs) {
+ if ($parametersAcceptor instanceof ParametersAcceptorWithPhpDocs) {
$this->extractFromParametersAcceptor($parametersAcceptor, $dependenciesReflections);
}
}
@@ -87,12 +88,12 @@ public function resolveDependencies(\PhpParser\Node $node, Scope $scope): NodeDe
foreach ($returnTypeReferencedClasses as $referencedClass) {
$this->addClassToDependencies($referencedClass, $dependenciesReflections);
}
- } elseif ($node instanceof \PhpParser\Node\Expr\FuncCall) {
+ } elseif ($node instanceof Node\Expr\FuncCall) {
$functionName = $node->name;
- if ($functionName instanceof \PhpParser\Node\Name) {
+ if ($functionName instanceof Node\Name) {
try {
$dependenciesReflections[] = $this->getFunctionReflection($functionName, $scope);
- } catch (\PHPStan\Broker\FunctionNotFoundException $e) {
+ } catch (FunctionNotFoundException) {
// pass
}
} else {
@@ -112,7 +113,7 @@ public function resolveDependencies(\PhpParser\Node $node, Scope $scope): NodeDe
foreach ($returnType->getReferencedClasses() as $referencedClass) {
$this->addClassToDependencies($referencedClass, $dependenciesReflections);
}
- } elseif ($node instanceof \PhpParser\Node\Expr\MethodCall || $node instanceof \PhpParser\Node\Expr\PropertyFetch) {
+ } elseif ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) {
$classNames = $scope->getType($node->var)->getReferencedClasses();
foreach ($classNames as $className) {
$this->addClassToDependencies($className, $dependenciesReflections);
@@ -123,11 +124,11 @@ public function resolveDependencies(\PhpParser\Node $node, Scope $scope): NodeDe
$this->addClassToDependencies($referencedClass, $dependenciesReflections);
}
} elseif (
- $node instanceof \PhpParser\Node\Expr\StaticCall
- || $node instanceof \PhpParser\Node\Expr\ClassConstFetch
- || $node instanceof \PhpParser\Node\Expr\StaticPropertyFetch
+ $node instanceof Node\Expr\StaticCall
+ || $node instanceof Node\Expr\ClassConstFetch
+ || $node instanceof Node\Expr\StaticPropertyFetch
) {
- if ($node->class instanceof \PhpParser\Node\Name) {
+ if ($node->class instanceof Node\Name) {
$this->addClassToDependencies($scope->resolveName($node->class), $dependenciesReflections);
} else {
foreach ($scope->getType($node->class)->getReferencedClasses() as $referencedClass) {
@@ -140,19 +141,19 @@ public function resolveDependencies(\PhpParser\Node $node, Scope $scope): NodeDe
$this->addClassToDependencies($referencedClass, $dependenciesReflections);
}
} elseif (
- $node instanceof \PhpParser\Node\Expr\New_
- && $node->class instanceof \PhpParser\Node\Name
+ $node instanceof Node\Expr\New_
+ && $node->class instanceof Node\Name
) {
$this->addClassToDependencies($scope->resolveName($node->class), $dependenciesReflections);
- } elseif ($node instanceof \PhpParser\Node\Stmt\TraitUse) {
+ } elseif ($node instanceof Node\Stmt\TraitUse) {
foreach ($node->traits as $traitName) {
$this->addClassToDependencies($traitName->toString(), $dependenciesReflections);
}
- } elseif ($node instanceof \PhpParser\Node\Expr\Instanceof_) {
+ } elseif ($node instanceof Node\Expr\Instanceof_) {
if ($node->class instanceof Name) {
$this->addClassToDependencies($scope->resolveName($node->class), $dependenciesReflections);
}
- } elseif ($node instanceof \PhpParser\Node\Stmt\Catch_) {
+ } elseif ($node instanceof Node\Stmt\Catch_) {
foreach ($node->types as $type) {
$this->addClassToDependencies($scope->resolveName($type), $dependenciesReflections);
}
@@ -194,27 +195,31 @@ public function resolveDependencies(\PhpParser\Node $node, Scope $scope): NodeDe
private function considerArrayForCallableTest(Scope $scope, Array_ $arrayNode): bool
{
- if (!isset($arrayNode->items[0])) {
+ $items = $arrayNode->items;
+ if (count($items) !== 2) {
return false;
}
- $itemType = $scope->getType($arrayNode->items[0]->value);
+ if ($items[0] === null) {
+ return false;
+ }
+
+ $itemType = $scope->getType($items[0]->value);
if (!$itemType instanceof ConstantStringType) {
- return true;
+ return false;
}
return $itemType->isClassString();
}
/**
- * @param string $className
- * @param array $dependenciesReflections
+ * @param array $dependenciesReflections
*/
private function addClassToDependencies(string $className, array &$dependenciesReflections): void
{
try {
$classReflection = $this->reflectionProvider->getClass($className);
- } catch (\PHPStan\Broker\ClassNotFoundException $e) {
+ } catch (ClassNotFoundException) {
return;
}
@@ -230,32 +235,26 @@ private function addClassToDependencies(string $className, array &$dependenciesR
}
$classReflection = $classReflection->getParentClass();
- } while ($classReflection !== false);
+ } while ($classReflection !== null);
}
- private function getFunctionReflection(\PhpParser\Node\Name $nameNode, ?Scope $scope): ReflectionWithFilename
+ private function getFunctionReflection(Node\Name $nameNode, ?Scope $scope): FunctionReflection
{
- $reflection = $this->reflectionProvider->getFunction($nameNode, $scope);
- if (!$reflection instanceof ReflectionWithFilename) {
- throw new \PHPStan\Broker\FunctionNotFoundException((string) $nameNode);
- }
-
- return $reflection;
+ return $this->reflectionProvider->getFunction($nameNode, $scope);
}
/**
- * @param ParametersAcceptorWithPhpDocs $parametersAcceptor
- * @param ReflectionWithFilename[] $dependenciesReflections
+ * @param array $dependenciesReflections
*/
private function extractFromParametersAcceptor(
ParametersAcceptorWithPhpDocs $parametersAcceptor,
- array &$dependenciesReflections
+ array &$dependenciesReflections,
): void
{
foreach ($parametersAcceptor->getParameters() as $parameter) {
$referencedClasses = array_merge(
$parameter->getNativeType()->getReferencedClasses(),
- $parameter->getPhpDocType()->getReferencedClasses()
+ $parameter->getPhpDocType()->getReferencedClasses(),
);
foreach ($referencedClasses as $referencedClass) {
@@ -265,7 +264,7 @@ private function extractFromParametersAcceptor(
$returnTypeReferencedClasses = array_merge(
$parametersAcceptor->getNativeReturnType()->getReferencedClasses(),
- $parametersAcceptor->getPhpDocReturnType()->getReferencedClasses()
+ $parametersAcceptor->getPhpDocReturnType()->getReferencedClasses(),
);
foreach ($returnTypeReferencedClasses as $referencedClass) {
$this->addClassToDependencies($referencedClass, $dependenciesReflections);
@@ -273,12 +272,11 @@ private function extractFromParametersAcceptor(
}
/**
- * @param Type|null $throwType
- * @param ReflectionWithFilename[] $dependenciesReflections
+ * @param array $dependenciesReflections
*/
private function extractThrowType(
?Type $throwType,
- array &$dependenciesReflections
+ array &$dependenciesReflections,
): void
{
if ($throwType === null) {
diff --git a/src/Dependency/ExportedNode.php b/src/Dependency/ExportedNode.php
index ee088cd05b..59c32f2a0c 100644
--- a/src/Dependency/ExportedNode.php
+++ b/src/Dependency/ExportedNode.php
@@ -9,13 +9,11 @@ public function equals(self $node): bool;
/**
* @param mixed[] $properties
- * @return self
*/
public static function __set_state(array $properties): self;
/**
* @param mixed[] $data
- * @return self
*/
public static function decode(array $data): self;
diff --git a/src/Dependency/ExportedNode/ExportedClassConstantNode.php b/src/Dependency/ExportedNode/ExportedClassConstantNode.php
index 4e75697de7..c24510b3cc 100644
--- a/src/Dependency/ExportedNode/ExportedClassConstantNode.php
+++ b/src/Dependency/ExportedNode/ExportedClassConstantNode.php
@@ -4,24 +4,13 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use ReturnTypeWillChange;
class ExportedClassConstantNode implements ExportedNode, JsonSerializable
{
- private string $name;
-
- private string $value;
-
- private bool $public;
-
- private bool $private;
-
- public function __construct(string $name, string $value, bool $public, bool $private)
+ public function __construct(private string $name, private string $value)
{
- $this->name = $name;
- $this->value = $value;
- $this->public = $public;
- $this->private = $private;
}
public function equals(ExportedNode $node): bool
@@ -31,9 +20,7 @@ public function equals(ExportedNode $node): bool
}
return $this->name === $node->name
- && $this->value === $node->value
- && $this->public === $node->public
- && $this->private === $node->private;
+ && $this->value === $node->value;
}
/**
@@ -45,8 +32,6 @@ public static function __set_state(array $properties): ExportedNode
return new self(
$properties['name'],
$properties['value'],
- $properties['public'],
- $properties['private']
);
}
@@ -59,14 +44,13 @@ public static function decode(array $data): ExportedNode
return new self(
$data['name'],
$data['value'],
- $data['public'],
- $data['private']
);
}
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
@@ -74,8 +58,6 @@ public function jsonSerialize()
'data' => [
'name' => $this->name,
'value' => $this->value,
- 'public' => $this->public,
- 'private' => $this->private,
],
];
}
diff --git a/src/Dependency/ExportedNode/ExportedClassConstantsNode.php b/src/Dependency/ExportedNode/ExportedClassConstantsNode.php
new file mode 100644
index 0000000000..04bace7282
--- /dev/null
+++ b/src/Dependency/ExportedNode/ExportedClassConstantsNode.php
@@ -0,0 +1,108 @@
+phpDoc === null) {
+ if ($node->phpDoc !== null) {
+ return false;
+ }
+ } elseif ($node->phpDoc !== null) {
+ if (!$this->phpDoc->equals($node->phpDoc)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ if (count($this->constants) !== count($node->constants)) {
+ return false;
+ }
+
+ foreach ($this->constants as $i => $constant) {
+ if (!$constant->equals($node->constants[$i])) {
+ return false;
+ }
+ }
+
+ return $this->public === $node->public
+ && $this->private === $node->private
+ && $this->final === $node->final;
+ }
+
+ /**
+ * @param mixed[] $properties
+ * @return self
+ */
+ public static function __set_state(array $properties): ExportedNode
+ {
+ return new self(
+ $properties['constants'],
+ $properties['public'],
+ $properties['private'],
+ $properties['final'],
+ $properties['phpDoc'],
+ );
+ }
+
+ /**
+ * @param mixed[] $data
+ * @return self
+ */
+ public static function decode(array $data): ExportedNode
+ {
+ return new self(
+ array_map(static function (array $constantData): ExportedClassConstantNode {
+ if ($constantData['type'] !== ExportedClassConstantNode::class) {
+ throw new ShouldNotHappenException();
+ }
+ return ExportedClassConstantNode::decode($constantData['data']);
+ }, $data['constants']),
+ $data['public'],
+ $data['private'],
+ $data['final'],
+ $data['phpDoc'] !== null ? ExportedPhpDocNode::decode($data['phpDoc']['data']) : null,
+ );
+ }
+
+ /**
+ * @return mixed
+ */
+ #[ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return [
+ 'type' => self::class,
+ 'data' => [
+ 'constants' => $this->constants,
+ 'public' => $this->public,
+ 'private' => $this->private,
+ 'final' => $this->final,
+ 'phpDoc' => $this->phpDoc,
+ ],
+ ];
+ }
+
+}
diff --git a/src/Dependency/ExportedNode/ExportedClassNode.php b/src/Dependency/ExportedNode/ExportedClassNode.php
index 4977a47643..c46001f515 100644
--- a/src/Dependency/ExportedNode/ExportedClassNode.php
+++ b/src/Dependency/ExportedNode/ExportedClassNode.php
@@ -4,58 +4,32 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use PHPStan\ShouldNotHappenException;
+use ReturnTypeWillChange;
+use function array_map;
+use function count;
class ExportedClassNode implements ExportedNode, JsonSerializable
{
- private string $name;
-
- private ?ExportedPhpDocNode $phpDoc;
-
- private bool $abstract;
-
- private bool $final;
-
- private ?string $extends;
-
- /** @var string[] */
- private array $implements;
-
- /** @var string[] */
- private array $usedTraits;
-
- /** @var ExportedTraitUseAdaptation[] */
- private array $traitUseAdaptations;
-
/**
- * @param string $name
- * @param ExportedPhpDocNode|null $phpDoc
- * @param bool $abstract
- * @param bool $final
- * @param string|null $extends
* @param string[] $implements
* @param string[] $usedTraits
* @param ExportedTraitUseAdaptation[] $traitUseAdaptations
+ * @param ExportedNode[] $statements
*/
public function __construct(
- string $name,
- ?ExportedPhpDocNode $phpDoc,
- bool $abstract,
- bool $final,
- ?string $extends,
- array $implements,
- array $usedTraits,
- array $traitUseAdaptations
+ private string $name,
+ private ?ExportedPhpDocNode $phpDoc,
+ private bool $abstract,
+ private bool $final,
+ private ?string $extends,
+ private array $implements,
+ private array $usedTraits,
+ private array $traitUseAdaptations,
+ private array $statements,
)
{
- $this->name = $name;
- $this->phpDoc = $phpDoc;
- $this->abstract = $abstract;
- $this->final = $final;
- $this->extends = $extends;
- $this->implements = $implements;
- $this->usedTraits = $usedTraits;
- $this->traitUseAdaptations = $traitUseAdaptations;
}
public function equals(ExportedNode $node): bool
@@ -87,6 +61,18 @@ public function equals(ExportedNode $node): bool
}
}
+ if (count($this->statements) !== count($node->statements)) {
+ return false;
+ }
+
+ foreach ($this->statements as $i => $statement) {
+ if ($statement->equals($node->statements[$i])) {
+ continue;
+ }
+
+ return false;
+ }
+
return $this->name === $node->name
&& $this->abstract === $node->abstract
&& $this->final === $node->final
@@ -109,13 +95,15 @@ public static function __set_state(array $properties): ExportedNode
$properties['extends'],
$properties['implements'],
$properties['usedTraits'],
- $properties['traitUseAdaptations']
+ $properties['traitUseAdaptations'],
+ $properties['statements'],
);
}
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
@@ -129,6 +117,7 @@ public function jsonSerialize()
'implements' => $this->implements,
'usedTraits' => $this->usedTraits,
'traitUseAdaptations' => $this->traitUseAdaptations,
+ 'statements' => $this->statements,
],
];
}
@@ -149,10 +138,15 @@ public static function decode(array $data): ExportedNode
$data['usedTraits'],
array_map(static function (array $traitUseAdaptationData): ExportedTraitUseAdaptation {
if ($traitUseAdaptationData['type'] !== ExportedTraitUseAdaptation::class) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return ExportedTraitUseAdaptation::decode($traitUseAdaptationData['data']);
- }, $data['traitUseAdaptations'])
+ }, $data['traitUseAdaptations']),
+ array_map(static function (array $node): ExportedNode {
+ $nodeType = $node['type'];
+
+ return $nodeType::decode($node['data']);
+ }, $data['statements']),
);
}
diff --git a/src/Dependency/ExportedNode/ExportedEnumCaseNode.php b/src/Dependency/ExportedNode/ExportedEnumCaseNode.php
new file mode 100644
index 0000000000..da304b5893
--- /dev/null
+++ b/src/Dependency/ExportedNode/ExportedEnumCaseNode.php
@@ -0,0 +1,80 @@
+phpDoc === null) {
+ if ($node->phpDoc !== null) {
+ return false;
+ }
+ } elseif ($node->phpDoc !== null) {
+ if (!$this->phpDoc->equals($node->phpDoc)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return $this->name === $node->name
+ && $this->value === $node->value;
+ }
+
+ /**
+ * @param mixed[] $properties
+ * @return self
+ */
+ public static function __set_state(array $properties): ExportedNode
+ {
+ return new self(
+ $properties['name'],
+ $properties['value'],
+ $properties['phpDoc'],
+ );
+ }
+
+ /**
+ * @param mixed[] $data
+ * @return self
+ */
+ public static function decode(array $data): ExportedNode
+ {
+ return new self(
+ $data['name'],
+ $data['value'],
+ $data['phpDoc'] !== null ? ExportedPhpDocNode::decode($data['phpDoc']['data']) : null,
+ );
+ }
+
+ /**
+ * @return mixed
+ */
+ #[ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return [
+ 'type' => self::class,
+ 'data' => [
+ 'name' => $this->name,
+ 'value' => $this->value,
+ 'phpDoc' => $this->phpDoc,
+ ],
+ ];
+ }
+
+}
diff --git a/src/Dependency/ExportedNode/ExportedEnumNode.php b/src/Dependency/ExportedNode/ExportedEnumNode.php
new file mode 100644
index 0000000000..831a823658
--- /dev/null
+++ b/src/Dependency/ExportedNode/ExportedEnumNode.php
@@ -0,0 +1,109 @@
+phpDoc === null) {
+ if ($node->phpDoc !== null) {
+ return false;
+ }
+ } elseif ($node->phpDoc !== null) {
+ if (!$this->phpDoc->equals($node->phpDoc)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ if (count($this->statements) !== count($node->statements)) {
+ return false;
+ }
+
+ foreach ($this->statements as $i => $statement) {
+ if ($statement->equals($node->statements[$i])) {
+ continue;
+ }
+
+ return false;
+ }
+
+ return $this->name === $node->name
+ && $this->scalarType === $node->scalarType
+ && $this->implements === $node->implements;
+ }
+
+ /**
+ * @param mixed[] $properties
+ * @return self
+ */
+ public static function __set_state(array $properties): ExportedNode
+ {
+ return new self(
+ $properties['name'],
+ $properties['scalarType'],
+ $properties['phpDoc'],
+ $properties['implements'],
+ $properties['statements'],
+ );
+ }
+
+ /**
+ * @return mixed
+ */
+ #[ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return [
+ 'type' => self::class,
+ 'data' => [
+ 'name' => $this->name,
+ 'scalarType' => $this->scalarType,
+ 'phpDoc' => $this->phpDoc,
+ 'implements' => $this->implements,
+ 'statements' => $this->statements,
+ ],
+ ];
+ }
+
+ /**
+ * @param mixed[] $data
+ * @return self
+ */
+ public static function decode(array $data): ExportedNode
+ {
+ return new self(
+ $data['name'],
+ $data['scalarType'],
+ $data['phpDoc'] !== null ? ExportedPhpDocNode::decode($data['phpDoc']['data']) : null,
+ $data['implements'],
+ array_map(static function (array $node): ExportedNode {
+ $nodeType = $node['type'];
+
+ return $nodeType::decode($node['data']);
+ }, $data['statements']),
+ );
+ }
+
+}
diff --git a/src/Dependency/ExportedNode/ExportedFunctionNode.php b/src/Dependency/ExportedNode/ExportedFunctionNode.php
index 6c4382b1b0..58f2704c79 100644
--- a/src/Dependency/ExportedNode/ExportedFunctionNode.php
+++ b/src/Dependency/ExportedNode/ExportedFunctionNode.php
@@ -4,41 +4,25 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use PHPStan\ShouldNotHappenException;
+use ReturnTypeWillChange;
+use function array_map;
+use function count;
class ExportedFunctionNode implements ExportedNode, JsonSerializable
{
- private string $name;
-
- private ?ExportedPhpDocNode $phpDoc;
-
- private bool $byRef;
-
- private ?string $returnType;
-
- /** @var ExportedParameterNode[] */
- private array $parameters;
-
/**
- * @param string $name
- * @param ExportedPhpDocNode|null $phpDoc
- * @param bool $byRef
- * @param string|null $returnType
* @param ExportedParameterNode[] $parameters
*/
public function __construct(
- string $name,
- ?ExportedPhpDocNode $phpDoc,
- bool $byRef,
- ?string $returnType,
- array $parameters
+ private string $name,
+ private ?ExportedPhpDocNode $phpDoc,
+ private bool $byRef,
+ private ?string $returnType,
+ private array $parameters,
)
{
- $this->name = $name;
- $this->phpDoc = $phpDoc;
- $this->byRef = $byRef;
- $this->returnType = $returnType;
- $this->parameters = $parameters;
}
public function equals(ExportedNode $node): bool
@@ -86,13 +70,14 @@ public static function __set_state(array $properties): ExportedNode
$properties['phpDoc'],
$properties['byRef'],
$properties['returnType'],
- $properties['parameters']
+ $properties['parameters'],
);
}
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
@@ -120,10 +105,10 @@ public static function decode(array $data): ExportedNode
$data['returnType'],
array_map(static function (array $parameterData): ExportedParameterNode {
if ($parameterData['type'] !== ExportedParameterNode::class) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return ExportedParameterNode::decode($parameterData['data']);
- }, $data['parameters'])
+ }, $data['parameters']),
);
}
diff --git a/src/Dependency/ExportedNode/ExportedInterfaceNode.php b/src/Dependency/ExportedNode/ExportedInterfaceNode.php
index 0519efba99..285db67f87 100644
--- a/src/Dependency/ExportedNode/ExportedInterfaceNode.php
+++ b/src/Dependency/ExportedNode/ExportedInterfaceNode.php
@@ -4,27 +4,19 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use ReturnTypeWillChange;
+use function array_map;
+use function count;
class ExportedInterfaceNode implements ExportedNode, JsonSerializable
{
- private string $name;
-
- private ?ExportedPhpDocNode $phpDoc;
-
- /** @var string[] */
- private array $extends;
-
/**
- * @param string $name
- * @param ExportedPhpDocNode|null $phpDoc
* @param string[] $extends
+ * @param ExportedNode[] $statements
*/
- public function __construct(string $name, ?ExportedPhpDocNode $phpDoc, array $extends)
+ public function __construct(private string $name, private ?ExportedPhpDocNode $phpDoc, private array $extends, private array $statements)
{
- $this->name = $name;
- $this->phpDoc = $phpDoc;
- $this->extends = $extends;
}
public function equals(ExportedNode $node): bool
@@ -45,6 +37,18 @@ public function equals(ExportedNode $node): bool
return false;
}
+ if (count($this->statements) !== count($node->statements)) {
+ return false;
+ }
+
+ foreach ($this->statements as $i => $statement) {
+ if ($statement->equals($node->statements[$i])) {
+ continue;
+ }
+
+ return false;
+ }
+
return $this->name === $node->name
&& $this->extends === $node->extends;
}
@@ -58,13 +62,15 @@ public static function __set_state(array $properties): ExportedNode
return new self(
$properties['name'],
$properties['phpDoc'],
- $properties['extends']
+ $properties['extends'],
+ $properties['statements'],
);
}
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
@@ -73,6 +79,7 @@ public function jsonSerialize()
'name' => $this->name,
'phpDoc' => $this->phpDoc,
'extends' => $this->extends,
+ 'statements' => $this->statements,
],
];
}
@@ -86,7 +93,12 @@ public static function decode(array $data): ExportedNode
return new self(
$data['name'],
$data['phpDoc'] !== null ? ExportedPhpDocNode::decode($data['phpDoc']['data']) : null,
- $data['extends']
+ $data['extends'],
+ array_map(static function (array $node): ExportedNode {
+ $nodeType = $node['type'];
+
+ return $nodeType::decode($node['data']);
+ }, $data['statements']),
);
}
diff --git a/src/Dependency/ExportedNode/ExportedMethodNode.php b/src/Dependency/ExportedNode/ExportedMethodNode.php
index a59a40c25a..7cb3376fab 100644
--- a/src/Dependency/ExportedNode/ExportedMethodNode.php
+++ b/src/Dependency/ExportedNode/ExportedMethodNode.php
@@ -4,66 +4,30 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use PHPStan\ShouldNotHappenException;
+use ReturnTypeWillChange;
+use function array_map;
+use function count;
class ExportedMethodNode implements ExportedNode, JsonSerializable
{
- private string $name;
-
- private ?ExportedPhpDocNode $phpDoc;
-
- private bool $byRef;
-
- private bool $public;
-
- private bool $private;
-
- private bool $abstract;
-
- private bool $final;
-
- private bool $static;
-
- private ?string $returnType;
-
- /** @var ExportedParameterNode[] */
- private array $parameters;
-
/**
- * @param string $name
- * @param ExportedPhpDocNode|null $phpDoc
- * @param bool $byRef
- * @param bool $public
- * @param bool $private
- * @param bool $abstract
- * @param bool $final
- * @param bool $static
- * @param string|null $returnType
* @param ExportedParameterNode[] $parameters
*/
public function __construct(
- string $name,
- ?ExportedPhpDocNode $phpDoc,
- bool $byRef,
- bool $public,
- bool $private,
- bool $abstract,
- bool $final,
- bool $static,
- ?string $returnType,
- array $parameters
+ private string $name,
+ private ?ExportedPhpDocNode $phpDoc,
+ private bool $byRef,
+ private bool $public,
+ private bool $private,
+ private bool $abstract,
+ private bool $final,
+ private bool $static,
+ private ?string $returnType,
+ private array $parameters,
)
{
- $this->name = $name;
- $this->phpDoc = $phpDoc;
- $this->byRef = $byRef;
- $this->public = $public;
- $this->private = $private;
- $this->abstract = $abstract;
- $this->final = $final;
- $this->static = $static;
- $this->returnType = $returnType;
- $this->parameters = $parameters;
}
public function equals(ExportedNode $node): bool
@@ -121,13 +85,14 @@ public static function __set_state(array $properties): ExportedNode
$properties['final'],
$properties['static'],
$properties['returnType'],
- $properties['parameters']
+ $properties['parameters'],
);
}
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
@@ -165,10 +130,10 @@ public static function decode(array $data): ExportedNode
$data['returnType'],
array_map(static function (array $parameterData): ExportedParameterNode {
if ($parameterData['type'] !== ExportedParameterNode::class) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return ExportedParameterNode::decode($parameterData['data']);
- }, $data['parameters'])
+ }, $data['parameters']),
);
}
diff --git a/src/Dependency/ExportedNode/ExportedParameterNode.php b/src/Dependency/ExportedNode/ExportedParameterNode.php
index 5f171a442f..ad36f20c42 100644
--- a/src/Dependency/ExportedNode/ExportedParameterNode.php
+++ b/src/Dependency/ExportedNode/ExportedParameterNode.php
@@ -4,33 +4,19 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use ReturnTypeWillChange;
class ExportedParameterNode implements ExportedNode, JsonSerializable
{
- private string $name;
-
- private ?string $type;
-
- private bool $byRef;
-
- private bool $variadic;
-
- private bool $hasDefault;
-
public function __construct(
- string $name,
- ?string $type,
- bool $byRef,
- bool $variadic,
- bool $hasDefault
+ private string $name,
+ private ?string $type,
+ private bool $byRef,
+ private bool $variadic,
+ private bool $hasDefault,
)
{
- $this->name = $name;
- $this->type = $type;
- $this->byRef = $byRef;
- $this->variadic = $variadic;
- $this->hasDefault = $hasDefault;
}
public function equals(ExportedNode $node): bool
@@ -57,13 +43,14 @@ public static function __set_state(array $properties): ExportedNode
$properties['type'],
$properties['byRef'],
$properties['variadic'],
- $properties['hasDefault']
+ $properties['hasDefault'],
);
}
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
@@ -89,7 +76,7 @@ public static function decode(array $data): ExportedNode
$data['type'],
$data['byRef'],
$data['variadic'],
- $data['hasDefault']
+ $data['hasDefault'],
);
}
diff --git a/src/Dependency/ExportedNode/ExportedPhpDocNode.php b/src/Dependency/ExportedNode/ExportedPhpDocNode.php
index e271b6b752..fbb3d3bd0d 100644
--- a/src/Dependency/ExportedNode/ExportedPhpDocNode.php
+++ b/src/Dependency/ExportedNode/ExportedPhpDocNode.php
@@ -4,26 +4,19 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use ReturnTypeWillChange;
class ExportedPhpDocNode implements ExportedNode, JsonSerializable
{
- private string $phpDocString;
-
- private ?string $namespace;
-
/** @var array alias(string) => fullName(string) */
private array $uses;
/**
- * @param string $phpDocString
- * @param string|null $namespace
* @param array $uses
*/
- public function __construct(string $phpDocString, ?string $namespace, array $uses)
+ public function __construct(private string $phpDocString, private ?string $namespace, array $uses)
{
- $this->phpDocString = $phpDocString;
- $this->namespace = $namespace;
$this->uses = $uses;
}
@@ -41,6 +34,7 @@ public function equals(ExportedNode $node): bool
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
diff --git a/src/Dependency/ExportedNode/ExportedPropertyNode.php b/src/Dependency/ExportedNode/ExportedPropertiesNode.php
similarity index 62%
rename from src/Dependency/ExportedNode/ExportedPropertyNode.php
rename to src/Dependency/ExportedNode/ExportedPropertiesNode.php
index 37aee5a044..2b61556824 100644
--- a/src/Dependency/ExportedNode/ExportedPropertyNode.php
+++ b/src/Dependency/ExportedNode/ExportedPropertiesNode.php
@@ -4,37 +4,25 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use ReturnTypeWillChange;
+use function count;
-class ExportedPropertyNode implements JsonSerializable, ExportedNode
+class ExportedPropertiesNode implements JsonSerializable, ExportedNode
{
- private string $name;
-
- private ?ExportedPhpDocNode $phpDoc;
-
- private ?string $type;
-
- private bool $public;
-
- private bool $private;
-
- private bool $static;
-
+ /**
+ * @param string[] $names
+ */
public function __construct(
- string $name,
- ?ExportedPhpDocNode $phpDoc,
- ?string $type,
- bool $public,
- bool $private,
- bool $static
+ private array $names,
+ private ?ExportedPhpDocNode $phpDoc,
+ private ?string $type,
+ private bool $public,
+ private bool $private,
+ private bool $static,
+ private bool $readonly,
)
{
- $this->name = $name;
- $this->phpDoc = $phpDoc;
- $this->type = $type;
- $this->public = $public;
- $this->private = $private;
- $this->static = $static;
}
public function equals(ExportedNode $node): bool
@@ -55,11 +43,21 @@ public function equals(ExportedNode $node): bool
return false;
}
- return $this->name === $node->name
- && $this->type === $node->type
+ if (count($this->names) !== count($node->names)) {
+ return false;
+ }
+
+ foreach ($this->names as $i => $name) {
+ if ($name !== $node->names[$i]) {
+ return false;
+ }
+ }
+
+ return $this->type === $node->type
&& $this->public === $node->public
&& $this->private === $node->private
- && $this->static === $node->static;
+ && $this->static === $node->static
+ && $this->readonly === $node->readonly;
}
/**
@@ -69,12 +67,13 @@ public function equals(ExportedNode $node): bool
public static function __set_state(array $properties): ExportedNode
{
return new self(
- $properties['name'],
+ $properties['names'],
$properties['phpDoc'],
$properties['type'],
$properties['public'],
$properties['private'],
- $properties['static']
+ $properties['static'],
+ $properties['readonly'],
);
}
@@ -85,29 +84,32 @@ public static function __set_state(array $properties): ExportedNode
public static function decode(array $data): ExportedNode
{
return new self(
- $data['name'],
+ $data['names'],
$data['phpDoc'] !== null ? ExportedPhpDocNode::decode($data['phpDoc']['data']) : null,
$data['type'],
$data['public'],
$data['private'],
- $data['static']
+ $data['static'],
+ $data['readonly'],
);
}
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
'type' => self::class,
'data' => [
- 'name' => $this->name,
+ 'names' => $this->names,
'phpDoc' => $this->phpDoc,
'type' => $this->type,
'public' => $this->public,
'private' => $this->private,
'static' => $this->static,
+ 'readonly' => $this->readonly,
],
];
}
diff --git a/src/Dependency/ExportedNode/ExportedTraitNode.php b/src/Dependency/ExportedNode/ExportedTraitNode.php
index ed8a6fa5ef..7874fca68c 100644
--- a/src/Dependency/ExportedNode/ExportedTraitNode.php
+++ b/src/Dependency/ExportedNode/ExportedTraitNode.php
@@ -4,15 +4,13 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use ReturnTypeWillChange;
class ExportedTraitNode implements ExportedNode, JsonSerializable
{
- private string $traitName;
-
- public function __construct(string $traitName)
+ public function __construct(private string $traitName)
{
- $this->traitName = $traitName;
}
public function equals(ExportedNode $node): bool
@@ -41,6 +39,7 @@ public static function decode(array $data): ExportedNode
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
diff --git a/src/Dependency/ExportedNode/ExportedTraitUseAdaptation.php b/src/Dependency/ExportedNode/ExportedTraitUseAdaptation.php
index 1de62fcf0a..57f3491010 100644
--- a/src/Dependency/ExportedNode/ExportedTraitUseAdaptation.php
+++ b/src/Dependency/ExportedNode/ExportedTraitUseAdaptation.php
@@ -4,63 +4,41 @@
use JsonSerializable;
use PHPStan\Dependency\ExportedNode;
+use ReturnTypeWillChange;
class ExportedTraitUseAdaptation implements ExportedNode, JsonSerializable
{
- private ?string $traitName;
-
- private string $method;
-
- private ?int $newModifier;
-
- private ?string $newName;
-
- /** @var string[]|null */
- private ?array $insteadOfs;
-
/**
- * @param string|null $traitName
- * @param string $method
- * @param int|null $newModifier
- * @param string|null $newName
* @param string[]|null $insteadOfs
*/
private function __construct(
- ?string $traitName,
- string $method,
- ?int $newModifier,
- ?string $newName,
- ?array $insteadOfs
+ private ?string $traitName,
+ private string $method,
+ private ?int $newModifier,
+ private ?string $newName,
+ private ?array $insteadOfs,
)
{
- $this->traitName = $traitName;
- $this->method = $method;
- $this->newModifier = $newModifier;
- $this->newName = $newName;
- $this->insteadOfs = $insteadOfs;
}
public static function createAlias(
?string $traitName,
string $method,
?int $newModifier,
- ?string $newName
+ ?string $newName,
): self
{
return new self($traitName, $method, $newModifier, $newName, null);
}
/**
- * @param string|null $traitName
- * @param string $method
* @param string[] $insteadOfs
- * @return self
*/
public static function createPrecedence(
?string $traitName,
string $method,
- array $insteadOfs
+ array $insteadOfs,
): self
{
return new self($traitName, $method, null, null, $insteadOfs);
@@ -90,7 +68,7 @@ public static function __set_state(array $properties): ExportedNode
$properties['method'],
$properties['newModifier'],
$properties['newName'],
- $properties['insteadOfs']
+ $properties['insteadOfs'],
);
}
@@ -105,13 +83,14 @@ public static function decode(array $data): ExportedNode
$data['method'],
$data['newModifier'],
$data['newName'],
- $data['insteadOfs']
+ $data['insteadOfs'],
);
}
/**
* @return mixed
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return [
diff --git a/src/Dependency/ExportedNodeFetcher.php b/src/Dependency/ExportedNodeFetcher.php
index 8a4c6beb79..95d82d5057 100644
--- a/src/Dependency/ExportedNodeFetcher.php
+++ b/src/Dependency/ExportedNodeFetcher.php
@@ -2,27 +2,22 @@
namespace PHPStan\Dependency;
+use PhpParser\Node;
use PhpParser\NodeTraverser;
use PHPStan\Parser\Parser;
+use PHPStan\Parser\ParserErrorsException;
class ExportedNodeFetcher
{
- private Parser $parser;
-
- private ExportedNodeVisitor $visitor;
-
public function __construct(
- Parser $parser,
- ExportedNodeVisitor $visitor
+ private Parser $parser,
+ private ExportedNodeVisitor $visitor,
)
{
- $this->parser = $parser;
- $this->visitor = $visitor;
}
/**
- * @param string $fileName
* @return ExportedNode[]
*/
public function fetchNodes(string $fileName): array
@@ -31,9 +26,9 @@ public function fetchNodes(string $fileName): array
$nodeTraverser->addVisitor($this->visitor);
try {
- /** @var \PhpParser\Node[] $ast */
+ /** @var Node[] $ast */
$ast = $this->parser->parseFile($fileName);
- } catch (\PHPStan\Parser\ParserErrorsException $e) {
+ } catch (ParserErrorsException) {
return [];
}
$this->visitor->reset($fileName);
diff --git a/src/Dependency/ExportedNodeResolver.php b/src/Dependency/ExportedNodeResolver.php
index 0b3981c736..0a2dc26633 100644
--- a/src/Dependency/ExportedNodeResolver.php
+++ b/src/Dependency/ExportedNodeResolver.php
@@ -7,34 +7,34 @@
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
-use PhpParser\Node\Stmt\Property;
use PhpParser\PrettyPrinter\Standard;
use PHPStan\Dependency\ExportedNode\ExportedClassConstantNode;
+use PHPStan\Dependency\ExportedNode\ExportedClassConstantsNode;
use PHPStan\Dependency\ExportedNode\ExportedClassNode;
+use PHPStan\Dependency\ExportedNode\ExportedEnumCaseNode;
+use PHPStan\Dependency\ExportedNode\ExportedEnumNode;
use PHPStan\Dependency\ExportedNode\ExportedFunctionNode;
use PHPStan\Dependency\ExportedNode\ExportedInterfaceNode;
use PHPStan\Dependency\ExportedNode\ExportedMethodNode;
use PHPStan\Dependency\ExportedNode\ExportedParameterNode;
use PHPStan\Dependency\ExportedNode\ExportedPhpDocNode;
-use PHPStan\Dependency\ExportedNode\ExportedPropertyNode;
+use PHPStan\Dependency\ExportedNode\ExportedPropertiesNode;
use PHPStan\Dependency\ExportedNode\ExportedTraitNode;
use PHPStan\Dependency\ExportedNode\ExportedTraitUseAdaptation;
+use PHPStan\ShouldNotHappenException;
use PHPStan\Type\FileTypeMapper;
+use function array_map;
+use function implode;
+use function is_string;
class ExportedNodeResolver
{
- private FileTypeMapper $fileTypeMapper;
-
- private Standard $printer;
-
- public function __construct(FileTypeMapper $fileTypeMapper, Standard $printer)
+ public function __construct(private FileTypeMapper $fileTypeMapper, private Standard $printer)
{
- $this->fileTypeMapper = $fileTypeMapper;
- $this->printer = $printer;
}
- public function resolve(string $fileName, \PhpParser\Node $node): ?ExportedNode
+ public function resolve(string $fileName, Node $node): ?ExportedNode
{
if ($node instanceof Class_ && isset($node->namespacedName)) {
$docComment = $node->getDocComment();
@@ -67,7 +67,7 @@ public function resolve(string $fileName, \PhpParser\Node $node): ?ExportedNode
$fileName,
$className,
null,
- $docComment !== null ? $docComment->getText() : null
+ $docComment !== null ? $docComment->getText() : null,
),
$node->isAbstract(),
$node->isFinal(),
@@ -80,7 +80,7 @@ public function resolve(string $fileName, \PhpParser\Node $node): ?ExportedNode
$adaptation->trait !== null ? $adaptation->trait->toString() : null,
$adaptation->method->toString(),
$adaptation->newModifier,
- $adaptation->newName !== null ? $adaptation->newName->toString() : null
+ $adaptation->newName !== null ? $adaptation->newName->toString() : null,
);
}
@@ -88,21 +88,18 @@ public function resolve(string $fileName, \PhpParser\Node $node): ?ExportedNode
return ExportedTraitUseAdaptation::createPrecedence(
$adaptation->trait !== null ? $adaptation->trait->toString() : null,
$adaptation->method->toString(),
- array_map(static function (Name $name): string {
- return $name->toString();
- }, $adaptation->insteadof)
+ array_map(static fn (Name $name): string => $name->toString(), $adaptation->insteadof),
);
}
- throw new \PHPStan\ShouldNotHappenException();
- }, $adaptations)
+ throw new ShouldNotHappenException();
+ }, $adaptations),
+ $this->exportClassStatements($node->stmts, $fileName, $className),
);
}
- if ($node instanceof \PhpParser\Node\Stmt\Interface_ && isset($node->namespacedName)) {
- $extendsNames = array_map(static function (Name $name): string {
- return (string) $name;
- }, $node->extends);
+ if ($node instanceof Node\Stmt\Interface_ && isset($node->namespacedName)) {
+ $extendsNames = array_map(static fn (Name $name): string => (string) $name, $node->extends);
$docComment = $node->getDocComment();
$interfaceName = $node->namespacedName->toString();
@@ -113,93 +110,39 @@ public function resolve(string $fileName, \PhpParser\Node $node): ?ExportedNode
$fileName,
$interfaceName,
null,
- $docComment !== null ? $docComment->getText() : null
+ $docComment !== null ? $docComment->getText() : null,
),
- $extendsNames
+ $extendsNames,
+ $this->exportClassStatements($node->stmts, $fileName, $interfaceName),
);
}
- if ($node instanceof Node\Stmt\Trait_ && isset($node->namespacedName)) {
- return new ExportedTraitNode($node->namespacedName->toString());
- }
-
- if ($node instanceof ClassMethod) {
- if ($node->isAbstract() || $node->isFinal() || !$node->isPrivate()) {
- $methodName = $node->name->toString();
- $docComment = $node->getDocComment();
- $parentNode = $node->getAttribute('parent');
- $continue = ($parentNode instanceof Class_ || $parentNode instanceof Node\Stmt\Interface_) && isset($parentNode->namespacedName);
- if (!$continue) {
- return null;
- }
-
- return new ExportedMethodNode(
- $methodName,
- $this->exportPhpDocNode(
- $fileName,
- $parentNode->namespacedName->toString(),
- $methodName,
- $docComment !== null ? $docComment->getText() : null
- ),
- $node->byRef,
- $node->isPublic(),
- $node->isPrivate(),
- $node->isAbstract(),
- $node->isFinal(),
- $node->isStatic(),
- $this->printType($node->returnType),
- $this->exportParameterNodes($node->params)
- );
- }
- }
+ if ($node instanceof Node\Stmt\Enum_ && $node->namespacedName !== null) {
+ $implementsNames = array_map(static fn (Name $name): string => (string) $name, $node->implements);
+ $docComment = $node->getDocComment();
- if ($node instanceof Node\Stmt\PropertyProperty) {
- $parentNode = $node->getAttribute('parent');
- if (!$parentNode instanceof Property) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('Expected node type %s, %s occurred.', Property::class, is_object($parentNode) ? get_class($parentNode) : gettype($parentNode)));
- }
- if ($parentNode->isPrivate()) {
- return null;
+ $enumName = $node->namespacedName->toString();
+ $scalarType = null;
+ if ($node->scalarType !== null) {
+ $scalarType = $node->scalarType->toString();
}
- $classNode = $parentNode->getAttribute('parent');
- if (!$classNode instanceof Class_ || !isset($classNode->namespacedName)) {
- return null;
- }
-
- $docComment = $parentNode->getDocComment();
-
- return new ExportedPropertyNode(
- $node->name->toString(),
+ return new ExportedEnumNode(
+ $enumName,
+ $scalarType,
$this->exportPhpDocNode(
$fileName,
- $classNode->namespacedName->toString(),
+ $enumName,
null,
- $docComment !== null ? $docComment->getText() : null
+ $docComment !== null ? $docComment->getText() : null,
),
- $this->printType($parentNode->type),
- $parentNode->isPublic(),
- $parentNode->isPrivate(),
- $parentNode->isStatic()
+ $implementsNames,
+ $this->exportClassStatements($node->stmts, $fileName, $enumName),
);
}
- if ($node instanceof Node\Const_) {
- $parentNode = $node->getAttribute('parent');
- if (!$parentNode instanceof Node\Stmt\ClassConst) {
- return null;
- }
-
- if ($parentNode->isPrivate()) {
- return null;
- }
-
- return new ExportedClassConstantNode(
- $node->name->toString(),
- $this->printer->prettyPrintExpr($node->value),
- $parentNode->isPublic(),
- $parentNode->isPrivate()
- );
+ if ($node instanceof Node\Stmt\Trait_ && isset($node->namespacedName)) {
+ return new ExportedTraitNode($node->namespacedName->toString());
}
if ($node instanceof Function_) {
@@ -216,11 +159,11 @@ public function resolve(string $fileName, \PhpParser\Node $node): ?ExportedNode
$fileName,
null,
$functionName,
- $docComment !== null ? $docComment->getText() : null
+ $docComment !== null ? $docComment->getText() : null,
),
$node->byRef,
$this->printType($node->returnType),
- $this->exportParameterNodes($node->params)
+ $this->exportParameterNodes($node->params),
);
}
@@ -228,8 +171,7 @@ public function resolve(string $fileName, \PhpParser\Node $node): ?ExportedNode
}
/**
- * @param Node\Identifier|Node\Name|Node\NullableType|Node\UnionType|null $type
- * @return string|null
+ * @param Node\Identifier|Node\Name|Node\ComplexType|null $type
*/
private function printType($type): ?string
{
@@ -245,14 +187,29 @@ private function printType($type): ?string
return implode('|', array_map(function ($innerType): string {
$printedType = $this->printType($innerType);
if ($printedType === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
+ }
+
+ return $printedType;
+ }, $type->types));
+ }
+
+ if ($type instanceof Node\IntersectionType) {
+ return implode('&', array_map(function ($innerType): string {
+ $printedType = $this->printType($innerType);
+ if ($printedType === null) {
+ throw new ShouldNotHappenException();
}
return $printedType;
}, $type->types));
}
- return $type->toString();
+ if ($type instanceof Node\Identifier || $type instanceof Name) {
+ return $type->toString();
+ }
+
+ throw new ShouldNotHappenException();
}
/**
@@ -264,7 +221,7 @@ private function exportParameterNodes(array $params): array
$nodes = [];
foreach ($params as $param) {
if (!$param->var instanceof Node\Expr\Variable || !is_string($param->var->name)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$type = $param->type;
if (
@@ -276,7 +233,7 @@ private function exportParameterNodes(array $params): array
$innerTypes = $type->types;
$innerTypes[] = new Name('null');
$type = new Node\UnionType($innerTypes);
- } elseif (!$type instanceof Node\NullableType) {
+ } elseif ($type instanceof Node\Identifier || $type instanceof Name) {
$type = new Node\NullableType($type);
}
}
@@ -285,7 +242,7 @@ private function exportParameterNodes(array $params): array
$this->printType($type),
$param->byRef,
$param->variadic,
- $param->default !== null
+ $param->default !== null,
);
}
@@ -296,7 +253,7 @@ private function exportPhpDocNode(
string $file,
?string $className,
?string $functionName,
- ?string $text
+ ?string $text,
): ?ExportedPhpDocNode
{
if ($text === null) {
@@ -308,7 +265,7 @@ private function exportPhpDocNode(
$className,
null,
$functionName,
- $text
+ $text,
);
$nameScope = $resolvedPhpDocBlock->getNullableNameScope();
@@ -319,4 +276,120 @@ private function exportPhpDocNode(
return new ExportedPhpDocNode($text, $nameScope->getNamespace(), $nameScope->getUses());
}
+ /**
+ * @param Node\Stmt[] $statements
+ * @return ExportedNode[]
+ */
+ private function exportClassStatements(array $statements, string $fileName, string $namespacedName): array
+ {
+ $exportedNodes = [];
+ foreach ($statements as $statement) {
+ $exportedNode = $this->exportClassStatement($statement, $fileName, $namespacedName);
+ if ($exportedNode === null) {
+ continue;
+ }
+
+ $exportedNodes[] = $exportedNode;
+ }
+
+ return $exportedNodes;
+ }
+
+ private function exportClassStatement(Node\Stmt $node, string $fileName, string $namespacedName): ?ExportedNode
+ {
+ if ($node instanceof ClassMethod) {
+ if ($node->isAbstract() || $node->isFinal() || !$node->isPrivate()) {
+ $methodName = $node->name->toString();
+ $docComment = $node->getDocComment();
+
+ return new ExportedMethodNode(
+ $methodName,
+ $this->exportPhpDocNode(
+ $fileName,
+ $namespacedName,
+ $methodName,
+ $docComment !== null ? $docComment->getText() : null,
+ ),
+ $node->byRef,
+ $node->isPublic(),
+ $node->isPrivate(),
+ $node->isAbstract(),
+ $node->isFinal(),
+ $node->isStatic(),
+ $this->printType($node->returnType),
+ $this->exportParameterNodes($node->params),
+ );
+ }
+ }
+
+ if ($node instanceof Node\Stmt\Property) {
+ if ($node->isPrivate()) {
+ return null;
+ }
+
+ $docComment = $node->getDocComment();
+
+ return new ExportedPropertiesNode(
+ array_map(static fn (Node\Stmt\PropertyProperty $prop): string => $prop->name->toString(), $node->props),
+ $this->exportPhpDocNode(
+ $fileName,
+ $namespacedName,
+ null,
+ $docComment !== null ? $docComment->getText() : null,
+ ),
+ $this->printType($node->type),
+ $node->isPublic(),
+ $node->isPrivate(),
+ $node->isStatic(),
+ $node->isReadonly(),
+ );
+ }
+
+ if ($node instanceof Node\Stmt\ClassConst) {
+ if ($node->isPrivate()) {
+ return null;
+ }
+
+ $docComment = $node->getDocComment();
+
+ $constants = [];
+ foreach ($node->consts as $const) {
+ $constants[] = new ExportedClassConstantNode(
+ $const->name->toString(),
+ $this->printer->prettyPrintExpr($const->value),
+ );
+ }
+
+ return new ExportedClassConstantsNode(
+ $constants,
+ $node->isPublic(),
+ $node->isPrivate(),
+ $node->isFinal(),
+ $this->exportPhpDocNode(
+ $fileName,
+ $namespacedName,
+ null,
+ $docComment !== null ? $docComment->getText() : null,
+ ),
+ );
+ }
+
+ if ($node instanceof Node\Stmt\EnumCase) {
+ $docComment = $node->getDocComment();
+
+ return new ExportedEnumCaseNode(
+ $node->name->toString(),
+ $node->expr !== null ? $this->printer->prettyPrintExpr($node->expr) : null,
+ $this->exportPhpDocNode(
+ $fileName,
+ $namespacedName,
+ null,
+ $docComment !== null ? $docComment->getText() : null,
+ ),
+ );
+ }
+
+ return null;
+ }
+
}
diff --git a/src/Dependency/ExportedNodeVisitor.php b/src/Dependency/ExportedNodeVisitor.php
index bedb18c037..61579fba4d 100644
--- a/src/Dependency/ExportedNodeVisitor.php
+++ b/src/Dependency/ExportedNodeVisitor.php
@@ -5,12 +5,11 @@
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
+use PHPStan\ShouldNotHappenException;
class ExportedNodeVisitor extends NodeVisitorAbstract
{
- private ExportedNodeResolver $exportedNodeResolver;
-
private ?string $fileName = null;
/** @var ExportedNode[] */
@@ -19,11 +18,9 @@ class ExportedNodeVisitor extends NodeVisitorAbstract
/**
* ExportedNodeVisitor constructor.
*
- * @param ExportedNodeResolver $exportedNodeResolver
*/
- public function __construct(ExportedNodeResolver $exportedNodeResolver)
+ public function __construct(private ExportedNodeResolver $exportedNodeResolver)
{
- $this->exportedNodeResolver = $exportedNodeResolver;
}
public function reset(string $fileName): void
@@ -43,7 +40,7 @@ public function getExportedNodes(): array
public function enterNode(Node $node): ?int
{
if ($this->fileName === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$exportedNode = $this->exportedNodeResolver->resolve($this->fileName, $node);
if ($exportedNode !== null) {
diff --git a/src/Dependency/NodeDependencies.php b/src/Dependency/NodeDependencies.php
index b693e5091c..2b35652747 100644
--- a/src/Dependency/NodeDependencies.php
+++ b/src/Dependency/NodeDependencies.php
@@ -2,45 +2,26 @@
namespace PHPStan\Dependency;
-use IteratorAggregate;
use PHPStan\File\FileHelper;
-use PHPStan\Reflection\ReflectionWithFilename;
+use PHPStan\Reflection\ClassReflection;
+use PHPStan\Reflection\FunctionReflection;
+use function array_values;
-/**
- * @implements \IteratorAggregate
- */
-class NodeDependencies implements IteratorAggregate
+class NodeDependencies
{
- private FileHelper $fileHelper;
-
- /** @var array */
- private array $reflections;
-
- private ?ExportedNode $exportedNode;
-
/**
- * @param FileHelper $fileHelper
- * @param array $reflections
+ * @param array $reflections
*/
public function __construct(
- FileHelper $fileHelper,
- array $reflections,
- ?ExportedNode $exportedNode
+ private FileHelper $fileHelper,
+ private array $reflections,
+ private ?ExportedNode $exportedNode,
)
{
- $this->fileHelper = $fileHelper;
- $this->reflections = $reflections;
- $this->exportedNode = $exportedNode;
- }
-
- public function getIterator(): \Traversable
- {
- return new \ArrayIterator($this->reflections);
}
/**
- * @param string $currentFile
* @param array $analysedFiles
* @return string[]
*/
@@ -50,7 +31,7 @@ public function getFileDependencies(string $currentFile, array $analysedFiles):
foreach ($this->reflections as $dependencyReflection) {
$dependencyFile = $dependencyReflection->getFileName();
- if ($dependencyFile === false) {
+ if ($dependencyFile === null) {
continue;
}
$dependencyFile = $this->fileHelper->normalizePath($dependencyFile);
diff --git a/src/DependencyInjection/ConditionalTagsExtension.php b/src/DependencyInjection/ConditionalTagsExtension.php
index 4808fcc85e..bd2e50c706 100644
--- a/src/DependencyInjection/ConditionalTagsExtension.php
+++ b/src/DependencyInjection/ConditionalTagsExtension.php
@@ -3,13 +3,17 @@
namespace PHPStan\DependencyInjection;
use Nette;
+use Nette\DI\CompilerExtension;
use Nette\Schema\Expect;
use PHPStan\Analyser\TypeSpecifierFactory;
use PHPStan\Broker\BrokerFactory;
use PHPStan\PhpDoc\TypeNodeResolverExtension;
use PHPStan\Rules\RegistryFactory;
+use PHPStan\ShouldNotHappenException;
+use function count;
+use function sprintf;
-class ConditionalTagsExtension extends \Nette\DI\CompilerExtension
+class ConditionalTagsExtension extends CompilerExtension
{
public function getConfigSchema(): Nette\Schema\Schema
@@ -39,7 +43,7 @@ public function beforeCompile(): void
foreach ($config as $type => $tags) {
$services = $builder->findByType($type);
if (count($services) === 0) {
- throw new \PHPStan\ShouldNotHappenException(sprintf('No services of type "%s" found.', $type));
+ throw new ShouldNotHappenException(sprintf('No services of type "%s" found.', $type));
}
foreach ($services as $service) {
foreach ($tags as $tag => $parameter) {
diff --git a/src/DependencyInjection/Configurator.php b/src/DependencyInjection/Configurator.php
index 1da912305f..1d3ba2b654 100644
--- a/src/DependencyInjection/Configurator.php
+++ b/src/DependencyInjection/Configurator.php
@@ -4,16 +4,15 @@
use Nette\DI\Config\Loader;
use Nette\DI\ContainerLoader;
+use function array_keys;
+use const PHP_RELEASE_VERSION;
+use const PHP_VERSION_ID;
-class Configurator extends \Nette\Configurator
+class Configurator extends \Nette\Bootstrap\Configurator
{
- private LoaderFactory $loaderFactory;
-
- public function __construct(LoaderFactory $loaderFactory)
+ public function __construct(private LoaderFactory $loaderFactory)
{
- $this->loaderFactory = $loaderFactory;
-
parent::__construct();
}
@@ -39,12 +38,12 @@ public function loadContainer(): string
{
$loader = new ContainerLoader(
$this->getContainerCacheDirectory(),
- $this->parameters['debugMode']
+ $this->staticParameters['debugMode'],
);
return $loader->load(
[$this, 'generateContainer'],
- [$this->parameters, array_keys($this->dynamicParameters), $this->configs, PHP_VERSION_ID - PHP_RELEASE_VERSION, NeonAdapter::CACHE_KEY]
+ [$this->staticParameters, array_keys($this->dynamicParameters), $this->configs, PHP_VERSION_ID - PHP_RELEASE_VERSION, NeonAdapter::CACHE_KEY],
);
}
diff --git a/src/DependencyInjection/Container.php b/src/DependencyInjection/Container.php
index 024b6e4cc9..07a7a574e1 100644
--- a/src/DependencyInjection/Container.php
+++ b/src/DependencyInjection/Container.php
@@ -9,25 +9,25 @@ interface Container
public function hasService(string $serviceName): bool;
/**
- * @param string $serviceName
* @return mixed
*/
public function getService(string $serviceName);
/**
- * @param string $className
+ * @phpstan-template T of object
+ * @phpstan-param class-string $className
+ * @phpstan-return T
* @return mixed
*/
public function getByType(string $className);
/**
- * @param string $className
+ * @param class-string $className
* @return string[]
*/
public function findServiceNamesByType(string $className): array;
/**
- * @param string $tagName
* @return mixed[]
*/
public function getServicesByTag(string $tagName): array;
@@ -40,9 +40,8 @@ public function getParameters(): array;
public function hasParameter(string $parameterName): bool;
/**
- * @param string $parameterName
* @return mixed
- * @throws \PHPStan\DependencyInjection\ParameterNotFoundException
+ * @throws ParameterNotFoundException
*/
public function getParameter(string $parameterName);
diff --git a/src/DependencyInjection/ContainerFactory.php b/src/DependencyInjection/ContainerFactory.php
index d1fa6f5746..9a3d2829e8 100644
--- a/src/DependencyInjection/ContainerFactory.php
+++ b/src/DependencyInjection/ContainerFactory.php
@@ -2,23 +2,33 @@
namespace PHPStan\DependencyInjection;
+use Nette\DI\Extensions\ExtensionsExtension;
use Nette\DI\Extensions\PhpExtension;
use Phar;
+use PhpParser\Parser;
use PHPStan\BetterReflection\BetterReflection;
+use PHPStan\BetterReflection\Reflector\Reflector;
use PHPStan\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber;
+use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator;
use PHPStan\Broker\Broker;
use PHPStan\Command\CommandHelper;
use PHPStan\File\FileHelper;
use PHPStan\Php\PhpVersion;
+use PHPStan\Reflection\ReflectionProvider;
+use PHPStan\Reflection\ReflectionProviderStaticAccessor;
use Symfony\Component\Finder\Finder;
+use function dirname;
+use function extension_loaded;
+use function ini_get;
+use function is_dir;
use function sys_get_temp_dir;
+use function time;
+use function unlink;
/** @api */
class ContainerFactory
{
- private string $currentWorkingDirectory;
-
private FileHelper $fileHelper;
private string $rootDirectory;
@@ -26,9 +36,8 @@ class ContainerFactory
private string $configDirectory;
/** @api */
- public function __construct(string $currentWorkingDirectory)
+ public function __construct(private string $currentWorkingDirectory)
{
- $this->currentWorkingDirectory = $currentWorkingDirectory;
$this->fileHelper = new FileHelper($currentWorkingDirectory);
$rootDir = __DIR__ . '/../..';
@@ -44,17 +53,10 @@ public function __construct(string $currentWorkingDirectory)
}
/**
- * @param string $tempDirectory
* @param string[] $additionalConfigFiles
* @param string[] $analysedPaths
* @param string[] $composerAutoloaderProjectPaths
* @param string[] $analysedPathsFromConfig
- * @param string $usedLevel
- * @param string|null $generateBaselineFile
- * @param string|null $cliAutoloadFile
- * @param string|null $singleReflectionFile
- * @param string|null $singleReflectionInsteadOfFile
- * @return \PHPStan\DependencyInjection\Container
*/
public function create(
string $tempDirectory,
@@ -66,18 +68,18 @@ public function create(
?string $generateBaselineFile = null,
?string $cliAutoloadFile = null,
?string $singleReflectionFile = null,
- ?string $singleReflectionInsteadOfFile = null
+ ?string $singleReflectionInsteadOfFile = null,
): Container
{
$configurator = new Configurator(new LoaderFactory(
$this->fileHelper,
$this->rootDirectory,
$this->currentWorkingDirectory,
- $generateBaselineFile
+ $generateBaselineFile,
));
$configurator->defaultExtensions = [
'php' => PhpExtension::class,
- 'extensions' => \Nette\DI\Extensions\ExtensionsExtension::class,
+ 'extensions' => ExtensionsExtension::class,
];
$configurator->setDebugMode(true);
$configurator->setTempDirectory($tempDirectory);
@@ -87,9 +89,7 @@ public function create(
'cliArgumentsVariablesRegistered' => ini_get('register_argc_argv') === '1',
'tmpDir' => $tempDirectory,
'additionalConfigFiles' => $additionalConfigFiles,
- 'analysedPaths' => $analysedPaths,
'composerAutoloaderProjectPaths' => $composerAutoloaderProjectPaths,
- 'analysedPathsFromConfig' => $analysedPathsFromConfig,
'generateBaselineFile' => $generateBaselineFile,
'usedLevel' => $usedLevel,
'cliAutoloadFile' => $cliAutoloadFile,
@@ -98,6 +98,8 @@ public function create(
$configurator->addDynamicParameters([
'singleReflectionFile' => $singleReflectionFile,
'singleReflectionInsteadOfFile' => $singleReflectionInsteadOfFile,
+ 'analysedPaths' => $analysedPaths,
+ 'analysedPathsFromConfig' => $analysedPathsFromConfig,
]);
$configurator->addConfig($this->configDirectory . '/config.neon');
foreach ($additionalConfigFiles as $additionalConfigFile) {
@@ -106,20 +108,27 @@ public function create(
$container = $configurator->createContainer();
- BetterReflection::$phpVersion = $container->getByType(PhpVersion::class)->getVersionId();
+ /** @var SourceLocator $sourceLocator */
+ $sourceLocator = $container->getService('betterReflectionSourceLocator');
+
+ /** @var Reflector $reflector */
+ $reflector = $container->getService('betterReflectionReflector');
+
+ /** @var Parser $phpParser */
+ $phpParser = $container->getService('phpParserDecorator');
BetterReflection::populate(
- $container->getService('betterReflectionSourceLocator'), // @phpstan-ignore-line
- $container->getService('betterReflectionClassReflector'), // @phpstan-ignore-line
- $container->getService('betterReflectionFunctionReflector'), // @phpstan-ignore-line
- $container->getService('betterReflectionConstantReflector'), // @phpstan-ignore-line
- $container->getService('phpParserDecorator'), // @phpstan-ignore-line
- $container->getByType(PhpStormStubsSourceStubber::class)
+ $container->getByType(PhpVersion::class)->getVersionId(),
+ $sourceLocator,
+ $reflector,
+ $phpParser,
+ $container->getByType(PhpStormStubsSourceStubber::class),
);
/** @var Broker $broker */
$broker = $container->getByType(Broker::class);
Broker::registerInstance($broker);
+ ReflectionProviderStaticAccessor::registerInstance($container->getByType(ReflectionProvider::class));
$container->getService('typeSpecifier');
BleedingEdgeToggle::setBleedingEdge($container->parameters['featureToggles']['bleedingEdge']);
@@ -133,13 +142,18 @@ public function clearOldContainers(string $tempDirectory): void
$this->fileHelper,
$this->rootDirectory,
$this->currentWorkingDirectory,
- null
+ null,
));
$configurator->setDebugMode(true);
$configurator->setTempDirectory($tempDirectory);
+ $containerDirectory = $configurator->getContainerCacheDirectory();
+ if (!is_dir($containerDirectory)) {
+ return;
+ }
+
$finder = new Finder();
- $finder->name('Container_*')->in($configurator->getContainerCacheDirectory());
+ $finder->name('Container_*')->in($containerDirectory);
$twoDaysAgo = time() - 24 * 60 * 60 * 2;
foreach ($finder as $containerFile) {
diff --git a/src/DependencyInjection/DerivativeContainerFactory.php b/src/DependencyInjection/DerivativeContainerFactory.php
index ada4f5f921..c859340da9 100644
--- a/src/DependencyInjection/DerivativeContainerFactory.php
+++ b/src/DependencyInjection/DerivativeContainerFactory.php
@@ -2,67 +2,40 @@
namespace PHPStan\DependencyInjection;
+use function array_merge;
+
class DerivativeContainerFactory
{
- private string $currentWorkingDirectory;
-
- private string $tempDirectory;
-
- /** @var string[] */
- private array $additionalConfigFiles;
-
- /** @var string[] */
- private array $analysedPaths;
-
- /** @var string[] */
- private array $composerAutoloaderProjectPaths;
-
- /** @var string[] */
- private array $analysedPathsFromConfig;
-
- private string $usedLevel;
-
- private ?string $generateBaselineFile;
-
/**
- * @param string $currentWorkingDirectory
- * @param string $tempDirectory
* @param string[] $additionalConfigFiles
* @param string[] $analysedPaths
* @param string[] $composerAutoloaderProjectPaths
* @param string[] $analysedPathsFromConfig
- * @param string $usedLevel
*/
public function __construct(
- string $currentWorkingDirectory,
- string $tempDirectory,
- array $additionalConfigFiles,
- array $analysedPaths,
- array $composerAutoloaderProjectPaths,
- array $analysedPathsFromConfig,
- string $usedLevel,
- ?string $generateBaselineFile
+ private string $currentWorkingDirectory,
+ private string $tempDirectory,
+ private array $additionalConfigFiles,
+ private array $analysedPaths,
+ private array $composerAutoloaderProjectPaths,
+ private array $analysedPathsFromConfig,
+ private string $usedLevel,
+ private ?string $generateBaselineFile,
+ private ?string $cliAutoloadFile,
+ private ?string $singleReflectionFile,
+ private ?string $singleReflectionInsteadOfFile,
)
{
- $this->currentWorkingDirectory = $currentWorkingDirectory;
- $this->tempDirectory = $tempDirectory;
- $this->additionalConfigFiles = $additionalConfigFiles;
- $this->analysedPaths = $analysedPaths;
- $this->composerAutoloaderProjectPaths = $composerAutoloaderProjectPaths;
- $this->analysedPathsFromConfig = $analysedPathsFromConfig;
- $this->usedLevel = $usedLevel;
- $this->generateBaselineFile = $generateBaselineFile;
}
/**
* @param string[] $additionalConfigFiles
- * @return \PHPStan\DependencyInjection\Container
*/
public function create(array $additionalConfigFiles): Container
{
$containerFactory = new ContainerFactory(
- $this->currentWorkingDirectory
+ $this->currentWorkingDirectory,
);
return $containerFactory->create(
@@ -72,7 +45,10 @@ public function create(array $additionalConfigFiles): Container
$this->composerAutoloaderProjectPaths,
$this->analysedPathsFromConfig,
$this->usedLevel,
- $this->generateBaselineFile
+ $this->generateBaselineFile,
+ $this->cliAutoloadFile,
+ $this->singleReflectionFile,
+ $this->singleReflectionInsteadOfFile,
);
}
diff --git a/src/DependencyInjection/InvalidIgnoredErrorPatternsException.php b/src/DependencyInjection/InvalidIgnoredErrorPatternsException.php
new file mode 100644
index 0000000000..dd9258c64d
--- /dev/null
+++ b/src/DependencyInjection/InvalidIgnoredErrorPatternsException.php
@@ -0,0 +1,27 @@
+errors));
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getErrors(): array
+ {
+ return $this->errors;
+ }
+
+}
diff --git a/src/DependencyInjection/LoaderFactory.php b/src/DependencyInjection/LoaderFactory.php
index eeb35a3110..d12900f5f1 100644
--- a/src/DependencyInjection/LoaderFactory.php
+++ b/src/DependencyInjection/LoaderFactory.php
@@ -8,25 +8,13 @@
class LoaderFactory
{
- private FileHelper $fileHelper;
-
- private string $rootDir;
-
- private string $currentWorkingDirectory;
-
- private ?string $generateBaselineFile;
-
public function __construct(
- FileHelper $fileHelper,
- string $rootDir,
- string $currentWorkingDirectory,
- ?string $generateBaselineFile
+ private FileHelper $fileHelper,
+ private string $rootDir,
+ private string $currentWorkingDirectory,
+ private ?string $generateBaselineFile,
)
{
- $this->fileHelper = $fileHelper;
- $this->rootDir = $rootDir;
- $this->currentWorkingDirectory = $currentWorkingDirectory;
- $this->generateBaselineFile = $generateBaselineFile;
}
public function createLoader(): Loader
diff --git a/src/DependencyInjection/MemoizingContainer.php b/src/DependencyInjection/MemoizingContainer.php
index d43641705c..07e92fbac5 100644
--- a/src/DependencyInjection/MemoizingContainer.php
+++ b/src/DependencyInjection/MemoizingContainer.php
@@ -2,17 +2,16 @@
namespace PHPStan\DependencyInjection;
+use function array_key_exists;
+
class MemoizingContainer implements Container
{
- private Container $originalContainer;
-
/** @var array */
private array $servicesByType = [];
- public function __construct(Container $originalContainer)
+ public function __construct(private Container $originalContainer)
{
- $this->originalContainer = $originalContainer;
}
public function hasService(string $serviceName): bool
@@ -20,19 +19,11 @@ public function hasService(string $serviceName): bool
return $this->originalContainer->hasService($serviceName);
}
- /**
- * @param string $serviceName
- * @return mixed
- */
public function getService(string $serviceName)
{
return $this->originalContainer->getService($serviceName);
}
- /**
- * @param string $className
- * @return mixed
- */
public function getByType(string $className)
{
if (array_key_exists($className, $this->servicesByType)) {
@@ -65,11 +56,6 @@ public function hasParameter(string $parameterName): bool
return $this->originalContainer->hasParameter($parameterName);
}
- /**
- * @param string $parameterName
- * @return mixed
- * @throws ParameterNotFoundException
- */
public function getParameter(string $parameterName)
{
return $this->originalContainer->getParameter($parameterName);
diff --git a/src/DependencyInjection/NeonAdapter.php b/src/DependencyInjection/NeonAdapter.php
index 8b3b7c9025..1f52ff84f5 100644
--- a/src/DependencyInjection/NeonAdapter.php
+++ b/src/DependencyInjection/NeonAdapter.php
@@ -6,15 +6,29 @@
use Nette\DI\Config\Helpers;
use Nette\DI\Definitions\Reference;
use Nette\DI\Definitions\Statement;
+use Nette\DI\InvalidConfigurationException;
use Nette\Neon\Entity;
+use Nette\Neon\Exception;
use Nette\Neon\Neon;
use PHPStan\File\FileHelper;
use PHPStan\File\FileReader;
+use function array_values;
+use function array_walk_recursive;
+use function dirname;
+use function implode;
+use function in_array;
+use function is_array;
+use function is_int;
+use function is_string;
+use function ltrim;
+use function sprintf;
+use function strpos;
+use function substr;
class NeonAdapter implements Adapter
{
- public const CACHE_KEY = 'v12-excludePaths-merge';
+ public const CACHE_KEY = 'v17-validate-schema';
private const PREVENT_MERGING_SUFFIX = '!';
@@ -22,7 +36,6 @@ class NeonAdapter implements Adapter
private array $fileHelpers = [];
/**
- * @param string $file
* @return mixed[]
*/
public function load(string $file): array
@@ -30,8 +43,8 @@ public function load(string $file): array
$contents = FileReader::read($file);
try {
return $this->process((array) Neon::decode($contents), '', $file);
- } catch (\Nette\Neon\Exception $e) {
- throw new \Nette\Neon\Exception(sprintf('Error while loading %s: %s', $file, $e->getMessage()));
+ } catch (Exception $e) {
+ throw new Exception(sprintf('Error while loading %s: %s', $file, $e->getMessage()));
}
}
@@ -45,7 +58,7 @@ public function process(array $arr, string $fileKey, string $file): array
foreach ($arr as $key => $val) {
if (is_string($key) && substr($key, -1) === self::PREVENT_MERGING_SUFFIX) {
if (!is_array($val) && $val !== null) {
- throw new \Nette\DI\InvalidConfigurationException(sprintf('Replacing operator is available only for arrays, item \'%s\' is not array.', $key));
+ throw new InvalidConfigurationException(sprintf('Replacing operator is available only for arrays, item \'%s\' is not array.', $key));
}
$key = substr($key, 0, -1);
$val[Helpers::PREVENT_MERGING] = true;
@@ -70,7 +83,7 @@ public function process(array $arr, string $fileKey, string $file): array
foreach ($this->process($val->attributes, $fileKeyToPass, $file) as $st) {
$tmp = new Statement(
$tmp === null ? $st->getEntity() : [$tmp, ltrim(implode('::', (array) $st->getEntity()), ':')],
- $st->arguments
+ $st->arguments,
);
}
$val = $tmp;
@@ -88,8 +101,6 @@ public function process(array $arr, string $fileKey, string $file): array
}
if (in_array($keyToResolve, [
- '[parameters][autoload_files][]',
- '[parameters][autoload_directories][]',
'[parameters][paths][]',
'[parameters][excludes_analyse][]',
'[parameters][excludePaths][]',
@@ -97,7 +108,6 @@ public function process(array $arr, string $fileKey, string $file): array
'[parameters][excludePaths][analyseAndScan][]',
'[parameters][ignoreErrors][][paths][]',
'[parameters][ignoreErrors][][path]',
- '[parameters][bootstrap]',
'[parameters][bootstrapFiles][]',
'[parameters][scanFiles][]',
'[parameters][scanDirectories][]',
@@ -106,7 +116,9 @@ public function process(array $arr, string $fileKey, string $file): array
'[parameters][benchmarkFile]',
'[parameters][stubFiles][]',
'[parameters][symfony][console_application_loader]',
+ '[parameters][symfony][consoleApplicationLoader]',
'[parameters][symfony][container_xml_path]',
+ '[parameters][symfony][containerXmlPath]',
'[parameters][doctrine][objectManagerLoader]',
], true) && is_string($val) && strpos($val, '%') === false && strpos($val, '*') !== 0) {
$fileHelper = $this->createFileHelperByFile($file);
@@ -128,7 +140,6 @@ public function process(array $arr, string $fileKey, string $file): array
/**
* @param mixed[] $data
- * @return string
*/
public function dump(array $data): string
{
@@ -140,7 +151,7 @@ static function (&$val): void {
}
$val = self::statementToEntity($val);
- }
+ },
);
return "# generated by Nette\n\n" . Neon::encode($data, Neon::BLOCK);
}
@@ -155,7 +166,7 @@ static function (&$val): void {
} elseif ($val instanceof Reference) {
$val = '@' . $val->getValue();
}
- }
+ },
);
$entity = $val->getEntity();
@@ -168,7 +179,7 @@ static function (&$val): void {
[
self::statementToEntity($entity[0]),
new Entity('::' . $entity[1], $val->arguments),
- ]
+ ],
);
} elseif ($entity[0] instanceof Reference) {
$entity = '@' . $entity[0]->getValue() . '::' . $entity[1];
diff --git a/src/DependencyInjection/NeonLoader.php b/src/DependencyInjection/NeonLoader.php
index cee5eb5e1e..4f797d1af7 100644
--- a/src/DependencyInjection/NeonLoader.php
+++ b/src/DependencyInjection/NeonLoader.php
@@ -2,27 +2,20 @@
namespace PHPStan\DependencyInjection;
+use Nette\DI\Config\Loader;
use PHPStan\File\FileHelper;
-class NeonLoader extends \Nette\DI\Config\Loader
+class NeonLoader extends Loader
{
- private FileHelper $fileHelper;
-
- private ?string $generateBaselineFile;
-
public function __construct(
- FileHelper $fileHelper,
- ?string $generateBaselineFile
+ private FileHelper $fileHelper,
+ private ?string $generateBaselineFile,
)
{
- $this->fileHelper = $fileHelper;
- $this->generateBaselineFile = $generateBaselineFile;
}
/**
- * @param string $file
- * @param bool|null $merge
* @return mixed[]
*/
public function load(string $file, ?bool $merge = true): array
diff --git a/src/DependencyInjection/Nette/NetteContainer.php b/src/DependencyInjection/Nette/NetteContainer.php
index 5e57025649..9d9466c8d7 100644
--- a/src/DependencyInjection/Nette/NetteContainer.php
+++ b/src/DependencyInjection/Nette/NetteContainer.php
@@ -3,6 +3,10 @@
namespace PHPStan\DependencyInjection\Nette;
use PHPStan\DependencyInjection\Container;
+use PHPStan\DependencyInjection\ParameterNotFoundException;
+use function array_key_exists;
+use function array_keys;
+use function array_map;
/**
* @internal
@@ -10,11 +14,8 @@
class NetteContainer implements Container
{
- private \Nette\DI\Container $container;
-
- public function __construct(\Nette\DI\Container $container)
+ public function __construct(private \Nette\DI\Container $container)
{
- $this->container = $container;
}
public function hasService(string $serviceName): bool
@@ -23,7 +24,6 @@ public function hasService(string $serviceName): bool
}
/**
- * @param string $serviceName
* @return mixed
*/
public function getService(string $serviceName)
@@ -32,7 +32,9 @@ public function getService(string $serviceName)
}
/**
- * @param string $className
+ * @phpstan-template T of object
+ * @phpstan-param class-string $className
+ * @phpstan-return T
* @return mixed
*/
public function getByType(string $className)
@@ -41,7 +43,7 @@ public function getByType(string $className)
}
/**
- * @param string $className
+ * @param class-string $className
* @return string[]
*/
public function findServiceNamesByType(string $className): array
@@ -50,7 +52,6 @@ public function findServiceNamesByType(string $className): array
}
/**
- * @param string $tagName
* @return mixed[]
*/
public function getServicesByTag(string $tagName): array
@@ -72,13 +73,12 @@ public function hasParameter(string $parameterName): bool
}
/**
- * @param string $parameterName
* @return mixed
*/
public function getParameter(string $parameterName)
{
if (!$this->hasParameter($parameterName)) {
- throw new \PHPStan\DependencyInjection\ParameterNotFoundException($parameterName);
+ throw new ParameterNotFoundException($parameterName);
}
return $this->container->parameters[$parameterName];
@@ -90,9 +90,7 @@ public function getParameter(string $parameterName)
*/
private function tagsToServices(array $tags): array
{
- return array_map(function (string $serviceName) {
- return $this->getService($serviceName);
- }, array_keys($tags));
+ return array_map(fn (string $serviceName) => $this->getService($serviceName), array_keys($tags));
}
}
diff --git a/src/DependencyInjection/ParameterNotFoundException.php b/src/DependencyInjection/ParameterNotFoundException.php
index 393c5cd779..07a92a376f 100644
--- a/src/DependencyInjection/ParameterNotFoundException.php
+++ b/src/DependencyInjection/ParameterNotFoundException.php
@@ -2,7 +2,10 @@
namespace PHPStan\DependencyInjection;
-class ParameterNotFoundException extends \Exception
+use Exception;
+use function sprintf;
+
+class ParameterNotFoundException extends Exception
{
public function __construct(string $parameterName)
diff --git a/src/DependencyInjection/ParametersSchemaExtension.php b/src/DependencyInjection/ParametersSchemaExtension.php
index ae35d20efa..3daebbbbad 100644
--- a/src/DependencyInjection/ParametersSchemaExtension.php
+++ b/src/DependencyInjection/ParametersSchemaExtension.php
@@ -2,48 +2,68 @@
namespace PHPStan\DependencyInjection;
+use Nette\DI\CompilerExtension;
use Nette\DI\Definitions\Statement;
+use Nette\Schema\Context as SchemaContext;
+use Nette\Schema\DynamicParameter;
+use Nette\Schema\Elements\AnyOf;
+use Nette\Schema\Elements\Structure;
+use Nette\Schema\Elements\Type;
use Nette\Schema\Expect;
+use Nette\Schema\Processor;
use Nette\Schema\Schema;
+use PHPStan\ShouldNotHappenException;
+use function array_map;
+use function count;
+use function is_array;
-class ParametersSchemaExtension extends \Nette\DI\CompilerExtension
+class ParametersSchemaExtension extends CompilerExtension
{
- public function getConfigSchema(): \Nette\Schema\Schema
+ public function getConfigSchema(): Schema
{
return Expect::arrayOf(Expect::type(Statement::class))->min(1);
}
public function loadConfiguration(): void
{
+ $builder = $this->getContainerBuilder();
+ if (!$builder->parameters['__validate']) {
+ return;
+ }
+
/** @var mixed[] $config */
$config = $this->config;
- $config['__parametersSchema'] = new Statement(Schema::class);
- $builder = $this->getContainerBuilder();
- $builder->parameters['__parametersSchema'] = $this->processArgument(
+ $config['analysedPaths'] = new Statement(DynamicParameter::class);
+ $config['analysedPathsFromConfig'] = new Statement(DynamicParameter::class);
+ $config['singleReflectionFile'] = new Statement(DynamicParameter::class);
+ $config['singleReflectionInsteadOfFile'] = new Statement(DynamicParameter::class);
+ $schema = $this->processArgument(
new Statement('schema', [
new Statement('structure', [$config]),
- ])
+ ]),
);
+ $processor = new Processor();
+ $processor->onNewContext[] = static function (SchemaContext $context): void {
+ $context->path = ['parameters'];
+ };
+ $processor->process($schema, $builder->parameters);
}
/**
* @param Statement[] $statements
- * @return \Nette\Schema\Schema
*/
private function processSchema(array $statements): Schema
{
if (count($statements) === 0) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$parameterSchema = null;
foreach ($statements as $statement) {
- $processedArguments = array_map(function ($argument) {
- return $this->processArgument($argument);
- }, $statement->arguments);
+ $processedArguments = array_map(fn ($argument) => $this->processArgument($argument), $statement->arguments);
if ($parameterSchema === null) {
- /** @var \Nette\Schema\Elements\Type|\Nette\Schema\Elements\AnyOf|\Nette\Schema\Elements\Structure $parameterSchema */
+ /** @var Type|AnyOf|Structure $parameterSchema */
$parameterSchema = Expect::{$statement->getEntity()}(...$processedArguments);
} else {
$parameterSchema->{$statement->getEntity()}(...$processedArguments);
@@ -66,14 +86,14 @@ private function processArgument($argument)
$arguments = [];
foreach ($argument->arguments as $schemaArgument) {
if (!$schemaArgument instanceof Statement) {
- throw new \PHPStan\ShouldNotHappenException('schema() should contain another statement().');
+ throw new ShouldNotHappenException('schema() should contain another statement().');
}
$arguments[] = $schemaArgument;
}
if (count($arguments) === 0) {
- throw new \PHPStan\ShouldNotHappenException('schema() should have at least one argument.');
+ throw new ShouldNotHappenException('schema() should have at least one argument.');
}
return $this->processSchema($arguments);
diff --git a/src/DependencyInjection/Reflection/DirectClassReflectionExtensionRegistryProvider.php b/src/DependencyInjection/Reflection/DirectClassReflectionExtensionRegistryProvider.php
index 06133de037..fa557b070a 100644
--- a/src/DependencyInjection/Reflection/DirectClassReflectionExtensionRegistryProvider.php
+++ b/src/DependencyInjection/Reflection/DirectClassReflectionExtensionRegistryProvider.php
@@ -13,25 +13,17 @@
class DirectClassReflectionExtensionRegistryProvider implements ClassReflectionExtensionRegistryProvider
{
- /** @var \PHPStan\Reflection\PropertiesClassReflectionExtension[] */
- private array $propertiesClassReflectionExtensions;
-
- /** @var \PHPStan\Reflection\MethodsClassReflectionExtension[] */
- private array $methodsClassReflectionExtensions;
-
private Broker $broker;
/**
- * @param \PHPStan\Reflection\PropertiesClassReflectionExtension[] $propertiesClassReflectionExtensions
- * @param \PHPStan\Reflection\MethodsClassReflectionExtension[] $methodsClassReflectionExtensions
+ * @param PropertiesClassReflectionExtension[] $propertiesClassReflectionExtensions
+ * @param MethodsClassReflectionExtension[] $methodsClassReflectionExtensions
*/
public function __construct(
- array $propertiesClassReflectionExtensions,
- array $methodsClassReflectionExtensions
+ private array $propertiesClassReflectionExtensions,
+ private array $methodsClassReflectionExtensions,
)
{
- $this->propertiesClassReflectionExtensions = $propertiesClassReflectionExtensions;
- $this->methodsClassReflectionExtensions = $methodsClassReflectionExtensions;
}
public function setBroker(Broker $broker): void
@@ -54,7 +46,7 @@ public function getRegistry(): ClassReflectionExtensionRegistry
return new ClassReflectionExtensionRegistry(
$this->broker,
$this->propertiesClassReflectionExtensions,
- $this->methodsClassReflectionExtensions
+ $this->methodsClassReflectionExtensions,
);
}
diff --git a/src/DependencyInjection/Reflection/LazyClassReflectionExtensionRegistryProvider.php b/src/DependencyInjection/Reflection/LazyClassReflectionExtensionRegistryProvider.php
index 41c35dacdf..259600a280 100644
--- a/src/DependencyInjection/Reflection/LazyClassReflectionExtensionRegistryProvider.php
+++ b/src/DependencyInjection/Reflection/LazyClassReflectionExtensionRegistryProvider.php
@@ -4,21 +4,20 @@
use PHPStan\Broker\Broker;
use PHPStan\Broker\BrokerFactory;
+use PHPStan\DependencyInjection\Container;
use PHPStan\Reflection\Annotations\AnnotationsMethodsClassReflectionExtension;
use PHPStan\Reflection\Annotations\AnnotationsPropertiesClassReflectionExtension;
use PHPStan\Reflection\ClassReflectionExtensionRegistry;
use PHPStan\Reflection\Php\PhpClassReflectionExtension;
+use function array_merge;
class LazyClassReflectionExtensionRegistryProvider implements ClassReflectionExtensionRegistryProvider
{
- private \PHPStan\DependencyInjection\Container $container;
+ private ?ClassReflectionExtensionRegistry $registry = null;
- private ?\PHPStan\Reflection\ClassReflectionExtensionRegistry $registry = null;
-
- public function __construct(\PHPStan\DependencyInjection\Container $container)
+ public function __construct(private Container $container)
{
- $this->container = $container;
}
public function getRegistry(): ClassReflectionExtensionRegistry
@@ -31,7 +30,7 @@ public function getRegistry(): ClassReflectionExtensionRegistry
$this->registry = new ClassReflectionExtensionRegistry(
$this->container->getByType(Broker::class),
array_merge([$phpClassReflectionExtension], $this->container->getServicesByTag(BrokerFactory::PROPERTIES_CLASS_REFLECTION_EXTENSION_TAG), [$annotationsPropertiesClassReflectionExtension]),
- array_merge([$phpClassReflectionExtension], $this->container->getServicesByTag(BrokerFactory::METHODS_CLASS_REFLECTION_EXTENSION_TAG), [$annotationsMethodsClassReflectionExtension])
+ array_merge([$phpClassReflectionExtension], $this->container->getServicesByTag(BrokerFactory::METHODS_CLASS_REFLECTION_EXTENSION_TAG), [$annotationsMethodsClassReflectionExtension]),
);
}
diff --git a/src/DependencyInjection/RulesExtension.php b/src/DependencyInjection/RulesExtension.php
index b72f286754..391fdb9a1d 100644
--- a/src/DependencyInjection/RulesExtension.php
+++ b/src/DependencyInjection/RulesExtension.php
@@ -2,13 +2,15 @@
namespace PHPStan\DependencyInjection;
+use Nette\DI\CompilerExtension;
use Nette\Schema\Expect;
+use Nette\Schema\Schema;
use PHPStan\Rules\RegistryFactory;
-class RulesExtension extends \Nette\DI\CompilerExtension
+class RulesExtension extends CompilerExtension
{
- public function getConfigSchema(): \Nette\Schema\Schema
+ public function getConfigSchema(): Schema
{
return Expect::listOf('string');
}
@@ -22,7 +24,7 @@ public function loadConfiguration(): void
foreach ($config as $key => $rule) {
$builder->addDefinition($this->prefix((string) $key))
->setFactory($rule)
- ->setAutowired(false)
+ ->setAutowired($rule)
->addTag(RegistryFactory::RULE_TAG);
}
}
diff --git a/src/DependencyInjection/Type/DirectDynamicReturnTypeExtensionRegistryProvider.php b/src/DependencyInjection/Type/DirectDynamicReturnTypeExtensionRegistryProvider.php
deleted file mode 100644
index 3759ec24ed..0000000000
--- a/src/DependencyInjection/Type/DirectDynamicReturnTypeExtensionRegistryProvider.php
+++ /dev/null
@@ -1,83 +0,0 @@
-dynamicMethodReturnTypeExtensions = $dynamicMethodReturnTypeExtensions;
- $this->dynamicStaticMethodReturnTypeExtensions = $dynamicStaticMethodReturnTypeExtensions;
- $this->dynamicFunctionReturnTypeExtensions = $dynamicFunctionReturnTypeExtensions;
- }
-
- public function setBroker(Broker $broker): void
- {
- $this->broker = $broker;
- }
-
- public function setReflectionProvider(ReflectionProvider $reflectionProvider): void
- {
- $this->reflectionProvider = $reflectionProvider;
- }
-
- public function addDynamicMethodReturnTypeExtension(DynamicMethodReturnTypeExtension $extension): void
- {
- $this->dynamicMethodReturnTypeExtensions[] = $extension;
- }
-
- public function addDynamicStaticMethodReturnTypeExtension(DynamicStaticMethodReturnTypeExtension $extension): void
- {
- $this->dynamicStaticMethodReturnTypeExtensions[] = $extension;
- }
-
- public function addDynamicFunctionReturnTypeExtension(DynamicFunctionReturnTypeExtension $extension): void
- {
- $this->dynamicFunctionReturnTypeExtensions[] = $extension;
- }
-
- public function getRegistry(): DynamicReturnTypeExtensionRegistry
- {
- return new DynamicReturnTypeExtensionRegistry(
- $this->broker,
- $this->reflectionProvider,
- $this->dynamicMethodReturnTypeExtensions,
- $this->dynamicStaticMethodReturnTypeExtensions,
- $this->dynamicFunctionReturnTypeExtensions
- );
- }
-
-}
diff --git a/src/DependencyInjection/Type/DirectOperatorTypeSpecifyingExtensionRegistryProvider.php b/src/DependencyInjection/Type/DirectOperatorTypeSpecifyingExtensionRegistryProvider.php
deleted file mode 100644
index 80f73d7b5a..0000000000
--- a/src/DependencyInjection/Type/DirectOperatorTypeSpecifyingExtensionRegistryProvider.php
+++ /dev/null
@@ -1,38 +0,0 @@
-extensions = $extensions;
- }
-
- public function setBroker(Broker $broker): void
- {
- $this->broker = $broker;
- }
-
- public function getRegistry(): OperatorTypeSpecifyingExtensionRegistry
- {
- return new OperatorTypeSpecifyingExtensionRegistry(
- $this->broker,
- $this->extensions
- );
- }
-
-}
diff --git a/src/DependencyInjection/Type/LazyDynamicReturnTypeExtensionRegistryProvider.php b/src/DependencyInjection/Type/LazyDynamicReturnTypeExtensionRegistryProvider.php
index 1e52ed099c..f992ad9ddf 100644
--- a/src/DependencyInjection/Type/LazyDynamicReturnTypeExtensionRegistryProvider.php
+++ b/src/DependencyInjection/Type/LazyDynamicReturnTypeExtensionRegistryProvider.php
@@ -4,19 +4,17 @@
use PHPStan\Broker\Broker;
use PHPStan\Broker\BrokerFactory;
+use PHPStan\DependencyInjection\Container;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\DynamicReturnTypeExtensionRegistry;
class LazyDynamicReturnTypeExtensionRegistryProvider implements DynamicReturnTypeExtensionRegistryProvider
{
- private \PHPStan\DependencyInjection\Container $container;
+ private ?DynamicReturnTypeExtensionRegistry $registry = null;
- private ?\PHPStan\Type\DynamicReturnTypeExtensionRegistry $registry = null;
-
- public function __construct(\PHPStan\DependencyInjection\Container $container)
+ public function __construct(private Container $container)
{
- $this->container = $container;
}
public function getRegistry(): DynamicReturnTypeExtensionRegistry
@@ -27,7 +25,7 @@ public function getRegistry(): DynamicReturnTypeExtensionRegistry
$this->container->getByType(ReflectionProvider::class),
$this->container->getServicesByTag(BrokerFactory::DYNAMIC_METHOD_RETURN_TYPE_EXTENSION_TAG),
$this->container->getServicesByTag(BrokerFactory::DYNAMIC_STATIC_METHOD_RETURN_TYPE_EXTENSION_TAG),
- $this->container->getServicesByTag(BrokerFactory::DYNAMIC_FUNCTION_RETURN_TYPE_EXTENSION_TAG)
+ $this->container->getServicesByTag(BrokerFactory::DYNAMIC_FUNCTION_RETURN_TYPE_EXTENSION_TAG),
);
}
diff --git a/src/DependencyInjection/Type/LazyDynamicThrowTypeExtensionProvider.php b/src/DependencyInjection/Type/LazyDynamicThrowTypeExtensionProvider.php
index ef7885034c..4696fb8b21 100644
--- a/src/DependencyInjection/Type/LazyDynamicThrowTypeExtensionProvider.php
+++ b/src/DependencyInjection/Type/LazyDynamicThrowTypeExtensionProvider.php
@@ -11,11 +11,8 @@ class LazyDynamicThrowTypeExtensionProvider implements DynamicThrowTypeExtension
public const METHOD_TAG = 'phpstan.dynamicMethodThrowTypeExtension';
public const STATIC_METHOD_TAG = 'phpstan.dynamicStaticMethodThrowTypeExtension';
- private Container $container;
-
- public function __construct(Container $container)
+ public function __construct(private Container $container)
{
- $this->container = $container;
}
public function getDynamicFunctionThrowTypeExtensions(): array
diff --git a/src/DependencyInjection/Type/LazyOperatorTypeSpecifyingExtensionRegistryProvider.php b/src/DependencyInjection/Type/LazyOperatorTypeSpecifyingExtensionRegistryProvider.php
index 3f9f0ca376..22e356fe70 100644
--- a/src/DependencyInjection/Type/LazyOperatorTypeSpecifyingExtensionRegistryProvider.php
+++ b/src/DependencyInjection/Type/LazyOperatorTypeSpecifyingExtensionRegistryProvider.php
@@ -4,18 +4,16 @@
use PHPStan\Broker\Broker;
use PHPStan\Broker\BrokerFactory;
+use PHPStan\DependencyInjection\Container;
use PHPStan\Type\OperatorTypeSpecifyingExtensionRegistry;
class LazyOperatorTypeSpecifyingExtensionRegistryProvider implements OperatorTypeSpecifyingExtensionRegistryProvider
{
- private \PHPStan\DependencyInjection\Container $container;
+ private ?OperatorTypeSpecifyingExtensionRegistry $registry = null;
- private ?\PHPStan\Type\OperatorTypeSpecifyingExtensionRegistry $registry = null;
-
- public function __construct(\PHPStan\DependencyInjection\Container $container)
+ public function __construct(private Container $container)
{
- $this->container = $container;
}
public function getRegistry(): OperatorTypeSpecifyingExtensionRegistry
@@ -23,7 +21,7 @@ public function getRegistry(): OperatorTypeSpecifyingExtensionRegistry
if ($this->registry === null) {
$this->registry = new OperatorTypeSpecifyingExtensionRegistry(
$this->container->getByType(Broker::class),
- $this->container->getServicesByTag(BrokerFactory::OPERATOR_TYPE_SPECIFYING_EXTENSION_TAG)
+ $this->container->getServicesByTag(BrokerFactory::OPERATOR_TYPE_SPECIFYING_EXTENSION_TAG),
);
}
diff --git a/src/DependencyInjection/ValidateIgnoredErrorsExtension.php b/src/DependencyInjection/ValidateIgnoredErrorsExtension.php
new file mode 100644
index 0000000000..260cee8f6b
--- /dev/null
+++ b/src/DependencyInjection/ValidateIgnoredErrorsExtension.php
@@ -0,0 +1,146 @@
+getContainerBuilder();
+ if (!$builder->parameters['__validate']) {
+ return;
+ }
+
+ $ignoreErrors = $builder->parameters['ignoreErrors'];
+ if (count($ignoreErrors) === 0) {
+ return;
+ }
+
+ /** @throws void */
+ $parser = Llk::load(new Read('hoa://Library/Regex/Grammar.pp'));
+ $reflectionProvider = new DummyReflectionProvider();
+ ReflectionProviderStaticAccessor::registerInstance($reflectionProvider);
+ $ignoredRegexValidator = new IgnoredRegexValidator(
+ $parser,
+ new TypeStringResolver(
+ new Lexer(),
+ new TypeParser(new ConstExprParser()),
+ new TypeNodeResolver(
+ new DirectTypeNodeResolverExtensionRegistryProvider(
+ new class implements TypeNodeResolverExtensionRegistry {
+
+ public function getExtensions(): array
+ {
+ return [];
+ }
+
+ },
+ ),
+ new DirectReflectionProviderProvider($reflectionProvider),
+ new DirectTypeAliasResolverProvider(new class implements TypeAliasResolver {
+
+ public function hasTypeAlias(string $aliasName, ?string $classNameScope): bool
+ {
+ return false;
+ }
+
+ public function resolveTypeAlias(string $aliasName, NameScope $nameScope): ?Type
+ {
+ return null;
+ }
+
+ }),
+ ),
+ ),
+ );
+ $errors = [];
+
+ foreach ($ignoreErrors as $ignoreError) {
+ try {
+ if (is_array($ignoreError)) {
+ if (isset($ignoreError['count'])) {
+ continue; // ignoreError coming from baseline will be correct
+ }
+ $ignoreMessage = $ignoreError['message'];
+ } else {
+ $ignoreMessage = $ignoreError;
+ }
+
+ Strings::match('', $ignoreMessage);
+ $validationResult = $ignoredRegexValidator->validate($ignoreMessage);
+ $ignoredTypes = $validationResult->getIgnoredTypes();
+ if (count($ignoredTypes) > 0) {
+ $errors[] = $this->createIgnoredTypesError($ignoreMessage, $ignoredTypes);
+ }
+
+ if ($validationResult->hasAnchorsInTheMiddle()) {
+ $errors[] = $this->createAnchorInTheMiddleError($ignoreMessage);
+ }
+
+ if ($validationResult->areAllErrorsIgnored()) {
+ $errors[] = sprintf("Ignored error %s has an unescaped '%s' which leads to ignoring all errors. Use '%s' instead.", $ignoreMessage, $validationResult->getWrongSequence(), $validationResult->getEscapedWrongSequence());
+ }
+ } catch (RegexpException $e) {
+ $errors[] = $e->getMessage();
+ }
+ }
+
+ if (count($errors) === 0) {
+ return;
+ }
+
+ throw new InvalidIgnoredErrorPatternsException($errors);
+ }
+
+ /**
+ * @param array $ignoredTypes
+ */
+ private function createIgnoredTypesError(string $regex, array $ignoredTypes): string
+ {
+ return sprintf(
+ "Ignored error %s has an unescaped '|' which leads to ignoring more errors than intended. Use '\\|' instead.\n%s",
+ $regex,
+ sprintf(
+ "It ignores all errors containing the following types:\n%s",
+ implode("\n", array_map(static fn (string $typeDescription): string => sprintf('* %s', $typeDescription), array_keys($ignoredTypes))),
+ ),
+ );
+ }
+
+ private function createAnchorInTheMiddleError(string $regex): string
+ {
+ return sprintf("Ignored error %s has an unescaped anchor '$' in the middle. This leads to unintended behavior. Use '\\$' instead.", $regex);
+ }
+
+}
diff --git a/src/File/CouldNotReadFileException.php b/src/File/CouldNotReadFileException.php
index 356d461a11..5803fd53df 100644
--- a/src/File/CouldNotReadFileException.php
+++ b/src/File/CouldNotReadFileException.php
@@ -2,7 +2,10 @@
namespace PHPStan\File;
-class CouldNotReadFileException extends \PHPStan\AnalysedCodeException
+use PHPStan\AnalysedCodeException;
+use function sprintf;
+
+class CouldNotReadFileException extends AnalysedCodeException
{
public function __construct(string $fileName)
diff --git a/src/File/CouldNotWriteFileException.php b/src/File/CouldNotWriteFileException.php
index 624f10e0d1..0fffa54dcf 100644
--- a/src/File/CouldNotWriteFileException.php
+++ b/src/File/CouldNotWriteFileException.php
@@ -2,7 +2,10 @@
namespace PHPStan\File;
-class CouldNotWriteFileException extends \PHPStan\AnalysedCodeException
+use PHPStan\AnalysedCodeException;
+use function sprintf;
+
+class CouldNotWriteFileException extends AnalysedCodeException
{
public function __construct(string $fileName, string $error)
diff --git a/src/File/FileExcluder.php b/src/File/FileExcluder.php
index 4b60c7d5c4..3c5cf85f3b 100644
--- a/src/File/FileExcluder.php
+++ b/src/File/FileExcluder.php
@@ -2,6 +2,16 @@
namespace PHPStan\File;
+use function array_merge;
+use function fnmatch;
+use function in_array;
+use function preg_match;
+use function strlen;
+use function strpos;
+use const DIRECTORY_SEPARATOR;
+use const FNM_CASEFOLD;
+use const FNM_NOESCAPE;
+
class FileExcluder
{
@@ -10,20 +20,27 @@ class FileExcluder
*
* @var string[]
*/
- private array $analyseExcludes;
+ private array $literalAnalyseExcludes = [];
+
+ /**
+ * fnmatch() patterns to use for excluding files and directories from analysing
+ * @var string[]
+ */
+ private array $fnmatchAnalyseExcludes = [];
+
+ private int $fnmatchFlags;
/**
- * @param FileHelper $fileHelper
* @param string[] $analyseExcludes
* @param string[] $stubFiles
*/
public function __construct(
FileHelper $fileHelper,
array $analyseExcludes,
- array $stubFiles
+ array $stubFiles,
)
{
- $this->analyseExcludes = array_map(function (string $exclude) use ($fileHelper): string {
+ foreach (array_merge($analyseExcludes, $stubFiles) as $exclude) {
$len = strlen($exclude);
$trailingDirSeparator = ($len > 0 && in_array($exclude[$len - 1], ['\\', '/'], true));
@@ -34,28 +51,29 @@ public function __construct(
}
if ($this->isFnmatchPattern($normalized)) {
- return $normalized;
+ $this->fnmatchAnalyseExcludes[] = $normalized;
+ } else {
+ $this->literalAnalyseExcludes[] = $fileHelper->absolutizePath($normalized);
}
+ }
- return $fileHelper->absolutizePath($normalized);
- }, array_merge($analyseExcludes, $stubFiles));
+ $isWindows = DIRECTORY_SEPARATOR === '\\';
+ if ($isWindows) {
+ $this->fnmatchFlags = FNM_NOESCAPE | FNM_CASEFOLD;
+ } else {
+ $this->fnmatchFlags = 0;
+ }
}
public function isExcludedFromAnalysing(string $file): bool
{
- foreach ($this->analyseExcludes as $exclude) {
+ foreach ($this->literalAnalyseExcludes as $exclude) {
if (strpos($file, $exclude) === 0) {
return true;
}
-
- $isWindows = DIRECTORY_SEPARATOR === '\\';
- if ($isWindows) {
- $fnmatchFlags = FNM_NOESCAPE | FNM_CASEFOLD;
- } else {
- $fnmatchFlags = 0;
- }
-
- if ($this->isFnmatchPattern($exclude) && fnmatch($exclude, $file, $fnmatchFlags)) {
+ }
+ foreach ($this->fnmatchAnalyseExcludes as $exclude) {
+ if (fnmatch($exclude, $file, $this->fnmatchFlags)) {
return true;
}
}
diff --git a/src/File/FileExcluderFactory.php b/src/File/FileExcluderFactory.php
index 53ba207582..26976a3dfb 100644
--- a/src/File/FileExcluderFactory.php
+++ b/src/File/FileExcluderFactory.php
@@ -2,31 +2,24 @@
namespace PHPStan\File;
+use function array_key_exists;
+use function array_merge;
+use function array_unique;
+use function array_values;
+
class FileExcluderFactory
{
- private FileExcluderRawFactory $fileExcluderRawFactory;
-
- /** @var string[] */
- private array $obsoleteExcludesAnalyse;
-
- /** @var array{analyse?: array, analyseAndScan?: array}|null */
- private ?array $excludePaths;
-
/**
- * @param FileExcluderRawFactory $fileExcluderRawFactory
* @param string[] $obsoleteExcludesAnalyse
* @param array{analyse?: array, analyseAndScan?: array}|null $excludePaths
*/
public function __construct(
- FileExcluderRawFactory $fileExcluderRawFactory,
- array $obsoleteExcludesAnalyse,
- ?array $excludePaths
+ private FileExcluderRawFactory $fileExcluderRawFactory,
+ private array $obsoleteExcludesAnalyse,
+ private ?array $excludePaths,
)
{
- $this->fileExcluderRawFactory = $fileExcluderRawFactory;
- $this->obsoleteExcludesAnalyse = $obsoleteExcludesAnalyse;
- $this->excludePaths = $excludePaths;
}
public function createAnalyseFileExcluder(): FileExcluder
diff --git a/src/File/FileExcluderRawFactory.php b/src/File/FileExcluderRawFactory.php
index b2f7e4ee77..0e3550cb3a 100644
--- a/src/File/FileExcluderRawFactory.php
+++ b/src/File/FileExcluderRawFactory.php
@@ -7,10 +7,9 @@ interface FileExcluderRawFactory
/**
* @param string[] $analyseExcludes
- * @return FileExcluder
*/
public function create(
- array $analyseExcludes
+ array $analyseExcludes,
): FileExcluder;
}
diff --git a/src/File/FileFinder.php b/src/File/FileFinder.php
index 61469de531..d549d9c2cc 100644
--- a/src/File/FileFinder.php
+++ b/src/File/FileFinder.php
@@ -3,46 +3,38 @@
namespace PHPStan\File;
use Symfony\Component\Finder\Finder;
+use function array_filter;
+use function array_values;
+use function file_exists;
+use function implode;
+use function is_file;
class FileFinder
{
- private FileExcluder $fileExcluder;
-
- private FileHelper $fileHelper;
-
- /** @var string[] */
- private array $fileExtensions;
-
/**
- * @param FileExcluder $fileExcluder
- * @param FileHelper $fileHelper
* @param string[] $fileExtensions
*/
public function __construct(
- FileExcluder $fileExcluder,
- FileHelper $fileHelper,
- array $fileExtensions
+ private FileExcluder $fileExcluder,
+ private FileHelper $fileHelper,
+ private array $fileExtensions,
)
{
- $this->fileExcluder = $fileExcluder;
- $this->fileHelper = $fileHelper;
- $this->fileExtensions = $fileExtensions;
}
/**
* @param string[] $paths
- * @return FileFinderResult
*/
public function findFiles(array $paths): FileFinderResult
{
$onlyFiles = true;
$files = [];
foreach ($paths as $path) {
- if (!file_exists($path)) {
- throw new \PHPStan\File\PathNotFoundException($path);
- } elseif (is_file($path)) {
+ if (is_file($path)) {
$files[] = $this->fileHelper->normalizePath($path);
+ } elseif (!file_exists($path)) {
+ throw new PathNotFoundException($path);
} else {
$finder = new Finder();
$finder->followLinks();
@@ -53,9 +45,7 @@ public function findFiles(array $paths): FileFinderResult
}
}
- $files = array_values(array_filter($files, function (string $file): bool {
- return !$this->fileExcluder->isExcludedFromAnalysing($file);
- }));
+ $files = array_values(array_filter($files, fn (string $file): bool => !$this->fileExcluder->isExcludedFromAnalysing($file)));
return new FileFinderResult($files, $onlyFiles);
}
diff --git a/src/File/FileFinderResult.php b/src/File/FileFinderResult.php
index db239b00cd..d88bcbb84c 100644
--- a/src/File/FileFinderResult.php
+++ b/src/File/FileFinderResult.php
@@ -5,19 +5,11 @@
class FileFinderResult
{
- /** @var string[] */
- private array $files;
-
- private bool $onlyFiles;
-
/**
* @param string[] $files
- * @param bool $onlyFiles
*/
- public function __construct(array $files, bool $onlyFiles)
+ public function __construct(private array $files, private bool $onlyFiles)
{
- $this->files = $files;
- $this->onlyFiles = $onlyFiles;
}
/**
diff --git a/src/File/FileHelper.php b/src/File/FileHelper.php
index b351f1e11f..06bfcdcf11 100644
--- a/src/File/FileHelper.php
+++ b/src/File/FileHelper.php
@@ -3,6 +3,17 @@
namespace PHPStan\File;
use Nette\Utils\Strings;
+use function array_pop;
+use function explode;
+use function implode;
+use function ltrim;
+use function rtrim;
+use function str_replace;
+use function str_starts_with;
+use function strpos;
+use function substr;
+use function trim;
+use const DIRECTORY_SEPARATOR;
class FileHelper
{
@@ -19,6 +30,7 @@ public function getWorkingDirectory(): string
return $this->workingDirectory;
}
+ /** @api */
public function absolutizePath(string $path): string
{
if (DIRECTORY_SEPARATOR === '/') {
@@ -30,16 +42,23 @@ public function absolutizePath(string $path): string
return $path;
}
}
- if (\Nette\Utils\Strings::startsWith($path, 'phar://')) {
+ if (str_starts_with($path, 'phar://')) {
return $path;
}
return rtrim($this->getWorkingDirectory(), '/\\') . DIRECTORY_SEPARATOR . ltrim($path, '/\\');
}
+ /** @api */
public function normalizePath(string $originalPath, string $directorySeparator = DIRECTORY_SEPARATOR): string
{
- $matches = \Nette\Utils\Strings::match($originalPath, '~^([a-z]+)\\:\\/\\/(.+)~');
+ $isLocalPath = $originalPath !== '' && $originalPath[0] === '/';
+
+ $matches = null;
+ if (!$isLocalPath) {
+ $matches = Strings::match($originalPath, '~^([a-z]+)\\:\\/\\/(.+)~');
+ }
+
if ($matches !== null) {
[, $scheme, $path] = $matches;
} else {
@@ -47,8 +66,7 @@ public function normalizePath(string $originalPath, string $directorySeparator =
$path = $originalPath;
}
- $path = str_replace('\\', '/', $path);
- $path = Strings::replace($path, '~/{2,}~', '/');
+ $path = str_replace(['\\', '//', '///', '////'], '/', $path);
$pathRoot = strpos($path, '/') === 0 ? $directorySeparator : '';
$pathParts = explode('/', trim($path, '/'));
diff --git a/src/File/FileMonitor.php b/src/File/FileMonitor.php
index c25856a140..52d91b0729 100644
--- a/src/File/FileMonitor.php
+++ b/src/File/FileMonitor.php
@@ -2,23 +2,23 @@
namespace PHPStan\File;
+use PHPStan\ShouldNotHappenException;
use function array_key_exists;
+use function array_keys;
+use function count;
+use function sha1;
class FileMonitor
{
- /** @var FileFinder */
- private $fileFinder;
-
/** @var array|null */
- private $fileHashes;
+ private ?array $fileHashes = null;
/** @var array|null */
- private $paths;
+ private ?array $paths = null;
- public function __construct(FileFinder $fileFinder)
+ public function __construct(private FileFinder $fileFinder)
{
- $this->fileFinder = $fileFinder;
}
/**
@@ -39,7 +39,7 @@ public function initialize(array $paths): void
public function getChanges(): FileMonitorResult
{
if ($this->fileHashes === null || $this->paths === null) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$finderResult = $this->fileFinder->findFiles($this->paths);
$oldFileHashes = $this->fileHashes;
@@ -75,7 +75,7 @@ public function getChanges(): FileMonitorResult
$newFiles,
$changedFiles,
$deletedFiles,
- count($fileHashes)
+ count($fileHashes),
);
}
diff --git a/src/File/FileMonitorResult.php b/src/File/FileMonitorResult.php
index 5e58357941..9da77e0bcc 100644
--- a/src/File/FileMonitorResult.php
+++ b/src/File/FileMonitorResult.php
@@ -2,38 +2,23 @@
namespace PHPStan\File;
+use function count;
+
class FileMonitorResult
{
- /** @var string[] */
- private $newFiles;
-
- /** @var string[] */
- private $changedFiles;
-
- /** @var string[] */
- private $deletedFiles;
-
- /** @var int */
- private $totalFilesCount;
-
/**
* @param string[] $newFiles
* @param string[] $changedFiles
* @param string[] $deletedFiles
- * @param int $totalFilesCount
*/
public function __construct(
- array $newFiles,
- array $changedFiles,
- array $deletedFiles,
- int $totalFilesCount
+ private array $newFiles,
+ private array $changedFiles,
+ private array $deletedFiles,
+ private int $totalFilesCount,
)
{
- $this->newFiles = $newFiles;
- $this->changedFiles = $changedFiles;
- $this->deletedFiles = $deletedFiles;
- $this->totalFilesCount = $totalFilesCount;
}
public function hasAnyChanges(): bool
diff --git a/src/File/FileReader.php b/src/File/FileReader.php
index 5a0c5570fc..5bc5220ae7 100644
--- a/src/File/FileReader.php
+++ b/src/File/FileReader.php
@@ -3,6 +3,7 @@
namespace PHPStan\File;
use function file_get_contents;
+use function is_file;
class FileReader
{
@@ -10,11 +11,11 @@ class FileReader
public static function read(string $fileName): string
{
if (!is_file($fileName)) {
- throw new \PHPStan\File\CouldNotReadFileException($fileName);
+ throw new CouldNotReadFileException($fileName);
}
$contents = @file_get_contents($fileName);
if ($contents === false) {
- throw new \PHPStan\File\CouldNotReadFileException($fileName);
+ throw new CouldNotReadFileException($fileName);
}
return $contents;
diff --git a/src/File/FileWriter.php b/src/File/FileWriter.php
index 2c92623552..2a40c35bb1 100644
--- a/src/File/FileWriter.php
+++ b/src/File/FileWriter.php
@@ -2,6 +2,9 @@
namespace PHPStan\File;
+use function error_get_last;
+use function file_put_contents;
+
class FileWriter
{
@@ -11,9 +14,9 @@ public static function write(string $fileName, string $contents): void
if ($success === false) {
$error = error_get_last();
- throw new \PHPStan\File\CouldNotWriteFileException(
+ throw new CouldNotWriteFileException(
$fileName,
- $error !== null ? $error['message'] : 'unknown cause'
+ $error !== null ? $error['message'] : 'unknown cause',
);
}
}
diff --git a/src/File/FuzzyRelativePathHelper.php b/src/File/FuzzyRelativePathHelper.php
index 882cc91c99..47452d60a3 100644
--- a/src/File/FuzzyRelativePathHelper.php
+++ b/src/File/FuzzyRelativePathHelper.php
@@ -2,29 +2,36 @@
namespace PHPStan\File;
+use function count;
+use function explode;
+use function implode;
+use function in_array;
+use function ltrim;
+use function realpath;
+use function str_ends_with;
+use function strlen;
+use function strpos;
+use function substr;
+use const DIRECTORY_SEPARATOR;
+
class FuzzyRelativePathHelper implements RelativePathHelper
{
- private RelativePathHelper $fallbackRelativePathHelper;
-
private string $directorySeparator;
private ?string $pathToTrim = null;
/**
- * @param RelativePathHelper $fallbackRelativePathHelper
- * @param string $currentWorkingDirectory
* @param string[] $analysedPaths
- * @param string|null $directorySeparator
+ * @param non-empty-string|null $directorySeparator
*/
public function __construct(
- RelativePathHelper $fallbackRelativePathHelper,
+ private RelativePathHelper $fallbackRelativePathHelper,
string $currentWorkingDirectory,
array $analysedPaths,
- ?string $directorySeparator = null
+ ?string $directorySeparator = null,
)
{
- $this->fallbackRelativePathHelper = $fallbackRelativePathHelper;
if ($directorySeparator === null) {
$directorySeparator = DIRECTORY_SEPARATOR;
}
@@ -64,7 +71,7 @@ public function __construct(
$pathArray = explode($directorySeparator, $path);
$pathTempParts = [];
foreach ($pathArray as $i => $pathPart) {
- if (\Nette\Utils\Strings::endsWith($pathPart, '.php')) {
+ if (str_ends_with($pathPart, '.php')) {
continue;
}
if (!isset($pathToTrimArray[$i])) {
diff --git a/src/File/ParentDirectoryRelativePathHelper.php b/src/File/ParentDirectoryRelativePathHelper.php
index 306f8f0dbf..218d4597fc 100644
--- a/src/File/ParentDirectoryRelativePathHelper.php
+++ b/src/File/ParentDirectoryRelativePathHelper.php
@@ -2,17 +2,23 @@
namespace PHPStan\File;
+use PHPStan\ShouldNotHappenException;
+use function array_fill;
+use function array_merge;
use function array_slice;
+use function count;
+use function explode;
+use function implode;
use function str_replace;
+use function strpos;
+use function substr;
+use function trim;
class ParentDirectoryRelativePathHelper implements RelativePathHelper
{
- private string $parentDirectory;
-
- public function __construct(string $parentDirectory)
+ public function __construct(private string $parentDirectory)
{
- $this->parentDirectory = $parentDirectory;
}
public function getRelativePath(string $filename): string
@@ -21,7 +27,6 @@ public function getRelativePath(string $filename): string
}
/**
- * @param string $filename
* @return string[]
*/
public function getFilenameParts(string $filename): array
@@ -55,6 +60,10 @@ public function getFilenameParts(string $filename): array
$dotsCount = $parentPartsCount - $i;
+ if ($dotsCount < 0) {
+ throw new ShouldNotHappenException();
+ }
+
return array_merge(array_fill(0, $dotsCount, '..'), array_slice($filenameParts, $i));
}
diff --git a/src/File/PathNotFoundException.php b/src/File/PathNotFoundException.php
index 185bcf459c..fb8f4dd191 100644
--- a/src/File/PathNotFoundException.php
+++ b/src/File/PathNotFoundException.php
@@ -2,15 +2,15 @@
namespace PHPStan\File;
-class PathNotFoundException extends \Exception
-{
+use Exception;
+use function sprintf;
- private string $path;
+class PathNotFoundException extends Exception
+{
- public function __construct(string $path)
+ public function __construct(private string $path)
{
parent::__construct(sprintf('Path %s does not exist', $path));
- $this->path = $path;
}
public function getPath(): string
diff --git a/src/File/SimpleRelativePathHelper.php b/src/File/SimpleRelativePathHelper.php
index f71341deeb..14181895ca 100644
--- a/src/File/SimpleRelativePathHelper.php
+++ b/src/File/SimpleRelativePathHelper.php
@@ -2,23 +2,25 @@
namespace PHPStan\File;
+use function str_replace;
+use function strlen;
+use function strpos;
+use function substr;
+
class SimpleRelativePathHelper implements RelativePathHelper
{
- private string $currentWorkingDirectory;
-
- public function __construct(string $currentWorkingDirectory)
+ public function __construct(private string $currentWorkingDirectory)
{
- $this->currentWorkingDirectory = $currentWorkingDirectory;
}
public function getRelativePath(string $filename): string
{
if ($this->currentWorkingDirectory !== '' && strpos($filename, $this->currentWorkingDirectory) === 0) {
- return substr($filename, strlen($this->currentWorkingDirectory) + 1);
+ return str_replace('\\', '/', substr($filename, strlen($this->currentWorkingDirectory) + 1));
}
- return $filename;
+ return str_replace('\\', '/', $filename);
}
}
diff --git a/src/Internal/BytesHelper.php b/src/Internal/BytesHelper.php
index 73561e0de5..48a852c0a3 100644
--- a/src/Internal/BytesHelper.php
+++ b/src/Internal/BytesHelper.php
@@ -2,6 +2,11 @@
namespace PHPStan\Internal;
+use PHPStan\ShouldNotHappenException;
+use function abs;
+use function end;
+use function round;
+
class BytesHelper
{
@@ -17,7 +22,7 @@ public static function bytes(int $bytes): string
}
if (!isset($unit)) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
return round($bytes, 2) . ' ' . $unit;
diff --git a/src/Internal/ContainerDynamicReturnTypeExtension.php b/src/Internal/ContainerDynamicReturnTypeExtension.php
index 58703f78c0..afb196dbbd 100644
--- a/src/Internal/ContainerDynamicReturnTypeExtension.php
+++ b/src/Internal/ContainerDynamicReturnTypeExtension.php
@@ -9,11 +9,14 @@
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantStringType;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
+use function count;
+use function in_array;
-class ContainerDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension
+class ContainerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
public function getClass(): string
@@ -30,17 +33,17 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
- if (count($methodCall->args) === 0) {
+ if (count($methodCall->getArgs()) === 0) {
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
}
- $argType = $scope->getType($methodCall->args[0]->value);
+ $argType = $scope->getType($methodCall->getArgs()[0]->value);
if (!$argType instanceof ConstantStringType) {
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
}
$type = new ObjectType($argType->getValue());
- if ($methodReflection->getName() === 'getByType' && count($methodCall->args) >= 2) {
- $argType = $scope->getType($methodCall->args[1]->value);
+ if ($methodReflection->getName() === 'getByType' && count($methodCall->getArgs()) >= 2) {
+ $argType = $scope->getType($methodCall->getArgs()[1]->value);
if ($argType instanceof ConstantBooleanType && $argType->getValue()) {
$type = TypeCombinator::addNull($type);
}
diff --git a/src/Internal/ScopeIsInClassTypeSpecifyingExtension.php b/src/Internal/ScopeIsInClassTypeSpecifyingExtension.php
index e90103ffd6..3e63618ff6 100644
--- a/src/Internal/ScopeIsInClassTypeSpecifyingExtension.php
+++ b/src/Internal/ScopeIsInClassTypeSpecifyingExtension.php
@@ -18,23 +18,14 @@
class ScopeIsInClassTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
{
- private string $isInMethodName;
-
- private string $removeNullMethodName;
-
- private \PHPStan\Reflection\ReflectionProvider $reflectionProvider;
-
- private \PHPStan\Analyser\TypeSpecifier $typeSpecifier;
+ private TypeSpecifier $typeSpecifier;
public function __construct(
- string $isInMethodName,
- string $removeNullMethodName,
- ReflectionProvider $reflectionProvider
+ private string $isInMethodName,
+ private string $removeNullMethodName,
+ private ReflectionProvider $reflectionProvider,
)
{
- $this->isInMethodName = $isInMethodName;
- $this->removeNullMethodName = $removeNullMethodName;
- $this->reflectionProvider = $reflectionProvider;
}
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
@@ -50,7 +41,7 @@ public function getClass(): string
public function isMethodSupported(
MethodReflection $methodReflection,
MethodCall $node,
- TypeSpecifierContext $context
+ TypeSpecifierContext $context,
): bool
{
return $methodReflection->getName() === $this->isInMethodName
@@ -61,7 +52,7 @@ public function specifyTypes(
MethodReflection $methodReflection,
MethodCall $node,
Scope $scope,
- TypeSpecifierContext $context
+ TypeSpecifierContext $context,
): SpecifiedTypes
{
$scopeClass = $this->reflectionProvider->getClass(Scope::class);
@@ -72,11 +63,11 @@ public function specifyTypes(
return $this->typeSpecifier->create(
new MethodCall($node->var, $this->removeNullMethodName),
TypeCombinator::removeNull(
- ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType()
+ ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType(),
),
$context,
false,
- $scope
+ $scope,
);
}
diff --git a/src/Internal/SprintfHelper.php b/src/Internal/SprintfHelper.php
new file mode 100644
index 0000000000..30d08500fa
--- /dev/null
+++ b/src/Internal/SprintfHelper.php
@@ -0,0 +1,15 @@
+args) < 2) {
+ if (count($methodCall->getArgs()) < 2) {
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
}
- $getterClosureType = $scope->getType($methodCall->args[1]->value);
+ $getterClosureType = $scope->getType($methodCall->getArgs()[1]->value);
return ParametersAcceptorSelector::selectSingle($getterClosureType->getCallableParametersAcceptors($scope))->getReturnType();
}
diff --git a/src/Node/BooleanAndNode.php b/src/Node/BooleanAndNode.php
index ee0f25b3b4..c4b750de00 100644
--- a/src/Node/BooleanAndNode.php
+++ b/src/Node/BooleanAndNode.php
@@ -11,20 +11,9 @@
class BooleanAndNode extends NodeAbstract implements VirtualNode
{
- /** @var BooleanAnd|LogicalAnd */
- private $originalNode;
-
- private Scope $rightScope;
-
- /**
- * @param BooleanAnd|LogicalAnd $originalNode
- * @param Scope $rightScope
- */
- public function __construct($originalNode, Scope $rightScope)
+ public function __construct(private BooleanAnd|LogicalAnd $originalNode, private Scope $rightScope)
{
parent::__construct($originalNode->getAttributes());
- $this->originalNode = $originalNode;
- $this->rightScope = $rightScope;
}
/**
diff --git a/src/Node/BooleanOrNode.php b/src/Node/BooleanOrNode.php
index 4a43adac5d..fb0e04134a 100644
--- a/src/Node/BooleanOrNode.php
+++ b/src/Node/BooleanOrNode.php
@@ -11,20 +11,9 @@
class BooleanOrNode extends NodeAbstract implements VirtualNode
{
- /** @var BooleanOr|LogicalOr */
- private $originalNode;
-
- private Scope $rightScope;
-
- /**
- * @param BooleanOr|LogicalOr $originalNode
- * @param Scope $rightScope
- */
- public function __construct($originalNode, Scope $rightScope)
+ public function __construct(private BooleanOr|LogicalOr $originalNode, private Scope $rightScope)
{
parent::__construct($originalNode->getAttributes());
- $this->originalNode = $originalNode;
- $this->rightScope = $rightScope;
}
/**
diff --git a/src/Node/BreaklessWhileLoopNode.php b/src/Node/BreaklessWhileLoopNode.php
new file mode 100644
index 0000000000..8a824778bc
--- /dev/null
+++ b/src/Node/BreaklessWhileLoopNode.php
@@ -0,0 +1,47 @@
+getAttributes());
+ }
+
+ public function getOriginalNode(): While_
+ {
+ return $this->originalNode;
+ }
+
+ /**
+ * @return StatementExitPoint[]
+ */
+ public function getExitPoints(): array
+ {
+ return $this->exitPoints;
+ }
+
+ public function getType(): string
+ {
+ return 'PHPStan_Node_BreaklessWhileLoop';
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getSubNodeNames(): array
+ {
+ return [];
+ }
+
+}
diff --git a/src/Node/CatchWithUnthrownExceptionNode.php b/src/Node/CatchWithUnthrownExceptionNode.php
index 01ad10629b..007fa83218 100644
--- a/src/Node/CatchWithUnthrownExceptionNode.php
+++ b/src/Node/CatchWithUnthrownExceptionNode.php
@@ -10,15 +10,9 @@
class CatchWithUnthrownExceptionNode extends NodeAbstract implements VirtualNode
{
- private Catch_ $originalNode;
-
- private Type $caughtType;
-
- public function __construct(Catch_ $originalNode, Type $caughtType)
+ public function __construct(private Catch_ $originalNode, private Type $caughtType, private Type $originalCaughtType)
{
parent::__construct($originalNode->getAttributes());
- $this->originalNode = $originalNode;
- $this->caughtType = $caughtType;
}
public function getOriginalNode(): Catch_
@@ -31,6 +25,11 @@ public function getCaughtType(): Type
return $this->caughtType;
}
+ public function getOriginalCaughtType(): Type
+ {
+ return $this->originalCaughtType;
+ }
+
public function getType(): string
{
return 'PHPStan_Node_CatchWithUnthrownExceptionNode';
diff --git a/src/Node/ClassConstantsNode.php b/src/Node/ClassConstantsNode.php
index 3ccb91441b..4b725b6bcd 100644
--- a/src/Node/ClassConstantsNode.php
+++ b/src/Node/ClassConstantsNode.php
@@ -11,25 +11,13 @@
class ClassConstantsNode extends NodeAbstract implements VirtualNode
{
- private ClassLike $class;
-
- /** @var ClassConst[] */
- private array $constants;
-
- /** @var ClassConstantFetch[] */
- private array $fetches;
-
/**
- * @param ClassLike $class
* @param ClassConst[] $constants
* @param ClassConstantFetch[] $fetches
*/
- public function __construct(ClassLike $class, array $constants, array $fetches)
+ public function __construct(private ClassLike $class, private array $constants, private array $fetches)
{
parent::__construct($class->getAttributes());
- $this->class = $class;
- $this->constants = $constants;
- $this->fetches = $fetches;
}
public function getClass(): ClassLike
diff --git a/src/Node/ClassMethodsNode.php b/src/Node/ClassMethodsNode.php
index 2f029cea34..545441e9b4 100644
--- a/src/Node/ClassMethodsNode.php
+++ b/src/Node/ClassMethodsNode.php
@@ -11,25 +11,13 @@
class ClassMethodsNode extends NodeAbstract implements VirtualNode
{
- private ClassLike $class;
-
- /** @var ClassMethod[] */
- private array $methods;
-
- /** @var array */
- private array $methodCalls;
-
/**
- * @param ClassLike $class
* @param ClassMethod[] $methods
* @param array $methodCalls
*/
- public function __construct(ClassLike $class, array $methods, array $methodCalls)
+ public function __construct(private ClassLike $class, private array $methods, private array $methodCalls)
{
parent::__construct($class->getAttributes());
- $this->class = $class;
- $this->methods = $methods;
- $this->methodCalls = $methodCalls;
}
public function getClass(): ClassLike
diff --git a/src/Node/ClassPropertiesNode.php b/src/Node/ClassPropertiesNode.php
index 3f0dde0cd2..feefd78061 100644
--- a/src/Node/ClassPropertiesNode.php
+++ b/src/Node/ClassPropertiesNode.php
@@ -2,6 +2,7 @@
namespace PHPStan\Node;
+use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Identifier;
@@ -15,37 +16,26 @@
use PHPStan\Node\Property\PropertyWrite;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Rules\Properties\ReadWritePropertiesExtension;
+use PHPStan\ShouldNotHappenException;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
+use function array_key_exists;
+use function array_keys;
+use function count;
+use function in_array;
/** @api */
class ClassPropertiesNode extends NodeAbstract implements VirtualNode
{
- private ClassLike $class;
-
- /** @var ClassPropertyNode[] */
- private array $properties;
-
- /** @var array */
- private array $propertyUsages;
-
- /** @var array */
- private array $methodCalls;
-
/**
- * @param ClassLike $class
* @param ClassPropertyNode[] $properties
* @param array $propertyUsages
* @param array $methodCalls
*/
- public function __construct(ClassLike $class, array $properties, array $propertyUsages, array $methodCalls)
+ public function __construct(private ClassLike $class, private array $properties, private array $propertyUsages, private array $methodCalls)
{
parent::__construct($class->getAttributes());
- $this->class = $class;
- $this->properties = $properties;
- $this->propertyUsages = $propertyUsages;
- $this->methodCalls = $methodCalls;
}
public function getClass(): ClassLike
@@ -85,19 +75,19 @@ public function getSubNodeNames(): array
/**
* @param string[] $constructors
* @param ReadWritePropertiesExtension[] $extensions
- * @return array{array, array}
+ * @return array{array, array, array}
*/
public function getUninitializedProperties(
Scope $scope,
array $constructors,
- array $extensions
+ array $extensions,
): array
{
if (!$this->getClass() instanceof Class_) {
- return [[], []];
+ return [[], [], []];
}
if (!$scope->isInClass()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
$classReflection = $scope->getClassReflection();
@@ -130,11 +120,13 @@ public function getUninitializedProperties(
}
if ($constructors === []) {
- return [$properties, []];
+ return [$properties, [], []];
}
$classType = new ObjectType($scope->getClassReflection()->getName());
$methodsCalledFromConstructor = $this->getMethodsCalledFromConstructor($classType, $this->methodCalls, $constructors);
$prematureAccess = [];
+ $additionalAssigns = [];
+ $originalProperties = $properties;
foreach ($this->getPropertyUsages() as $usage) {
$fetch = $usage->getFetch();
if (!$fetch instanceof PropertyFetch) {
@@ -159,9 +151,6 @@ public function getUninitializedProperties(
continue;
}
$propertyName = $fetch->name->toString();
- if (!array_key_exists($propertyName, $properties)) {
- continue;
- }
$fetchedOnType = $usageScope->getType($fetch->var);
if ($classType->isSuperTypeOf($fetchedOnType)->no()) {
continue;
@@ -171,11 +160,20 @@ public function getUninitializedProperties(
}
if ($usage instanceof PropertyWrite) {
- unset($properties[$propertyName]);
+ if (array_key_exists($propertyName, $properties)) {
+ unset($properties[$propertyName]);
+ } elseif (array_key_exists($propertyName, $originalProperties)) {
+ $additionalAssigns[] = [
+ $propertyName,
+ $fetch->getLine(),
+ $originalProperties[$propertyName],
+ ];
+ }
} elseif (array_key_exists($propertyName, $properties)) {
$prematureAccess[] = [
$propertyName,
$fetch->getLine(),
+ $properties[$propertyName],
];
}
}
@@ -183,11 +181,11 @@ public function getUninitializedProperties(
return [
$properties,
$prematureAccess,
+ $additionalAssigns,
];
}
/**
- * @param ObjectType $classType
* @param MethodCall[] $methodCalls
* @param string[] $methods
* @return string[]
@@ -195,7 +193,7 @@ public function getUninitializedProperties(
private function getMethodsCalledFromConstructor(
ObjectType $classType,
array $methodCalls,
- array $methods
+ array $methods,
): array
{
$originalCount = count($methods);
@@ -208,7 +206,7 @@ private function getMethodsCalledFromConstructor(
continue;
}
$callScope = $methodCall->getScope();
- if ($methodCallNode instanceof \PhpParser\Node\Expr\MethodCall) {
+ if ($methodCallNode instanceof Node\Expr\MethodCall) {
$calledOnType = $callScope->getType($methodCallNode->var);
} else {
if (!$methodCallNode->class instanceof Name) {
diff --git a/src/Node/ClassPropertyNode.php b/src/Node/ClassPropertyNode.php
index 36bde885b1..b45d2fd931 100644
--- a/src/Node/ClassPropertyNode.php
+++ b/src/Node/ClassPropertyNode.php
@@ -6,51 +6,24 @@
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
-use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\Class_;
-use PhpParser\Node\UnionType;
use PhpParser\NodeAbstract;
/** @api */
class ClassPropertyNode extends NodeAbstract implements VirtualNode
{
- private string $name;
-
- private int $flags;
-
- /** @var Identifier|Name|NullableType|UnionType|null */
- private $type;
-
- private ?Expr $default;
-
- private ?string $phpDoc;
-
- private bool $isPromoted;
-
- /**
- * @param int $flags
- * @param Identifier|Name|NullableType|UnionType|null $type
- * @param string $name
- * @param Expr|null $default
- */
public function __construct(
- string $name,
- int $flags,
- $type,
- ?Expr $default,
- ?string $phpDoc,
- bool $isPromoted,
- Node $originalNode
+ private string $name,
+ private int $flags,
+ private Identifier|Name|Node\ComplexType|null $type,
+ private ?Expr $default,
+ private ?string $phpDoc,
+ private bool $isPromoted,
+ Node $originalNode,
)
{
parent::__construct($originalNode->getAttributes());
- $this->name = $name;
- $this->flags = $flags;
- $this->type = $type;
- $this->default = $default;
- $this->isPromoted = $isPromoted;
- $this->phpDoc = $phpDoc;
}
public function getName(): string
@@ -99,8 +72,13 @@ public function isStatic(): bool
return (bool) ($this->flags & Class_::MODIFIER_STATIC);
}
+ public function isReadOnly(): bool
+ {
+ return (bool) ($this->flags & Class_::MODIFIER_READONLY);
+ }
+
/**
- * @return Identifier|Name|NullableType|UnionType|null
+ * @return Identifier|Name|Node\ComplexType|null
*/
public function getNativeType()
{
diff --git a/src/Node/ClassStatementsGatherer.php b/src/Node/ClassStatementsGatherer.php
index 14baad972b..80f5a3e3d6 100644
--- a/src/Node/ClassStatementsGatherer.php
+++ b/src/Node/ClassStatementsGatherer.php
@@ -2,6 +2,7 @@
namespace PHPStan\Node;
+use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
@@ -15,19 +16,19 @@
use PHPStan\Node\Property\PropertyRead;
use PHPStan\Node\Property\PropertyWrite;
use PHPStan\Reflection\ClassReflection;
+use PHPStan\ShouldNotHappenException;
+use function count;
class ClassStatementsGatherer
{
- private ClassReflection $classReflection;
-
- /** @var callable(\PhpParser\Node $node, Scope $scope): void */
+ /** @var callable(Node $node, Scope $scope): void */
private $nodeCallback;
/** @var ClassPropertyNode[] */
private array $properties = [];
- /** @var \PhpParser\Node\Stmt\ClassMethod[] */
+ /** @var Node\Stmt\ClassMethod[] */
private array $methods = [];
/** @var \PHPStan\Node\Method\MethodCall[] */
@@ -36,22 +37,20 @@ class ClassStatementsGatherer
/** @var array */
private array $propertyUsages = [];
- /** @var \PhpParser\Node\Stmt\ClassConst[] */
+ /** @var Node\Stmt\ClassConst[] */
private array $constants = [];
/** @var ClassConstantFetch[] */
private array $constantFetches = [];
/**
- * @param ClassReflection $classReflection
- * @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
+ * @param callable(Node $node, Scope $scope): void $nodeCallback
*/
public function __construct(
- ClassReflection $classReflection,
- callable $nodeCallback
+ private ClassReflection $classReflection,
+ callable $nodeCallback,
)
{
- $this->classReflection = $classReflection;
$this->nodeCallback = $nodeCallback;
}
@@ -64,7 +63,7 @@ public function getProperties(): array
}
/**
- * @return \PhpParser\Node\Stmt\ClassMethod[]
+ * @return Node\Stmt\ClassMethod[]
*/
public function getMethods(): array
{
@@ -88,7 +87,7 @@ public function getPropertyUsages(): array
}
/**
- * @return \PhpParser\Node\Stmt\ClassConst[]
+ * @return Node\Stmt\ClassConst[]
*/
public function getConstants(): array
{
@@ -103,17 +102,17 @@ public function getConstantFetches(): array
return $this->constantFetches;
}
- public function __invoke(\PhpParser\Node $node, Scope $scope): void
+ public function __invoke(Node $node, Scope $scope): void
{
$nodeCallback = $this->nodeCallback;
$nodeCallback($node, $scope);
$this->gatherNodes($node, $scope);
}
- private function gatherNodes(\PhpParser\Node $node, Scope $scope): void
+ private function gatherNodes(Node $node, Scope $scope): void
{
if (!$scope->isInClass()) {
- throw new \PHPStan\ShouldNotHappenException();
+ throw new ShouldNotHappenException();
}
if ($scope->getClassReflection()->getName() !== $this->classReflection->getName()) {
return;
@@ -123,16 +122,16 @@ private function gatherNodes(\PhpParser\Node $node, Scope $scope): void
if ($node->isPromoted()) {
$this->propertyUsages[] = new PropertyWrite(
new PropertyFetch(new Expr\Variable('this'), new Identifier($node->getName())),
- $scope
+ $scope,
);
}
return;
}
- if ($node instanceof \PhpParser\Node\Stmt\ClassMethod && !$scope->isInTrait()) {
+ if ($node instanceof Node\Stmt\ClassMethod && !$scope->isInTrait()) {
$this->methods[] = $node;
return;
}
- if ($node instanceof \PhpParser\Node\Stmt\ClassConst) {
+ if ($node instanceof Node\Stmt\ClassConst) {
$this->constants[] = $node;
return;
}
@@ -140,6 +139,10 @@ private function gatherNodes(\PhpParser\Node $node, Scope $scope): void
$this->methodCalls[] = new \PHPStan\Node\Method\MethodCall($node, $scope);
return;
}
+ if ($node instanceof MethodCallableNode || $node instanceof StaticMethodCallableNode) {
+ $this->methodCalls[] = new \PHPStan\Node\Method\MethodCall($node->getOriginalNode(), $scope);
+ return;
+ }
if ($node instanceof Array_ && count($node->items) === 2) {
$this->methodCalls[] = new \PHPStan\Node\Method\MethodCall($node, $scope);
return;
@@ -148,6 +151,10 @@ private function gatherNodes(\PhpParser\Node $node, Scope $scope): void
$this->constantFetches[] = new ClassConstantFetch($node, $scope);
return;
}
+ if ($node instanceof PropertyAssignNode) {
+ $this->propertyUsages[] = new PropertyWrite($node->getPropertyFetch(), $scope);
+ return;
+ }
if (!$node instanceof Expr) {
return;
}
@@ -155,10 +162,25 @@ private function gatherNodes(\PhpParser\Node $node, Scope $scope): void
$this->gatherNodes($node->var, $scope);
return;
}
- if ($node instanceof \PhpParser\Node\Scalar\EncapsedStringPart) {
+ if ($node instanceof Expr\AssignRef) {
+ if (!$node->expr instanceof PropertyFetch && !$node->expr instanceof StaticPropertyFetch) {
+ $this->gatherNodes($node->expr, $scope);
+ return;
+ }
+
+ $this->propertyUsages[] = new PropertyRead($node->expr, $scope);
+ $this->propertyUsages[] = new PropertyWrite($node->expr, $scope);
+ return;
+ }
+ if ($node instanceof Node\Scalar\EncapsedStringPart) {
return;
}
+
$inAssign = $scope->isInExpressionAssign($node);
+ if ($inAssign) {
+ return;
+ }
+
while ($node instanceof ArrayDimFetch) {
$node = $node->var;
}
@@ -166,11 +188,7 @@ private function gatherNodes(\PhpParser\Node $node, Scope $scope): void
return;
}
- if ($inAssign) {
- $this->propertyUsages[] = new PropertyWrite($node, $scope);
- } else {
- $this->propertyUsages[] = new PropertyRead($node, $scope);
- }
+ $this->propertyUsages[] = new PropertyRead($node, $scope);
}
}
diff --git a/src/Node/ClosureReturnStatementsNode.php b/src/Node/ClosureReturnStatementsNode.php
index 07b0a67710..04c229b438 100644
--- a/src/Node/ClosureReturnStatementsNode.php
+++ b/src/Node/ClosureReturnStatementsNode.php
@@ -2,6 +2,7 @@
namespace PHPStan\Node;
+use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Expr\YieldFrom;
@@ -12,34 +13,21 @@
class ClosureReturnStatementsNode extends NodeAbstract implements ReturnStatementsNode
{
- private \PhpParser\Node\Expr\Closure $closureExpr;
-
- /** @var \PHPStan\Node\ReturnStatement[] */
- private array $returnStatements;
-
- /** @var array