From d39013324d5558925c6194d4df7afaf8e40109ac Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 20:30:57 +0200 Subject: [PATCH 01/39] #10026 added GitHub CI for unit tests with PHP 8.2 and 8.3 --- .github/workflows/ci.yml | 132 +++++++++++++++++++++++++++++++++++++-- composer.json | 2 +- 2 files changed, 129 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0693fb9c4a0..1cfb5800818 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,8 +69,8 @@ jobs: echo "count=$(php -r 'echo json_encode([ ${{ env.CHUNK_COUNT }} ]);')" >> $GITHUB_OUTPUT echo "chunks=$(php -r 'echo json_encode(range(1, ${{ env.CHUNK_COUNT }} ));')" >> $GITHUB_OUTPUT - tests: - name: "Unit Tests - ${{ matrix.chunk }}" + tests_81: + name: "Unit Tests PHP 8.1 - ${{ matrix.chunk }}" runs-on: ubuntu-latest needs: @@ -88,7 +88,7 @@ jobs: PARALLEL_PROCESSES: 5 steps: - - name: Set up PHP + - name: Set up PHP 8.1 uses: shivammathur/setup-php@v2 with: php-version: '8.1' @@ -128,5 +128,129 @@ jobs: env: COMPOSER_ROOT_VERSION: dev-master - - name: Run unit tests + - name: Run unit tests php 8.1 + run: bin/tests-github-actions.sh + + tests_82: + name: "Unit Tests PHP 8.2 - ${{ matrix.chunk }}" + + runs-on: ubuntu-latest + needs: + - chunk-matrix + + strategy: + fail-fast: false + matrix: + count: ${{ fromJson(needs.chunk-matrix.outputs.count) }} + chunk: ${{ fromJson(needs.chunk-matrix.outputs.chunks) }} + + env: + CHUNK_COUNT: "${{ matrix.count }}" + CHUNK_NUMBER: "${{ matrix.chunk }}" + PARALLEL_PROCESSES: 5 + + steps: + - name: Set up PHP 8.2 + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=function, opcache.jit_buffer_size=512M + tools: composer:v2 + coverage: none + extensions: none, curl, dom, filter, intl, json, libxml, mbstring, opcache, openssl, pcre, phar, reflection, simplexml, spl, tokenizer, xml, xmlwriter + env: + fail-fast: true + + - uses: actions/checkout@v3 + + - name: Get Composer Cache Directories + id: composer-cache + run: | + echo "files_cache=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + echo "vcs_cache=$(composer config cache-vcs-dir)" >> $GITHUB_OUTPUT + + - name: Generate composer.lock + run: | + composer update --no-install + env: + COMPOSER_ROOT_VERSION: dev-master + + - name: Cache composer cache + uses: actions/cache@v3 + with: + path: | + ${{ steps.composer-cache.outputs.files_cache }} + ${{ steps.composer-cache.outputs.vcs_cache }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Run composer install + run: composer install -o + env: + COMPOSER_ROOT_VERSION: dev-master + + - name: Run unit tests php 8.2 + run: bin/tests-github-actions.sh + + tests_83: + name: "Unit Tests PHP 8.3 - ${{ matrix.chunk }}" + + runs-on: ubuntu-latest + needs: + - chunk-matrix + + strategy: + fail-fast: false + matrix: + count: ${{ fromJson(needs.chunk-matrix.outputs.count) }} + chunk: ${{ fromJson(needs.chunk-matrix.outputs.chunks) }} + + env: + CHUNK_COUNT: "${{ matrix.count }}" + CHUNK_NUMBER: "${{ matrix.chunk }}" + PARALLEL_PROCESSES: 5 + + steps: + - name: Set up PHP 8.3 + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=function, opcache.jit_buffer_size=512M + tools: composer:v2 + coverage: none + extensions: none, curl, dom, filter, intl, json, libxml, mbstring, opcache, openssl, pcre, phar, reflection, simplexml, spl, tokenizer, xml, xmlwriter + env: + fail-fast: true + + - uses: actions/checkout@v3 + + - name: Get Composer Cache Directories + id: composer-cache + run: | + echo "files_cache=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + echo "vcs_cache=$(composer config cache-vcs-dir)" >> $GITHUB_OUTPUT + + - name: Generate composer.lock + run: | + composer update --no-install + env: + COMPOSER_ROOT_VERSION: dev-master + + - name: Cache composer cache + uses: actions/cache@v3 + with: + path: | + ${{ steps.composer-cache.outputs.files_cache }} + ${{ steps.composer-cache.outputs.vcs_cache }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Run composer install + run: composer install -o + env: + COMPOSER_ROOT_VERSION: dev-master + + - name: Run unit tests php 8.3 run: bin/tests-github-actions.sh diff --git a/composer.json b/composer.json index 7f4a9f9c8a9..7df7c97af38 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", "ext-SimpleXML": "*", "ext-ctype": "*", "ext-dom": "*", From 9eb3c7eb7d4347eb56fca64907bb618520812c3e Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 20:41:59 +0200 Subject: [PATCH 02/39] #10026 test matrix for different php versions and operating systems --- .github/workflows/ci.yml | 141 ++++----------------------------------- 1 file changed, 13 insertions(+), 128 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cfb5800818..757b8eef42f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,140 +69,25 @@ jobs: echo "count=$(php -r 'echo json_encode([ ${{ env.CHUNK_COUNT }} ]);')" >> $GITHUB_OUTPUT echo "chunks=$(php -r 'echo json_encode(range(1, ${{ env.CHUNK_COUNT }} ));')" >> $GITHUB_OUTPUT - tests_81: - name: "Unit Tests PHP 8.1 - ${{ matrix.chunk }}" + tests: + name: "Unit Tests - ${{ matrix.chunk }}" runs-on: ubuntu-latest needs: - chunk-matrix - strategy: - fail-fast: false - matrix: - count: ${{ fromJson(needs.chunk-matrix.outputs.count) }} - chunk: ${{ fromJson(needs.chunk-matrix.outputs.chunks) }} - - env: - CHUNK_COUNT: "${{ matrix.count }}" - CHUNK_NUMBER: "${{ matrix.chunk }}" - PARALLEL_PROCESSES: 5 - - steps: - - name: Set up PHP 8.1 - uses: shivammathur/setup-php@v2 - with: - php-version: '8.1' - ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=function, opcache.jit_buffer_size=512M - tools: composer:v2 - coverage: none - extensions: none, curl, dom, filter, intl, json, libxml, mbstring, opcache, openssl, pcre, phar, reflection, simplexml, spl, tokenizer, xml, xmlwriter - env: - fail-fast: true - - - uses: actions/checkout@v3 - - - name: Get Composer Cache Directories - id: composer-cache - run: | - echo "files_cache=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - echo "vcs_cache=$(composer config cache-vcs-dir)" >> $GITHUB_OUTPUT - - - name: Generate composer.lock - run: | - composer update --no-install - env: - COMPOSER_ROOT_VERSION: dev-master - - - name: Cache composer cache - uses: actions/cache@v3 - with: - path: | - ${{ steps.composer-cache.outputs.files_cache }} - ${{ steps.composer-cache.outputs.vcs_cache }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-composer- - - - name: Run composer install - run: composer install -o - env: - COMPOSER_ROOT_VERSION: dev-master - - - name: Run unit tests php 8.1 - run: bin/tests-github-actions.sh - - tests_82: - name: "Unit Tests PHP 8.2 - ${{ matrix.chunk }}" - - runs-on: ubuntu-latest - needs: - - chunk-matrix - - strategy: - fail-fast: false - matrix: - count: ${{ fromJson(needs.chunk-matrix.outputs.count) }} - chunk: ${{ fromJson(needs.chunk-matrix.outputs.chunks) }} - - env: - CHUNK_COUNT: "${{ matrix.count }}" - CHUNK_NUMBER: "${{ matrix.chunk }}" - PARALLEL_PROCESSES: 5 - - steps: - - name: Set up PHP 8.2 - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=function, opcache.jit_buffer_size=512M - tools: composer:v2 - coverage: none - extensions: none, curl, dom, filter, intl, json, libxml, mbstring, opcache, openssl, pcre, phar, reflection, simplexml, spl, tokenizer, xml, xmlwriter - env: - fail-fast: true - - - uses: actions/checkout@v3 - - - name: Get Composer Cache Directories - id: composer-cache - run: | - echo "files_cache=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - echo "vcs_cache=$(composer config cache-vcs-dir)" >> $GITHUB_OUTPUT - - - name: Generate composer.lock - run: | - composer update --no-install - env: - COMPOSER_ROOT_VERSION: dev-master - - - name: Cache composer cache - uses: actions/cache@v3 - with: - path: | - ${{ steps.composer-cache.outputs.files_cache }} - ${{ steps.composer-cache.outputs.vcs_cache }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-composer- - - - name: Run composer install - run: composer install -o - env: - COMPOSER_ROOT_VERSION: dev-master - - - name: Run unit tests php 8.2 - run: bin/tests-github-actions.sh - - tests_83: - name: "Unit Tests PHP 8.3 - ${{ matrix.chunk }}" - - runs-on: ubuntu-latest - needs: - - chunk-matrix + runs-on: ${{ matrix.operating-system }} strategy: fail-fast: false matrix: + php-version: + - "7.4" + - "8.0" + - "8.1" + - "8.2" + - "8.3" + operating-system: [ubuntu-latest, windows-latest] count: ${{ fromJson(needs.chunk-matrix.outputs.count) }} chunk: ${{ fromJson(needs.chunk-matrix.outputs.chunks) }} @@ -212,10 +97,10 @@ jobs: PARALLEL_PROCESSES: 5 steps: - - name: Set up PHP 8.3 + - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.3' + php-version: "${{ matrix.php-version }}" ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=function, opcache.jit_buffer_size=512M tools: composer:v2 coverage: none @@ -252,5 +137,5 @@ jobs: env: COMPOSER_ROOT_VERSION: dev-master - - name: Run unit tests php 8.3 + - name: Run unit tests run: bin/tests-github-actions.sh From e8586a7a35b2db6fa650dfc03b5c0e1b96569b74 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 20:42:39 +0200 Subject: [PATCH 03/39] #10026 test matrix for different php versions and operating systems --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 757b8eef42f..b47e6daa7a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,11 +72,10 @@ jobs: tests: name: "Unit Tests - ${{ matrix.chunk }}" - runs-on: ubuntu-latest + runs-on: ${{ matrix.operating-system }} needs: - chunk-matrix - runs-on: ${{ matrix.operating-system }} strategy: fail-fast: false From d0605ee765d894079d8f318958a9459e00036308 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 20:46:50 +0200 Subject: [PATCH 04/39] cleanup --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b47e6daa7a3..a0d1424fd1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: echo "chunks=$(php -r 'echo json_encode(range(1, ${{ env.CHUNK_COUNT }} ));')" >> $GITHUB_OUTPUT tests: - name: "Unit Tests - ${{ matrix.chunk }}" + name: "Unit Tests - ${{ matrix.operating-system }} ${{ matrix.php-version }} ${{ matrix.chunk }}" runs-on: ${{ matrix.operating-system }} needs: From ba4e40a9f67ec701d7e11d622460228423543cd2 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 20:47:58 +0200 Subject: [PATCH 05/39] cleanup --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0d1424fd1c..9c2608df802 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,12 +81,11 @@ jobs: fail-fast: false matrix: php-version: - - "7.4" - "8.0" - "8.1" - "8.2" - "8.3" - operating-system: [ubuntu-latest, windows-latest] + operating-system: [ubuntu-latest] count: ${{ fromJson(needs.chunk-matrix.outputs.count) }} chunk: ${{ fromJson(needs.chunk-matrix.outputs.chunks) }} From 1c55437d78e98d8e9bd41c6fd2ea8eb8faf30957 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 20:51:13 +0200 Subject: [PATCH 06/39] cleanup --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c2608df802..7902007a745 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-latest env: - CHUNK_COUNT: 8 + CHUNK_COUNT: 1 outputs: count: ${{ steps.chunk-matrix.outputs.count }} From fffbf75cf8586b0b505e3e807d4287ab61aeae22 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 21:06:35 +0200 Subject: [PATCH 07/39] cleanup --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7902007a745..91648f3c09a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-latest env: - CHUNK_COUNT: 1 + CHUNK_COUNT: 8 outputs: count: ${{ steps.chunk-matrix.outputs.count }} @@ -70,7 +70,7 @@ jobs: echo "chunks=$(php -r 'echo json_encode(range(1, ${{ env.CHUNK_COUNT }} ));')" >> $GITHUB_OUTPUT tests: - name: "Unit Tests - ${{ matrix.operating-system }} ${{ matrix.php-version }} ${{ matrix.chunk }}" + name: "Tests - ${{ matrix.php-version }} ${{ matrix.chunk }}/${{ matrix.count }} ${{ matrix.operating-system }}" runs-on: ${{ matrix.operating-system }} needs: From 0f99799fa9c3f226efafca913c99a7e2bd43234e Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 22:52:15 +0200 Subject: [PATCH 08/39] #10026 fixed AnnotationTest --- tests/AnnotationTest.php | 63 +++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index c6f0f0bc47c..b06bd217e7f 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -25,17 +25,28 @@ public function setUp(): void public function testLessSpecificImplementedReturnTypeWithDocblockOnMultipleLines(): void { $this->expectException(CodeException::class); - $this->expectExceptionMessage('LessSpecificImplementedReturnType - somefile.php:5:'); + $this->expectExceptionMessage('LessSpecificImplementedReturnType - somefile.php:16:'); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); @@ -50,23 +61,23 @@ public function testLessSpecificImplementedReturnTypeWithDocblockOnMultipleLines 'somefile.php', 'analyzeFile('somefile.php', new Context()); @@ -75,19 +86,31 @@ class BreakingThings extends ParentClass public function testLessSpecificImplementedReturnTypeWithDescription(): void { $this->expectException(CodeException::class); - $this->expectExceptionMessage('LessSpecificImplementedReturnType - somefile.php:7:'); + $this->expectExceptionMessage('LessSpecificImplementedReturnType - somefile.php:19:'); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); From 804087b5d59f70c8186aa234e56ccde5aca0e1cc Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 23:14:04 +0200 Subject: [PATCH 09/39] #10026 added workaround for DateTimeInterface::__unserialize() signature --- tests/Traits/ValidCodeAnalysisTestTrait.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/Traits/ValidCodeAnalysisTestTrait.php b/tests/Traits/ValidCodeAnalysisTestTrait.php index 7a76481d275..c6b78b6c4ec 100644 --- a/tests/Traits/ValidCodeAnalysisTestTrait.php +++ b/tests/Traits/ValidCodeAnalysisTestTrait.php @@ -76,6 +76,19 @@ public function testValidCode( $codebase->enterServerMode(); $codebase->config->visitPreloadedStubFiles($codebase); + if (version_compare(PHP_VERSION, '8.2.0', '>=')) { + $this->addStubFile( + 'stubOne.phpstub', + 'addFile($file_path, $code); From cbee1e094e390fb22a3c1555b96c8b31912d73e2 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Wed, 19 Jul 2023 23:52:25 +0200 Subject: [PATCH 10/39] #10026 adapted DateTime tests for DateMalformedStringException in PHP 8.3 --- .github/workflows/ci.yml | 6 +- tests/DateTimeTest.php | 107 ++++++++++++-------- tests/Traits/ValidCodeAnalysisTestTrait.php | 3 +- 3 files changed, 72 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91648f3c09a..f97b31396b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,9 +70,9 @@ jobs: echo "chunks=$(php -r 'echo json_encode(range(1, ${{ env.CHUNK_COUNT }} ));')" >> $GITHUB_OUTPUT tests: - name: "Tests - ${{ matrix.php-version }} ${{ matrix.chunk }}/${{ matrix.count }} ${{ matrix.operating-system }}" + name: "Tests - PHP ${{ matrix.php-version }} ${{ matrix.chunk }}/${{ matrix.count }}" - runs-on: ${{ matrix.operating-system }} + runs-on: ubuntu-latest needs: - chunk-matrix @@ -81,11 +81,11 @@ jobs: fail-fast: false matrix: php-version: + - "7.4" - "8.0" - "8.1" - "8.2" - "8.3" - operating-system: [ubuntu-latest] count: ${{ fromJson(needs.chunk-matrix.outputs.count) }} chunk: ${{ fromJson(needs.chunk-matrix.outputs.chunks) }} diff --git a/tests/DateTimeTest.php b/tests/DateTimeTest.php index ce9e926ff3a..424bf596d24 100644 --- a/tests/DateTimeTest.php +++ b/tests/DateTimeTest.php @@ -2,12 +2,79 @@ namespace Psalm\Tests; +use Psalm\Context; use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait; class DateTimeTest extends TestCase { use ValidCodeAnalysisTestTrait; + public function testModifyWithInvalidConstant(): void + { + $context = new Context(); + + if (version_compare(PHP_VERSION, '8.3', '>')) { + $this->expectException(\DateMalformedStringException::class); + $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (foo) at position 0 (f)'); + } + + $this->addFile( + 'somefile.php', + 'modify(getString()); + $b = $dateTimeImmutable->modify(getString());', + ); + + $this->analyzeFile('somefile.php', $context); + + $this->assertSame('false', $context->vars_in_scope['$a']->getId(true)); + $this->assertSame('false', $context->vars_in_scope['$b']->getId(true)); + } + + public function testModifyWithBothConstant(): void + { + $context = new Context(); + + if (version_compare(PHP_VERSION, '8.3', '>')) { + $this->expectException(\DateMalformedStringException::class); + $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (bar) at position 0 (b)'); + } + + $this->addFile( + 'somefile.php', + 'modify(getString()); + $b = $dateTimeImmutable->modify(getString());', + ); + + $this->analyzeFile('somefile.php', $context); + + $this->assertSame('DateTime|false', $context->vars_in_scope['$a']->getId(false)); + $this->assertSame('DateTimeImmutable|false', $context->vars_in_scope['$b']->getId(false)); + } + public function providerValidCodeParse(): iterable { return [ @@ -48,46 +115,6 @@ function getString(): string '$b' => 'DateTimeImmutable', ], ], - 'modifyWithInvalidConstant' => [ - 'code' => 'modify(getString()); - $b = $dateTimeImmutable->modify(getString()); - ', - 'assertions' => [ - '$a' => 'false', - '$b' => 'false', - ], - ], - 'modifyWithBothConstant' => [ - 'code' => 'modify(getString()); - $b = $dateTimeImmutable->modify(getString()); - ', - 'assertions' => [ - '$a' => 'DateTime|false', - '$b' => 'DateTimeImmutable|false', - ], - ], 'otherMethodAfterModify' => [ 'code' => 'enterServerMode(); $codebase->config->visitPreloadedStubFiles($codebase); - if (version_compare(PHP_VERSION, '8.2.0', '>=')) { + // avoid MethodSignatureMismatch for __unserialize/() when extending DateTime + if (version_compare(PHP_VERSION, '8.2', '>')) { $this->addStubFile( 'stubOne.phpstub', ' Date: Thu, 20 Jul 2023 00:58:18 +0200 Subject: [PATCH 11/39] #10026 extended callmap delta for php 8.3 --- dictionaries/CallMap.php | 7 ------- dictionaries/CallMap_83_delta.php | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 4145bf41e22..1ec37f85b60 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -491,7 +491,6 @@ 'atan' => ['float', 'num'=>'float'], 'atan2' => ['float', 'y'=>'float', 'x'=>'float'], 'atanh' => ['float', 'num'=>'float'], -'BadFunctionCallException::__clone' => ['void'], 'BadFunctionCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'BadFunctionCallException::__toString' => ['string'], 'BadFunctionCallException::getCode' => ['int'], @@ -7652,7 +7651,6 @@ 'mt_getrandmax' => ['int'], 'mt_rand' => ['int', 'min'=>'int', 'max'=>'int'], 'mt_rand\'1' => ['int'], -'mt_srand' => ['void', 'seed='=>'int', 'mode='=>'int'], 'MultipleIterator::__construct' => ['void', 'flags='=>'int'], 'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'info='=>'string|int|null'], 'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'Iterator'], @@ -8129,8 +8127,6 @@ 'MysqlndUhPreparedStatement::__construct' => ['void'], 'MysqlndUhPreparedStatement::execute' => ['bool', 'statement'=>'mysqlnd_prepared_statement'], 'MysqlndUhPreparedStatement::prepare' => ['bool', 'statement'=>'mysqlnd_prepared_statement', 'query'=>'string'], -'natcasesort' => ['bool', '&rw_array'=>'array'], -'natsort' => ['bool', '&rw_array'=>'array'], 'net_get_interfaces' => ['array>|false'], 'newrelic_add_custom_parameter' => ['bool', 'key'=>'string', 'value'=>'bool|float|int|string'], 'newrelic_add_custom_tracer' => ['bool', 'function_name'=>'string'], @@ -9400,7 +9396,6 @@ 'posix_getppid' => ['int'], 'posix_getpwnam' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'username'=>'string'], 'posix_getpwuid' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'user_id'=>'int'], -'posix_getrlimit' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], 'posix_getsid' => ['int|false', 'process_id'=>'int'], 'posix_getuid' => ['int'], 'posix_initgroups' => ['bool', 'username'=>'string', 'group_id'=>'int'], @@ -10851,7 +10846,6 @@ 'RRDGraph::setOptions' => ['void', 'options'=>'array'], 'RRDUpdater::__construct' => ['void', 'path'=>'string'], 'RRDUpdater::update' => ['bool', 'values'=>'array', 'time='=>'string'], -'rsort' => ['bool', '&rw_array'=>'array', 'flags='=>'int'], 'rtrim' => ['string', 'string'=>'string', 'characters='=>'string'], 'runkit7_constant_add' => ['bool', 'constant_name'=>'string', 'value'=>'mixed', 'new_visibility='=>'int'], 'runkit7_constant_redefine' => ['bool', 'constant_name'=>'string', 'value'=>'mixed', 'new_visibility='=>'?int'], @@ -12758,7 +12752,6 @@ 'sqlsrv_send_stream_data' => ['bool', 'stmt'=>'resource'], 'sqlsrv_server_info' => ['array', 'conn'=>'resource'], 'sqrt' => ['float', 'num'=>'float'], -'srand' => ['void', 'seed='=>'int', 'mode='=>'int'], 'sscanf' => ['list|int|null', 'string'=>'string', 'format'=>'string', '&...w_vars='=>'string|int|float|null'], 'ssdeep_fuzzy_compare' => ['int', 'signature1'=>'string', 'signature2'=>'string'], 'ssdeep_fuzzy_hash' => ['string', 'to_hash'=>'string'], diff --git a/dictionaries/CallMap_83_delta.php b/dictionaries/CallMap_83_delta.php index 2d27020ddc5..1c813f3131a 100644 --- a/dictionaries/CallMap_83_delta.php +++ b/dictionaries/CallMap_83_delta.php @@ -25,6 +25,30 @@ 'old' => ['array{runs:int,collected:int,threshold:int,roots:int}'], 'new' => ['array{runs:int,collected:int,threshold:int,roots:int,running:bool,protected:bool,full:bool,buffer_size:int}'], ], + 'srand' => [ + 'old' => ['void', 'seed='=>'int', 'mode='=>'int'], + 'new' => ['void', 'seed='=>'?int', 'mode='=>'int'], + ], + 'mt_srand' => [ + 'old' => ['void', 'seed='=>'int', 'mode='=>'int'], + 'new' =>['void', 'seed='=>'?int', 'mode='=>'int'], + ], + 'posix_getrlimit' => [ + 'old' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], + 'new' => ['array{"resource": string, "soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], + ], + 'natcasesort' => [ + 'old' => ['bool', '&rw_array'=>'array'], + 'new' => ['true', '&rw_array'=>'array'], + ], + 'natsort' => [ + 'old' => ['bool', '&rw_array'=>'array'], + 'new' => ['true', '&rw_array'=>'array'], + ], + 'rsort' => [ + 'old' => ['bool', '&rw_array'=>'array', 'flags='=>'int'], + 'new' => ['true', '&rw_array'=>'array', 'flags='=>'int'], + ], ], 'removed' => [ From f34327e079eba52106751c227e6291d2e04b5752 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 20 Jul 2023 01:01:10 +0200 Subject: [PATCH 12/39] 10026 extended InternalCallMapHandlerTest ignores for php 8.3 --- .../Codebase/InternalCallMapHandlerTest.php | 129 ++++++++++++------ 1 file changed, 85 insertions(+), 44 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index b21fc991a6b..03c60132459 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -73,15 +73,28 @@ class InternalCallMapHandlerTest extends TestCase * @var array> */ private static array $ignoredFunctions = [ + 'argumentcounterror::__clone' => ['8.3'], + 'arithmeticerror::__clone' => ['8.3'], 'array_multisort', + 'badfunctioncallexception::__clone' => ['8.3'], + 'badmethodcallexception::__clone' => ['8.3'], + 'closedgeneratorexception::__clone' => ['8.3'], 'datefmt_create' => ['8.0'], + 'domainexception::__clone' => ['8.3'], + 'errorexception::__clone' => ['8.3'], 'fiber::start', 'imagefilledpolygon', 'imagegd', 'imagegd2', 'imageopenpolygon', 'imagepolygon', + 'intlcodepointbreakiterator::__construct' => ['8.3'], + 'intlexception::__clone' => ['8.3'], 'intlgregoriancalendar::__construct', + 'invalidargumentexception::__clone' => ['8.3'], + 'jsonexception::__clone' => ['8.3'], + 'lengthexception::__clone' => ['8.3'], + 'logicexception::__clone' => ['8.3'], 'lzf_compress', 'lzf_decompress', 'mailparse_msg_extract_part', @@ -140,7 +153,15 @@ class InternalCallMapHandlerTest extends TestCase 'oci_result', 'ocigetbufferinglob', 'ocisetbufferinglob', + 'outofboundsexception::__clone' => ['8.3'], + 'outofrangeexception::__clone' => ['8.3'], + 'overflowexception::__clone' => ['8.3'], + 'parseerror::__clone' => ['8.3'], + 'rangeexception::__clone' => ['8.3'], 'recursiveiteratoriterator::__construct', // Class used in CallMap does not exist: recursiveiterator + 'reflectionnamedtype::__clone' => ['8.3'], + 'reflectionobject::__clone' => ['8.3'], + 'runtimeexception::__clone' => ['8.3'], 'sqlsrv_fetch_array', 'sqlsrv_fetch_object', 'sqlsrv_get_field', @@ -148,6 +169,9 @@ class InternalCallMapHandlerTest extends TestCase 'sqlsrv_query', 'sqlsrv_server_info', 'ssh2_forward_accept', + 'typeerror::__clone' => ['8.3'], + 'underflowexception::__clone' => ['8.3'], + 'unexpectedvalueexception::__clone' => ['8.3'], 'xdiff_file_bdiff', 'xdiff_file_bdiff_size', 'xdiff_file_diff', @@ -171,62 +195,79 @@ class InternalCallMapHandlerTest extends TestCase * @var array> */ private static array $ignoredReturnTypeOnlyFunctions = [ - 'appenditerator::getinneriterator' => ['8.1', '8.2'], - 'appenditerator::getiteratorindex' => ['8.1', '8.2'], - 'arrayobject::getiterator' => ['8.1', '8.2'], - 'cachingiterator::getinneriterator' => ['8.1', '8.2'], - 'callbackfilteriterator::getinneriterator' => ['8.1', '8.2'], + 'appenditerator::getinneriterator' => ['8.1', '8.2', '8.3'], + 'appenditerator::getiteratorindex' => ['8.1', '8.2', '8.3'], + 'arrayobject::getiterator' => ['8.1', '8.2', '8.3'], + 'cachingiterator::getinneriterator' => ['8.1', '8.2', '8.3'], + 'callbackfilteriterator::getinneriterator' => ['8.1', '8.2', '8.3'], 'curl_multi_getcontent', - 'datetime::add' => ['8.1', '8.2'], // DateTime does not contain static - 'datetime::modify' => ['8.1', '8.2'], // DateTime does not contain static - 'datetime::createfromformat' => ['8.1', '8.2'], // DateTime does not contain static + 'datetime::add' => ['8.1', '8.2', '8.3'], // DateTime does not contain static + 'datetime::modify' => ['8.1', '8.2', '8.3'], // DateTime does not contain static + 'datetime::createfromformat' => ['8.1', '8.2', '8.3'], // DateTime does not contain static 'datetime::createfromimmutable' => ['8.1'], 'datetime::createfrominterface', - 'datetime::setdate' => ['8.1', '8.2'], // DateTime does not contain static - 'datetime::setisodate' => ['8.1', '8.2'], // DateTime does not contain static - 'datetime::settime' => ['8.1', '8.2'], // DateTime does not contain static - 'datetime::settimestamp' => ['8.1', '8.2'], // DateTime does not contain static - 'datetime::settimezone' => ['8.1', '8.2'], // DateTime does not contain static - 'datetime::sub' => ['8.1', '8.2'], // DateTime does not contain static + 'datetime::setdate' => ['8.1', '8.2', '8.3'], // DateTime does not contain static + 'datetime::setisodate' => ['8.1', '8.2', '8.3'], // DateTime does not contain static + 'datetime::settime' => ['8.1', '8.2', '8.3'], // DateTime does not contain static + 'datetime::settimestamp' => ['8.1', '8.2', '8.3'], // DateTime does not contain static + 'datetime::settimezone' => ['8.1', '8.2', '8.3'], // DateTime does not contain static + 'datetime::sub' => ['8.1', '8.2', '8.3'], // DateTime does not contain static 'datetimeimmutable::createfrominterface', 'fiber::getcurrent', - 'filteriterator::getinneriterator' => ['8.1', '8.2'], + 'filteriterator::getinneriterator' => ['8.1', '8.2', '8.3'], 'get_cfg_var', // Ignore array return type - 'infiniteiterator::getinneriterator' => ['8.1', '8.2'], - 'iteratoriterator::getinneriterator' => ['8.1', '8.2'], - 'limititerator::getinneriterator' => ['8.1', '8.2'], - 'locale::canonicalize' => ['8.1', '8.2'], - 'locale::getallvariants' => ['8.1', '8.2'], - 'locale::getkeywords' => ['8.1', '8.2'], - 'locale::getprimarylanguage' => ['8.1', '8.2'], - 'locale::getregion' => ['8.1', '8.2'], - 'locale::getscript' => ['8.1', '8.2'], - 'locale::parselocale' => ['8.1', '8.2'], - 'messageformatter::create' => ['8.1', '8.2'], - 'multipleiterator::current' => ['8.1', '8.2'], - 'mysqli::get_charset' => ['8.1', '8.2'], - 'mysqli_stmt::get_warnings' => ['8.1', '8.2'], + 'infiniteiterator::getinneriterator' => ['8.1', '8.2', '8.3'], + 'iteratoriterator::getinneriterator' => ['8.1', '8.2', '8.3'], + 'limititerator::getinneriterator' => ['8.1', '8.2', '8.3'], + 'locale::canonicalize' => ['8.1', '8.2', '8.3'], + 'locale::getallvariants' => ['8.1', '8.2', '8.3'], + 'locale::getkeywords' => ['8.1', '8.2', '8.3'], + 'locale::getprimarylanguage' => ['8.1', '8.2', '8.3'], + 'locale::getregion' => ['8.1', '8.2', '8.3'], + 'locale::getscript' => ['8.1', '8.2', '8.3'], + 'locale::parselocale' => ['8.1', '8.2', '8.3'], + 'messageformatter::create' => ['8.1', '8.2', '8.3'], + 'multipleiterator::current' => ['8.1', '8.2', '8.3'], + 'mysqli::get_charset' => ['8.1', '8.2', '8.3'], + 'mysqli_stmt::get_warnings' => ['8.1', '8.2', '8.3'], 'mysqli_stmt_get_warnings', 'mysqli_stmt_insert_id', - 'norewinditerator::getinneriterator' => ['8.1', '8.2'], + 'norewinditerator::getinneriterator' => ['8.1', '8.2', '8.3'], 'passthru', - 'recursivecachingiterator::getinneriterator' => ['8.1', '8.2'], - 'recursivecallbackfilteriterator::getinneriterator' => ['8.1', '8.2'], - 'recursivefilteriterator::getinneriterator' => ['8.1', '8.2'], - 'recursiveregexiterator::getinneriterator' => ['8.1', '8.2'], + 'recursivecachingiterator::getinneriterator' => ['8.1', '8.2', '8.3'], + 'recursivecallbackfilteriterator::getinneriterator' => ['8.1', '8.2', '8.3'], + 'recursivefilteriterator::getinneriterator' => ['8.1', '8.2', '8.3'], + 'recursiveregexiterator::getinneriterator' => ['8.1', '8.2', '8.3'], 'reflectionclass::getstaticproperties' => ['8.1', '8.2'], - 'reflectionclass::newinstanceargs' => ['8.1', '8.2'], - 'reflectionfunction::getclosurescopeclass' => ['8.1', '8.2'], - 'reflectionfunction::getclosurethis' => ['8.1', '8.2'], - 'reflectionmethod::getclosurescopeclass' => ['8.1', '8.2'], - 'reflectionmethod::getclosurethis' => ['8.1', '8.2'], + 'reflectionclass::newinstanceargs' => ['8.1', '8.2', '8.3'], + 'reflectionfunction::getclosurescopeclass' => ['8.1', '8.2', '8.3'], + 'reflectionfunction::getclosurethis' => ['8.1', '8.2', '8.3'], + 'reflectionmethod::getclosurescopeclass' => ['8.1', '8.2', '8.3'], + 'reflectionmethod::getclosurethis' => ['8.1', '8.2', '8.3'], 'reflectionobject::getstaticproperties' => ['8.1', '8.2'], - 'reflectionobject::newinstanceargs' => ['8.1', '8.2'], - 'regexiterator::getinneriterator' => ['8.1', '8.2'], + 'reflectionobject::newinstanceargs' => ['8.1', '8.2', '8.3'], + 'regexiterator::getinneriterator' => ['8.1', '8.2', '8.3'], 'register_shutdown_function' => ['8.0', '8.1'], - 'splfileobject::fscanf' => ['8.1', '8.2'], - 'spltempfileobject::fscanf' => ['8.1', '8.2'], - 'xsltprocessor::transformtoxml' => ['8.1', '8.2'], + 'splfileobject::fscanf' => ['8.1', '8.2', '8.3'], + 'spltempfileobject::fscanf' => ['8.1', '8.2', '8.3'], + 'xsltprocessor::transformtoxml' => ['8.1', '8.2', '8.3'], + 'intlcal_clear' => ['8.3'], + 'intlrulebasedbreakiterator::settext' => ['8.3'], + 'intlcodepointbreakiterator::__construct' => ['8.3'], + 'intlcodepointbreakiterator::settext' => ['8.3'], + 'intldateformatter::settimezone' => ['8.3'], + 'intlchar::enumcharnames' => ['8.3'], + 'intlbreakiterator::settext' => ['8.3'], + 'intlcal_set_lenient' => ['8.3'], + 'intlcal_set_first_day_of_week' => ['8.3'], + 'datefmt_set_timezone' => ['8.3'], + 'imap_setflag_full' => ['8.3'], + 'imap_expunge' => ['8.3'], + 'imap_gc' => ['8.3'], + 'imap_undelete' => ['8.3'], + 'imap_delete' => ['8.3'], + 'imap_clearflag_full' => ['8.3'], + 'imap_close' => ['8.3'], ]; /** From ee185b9ad84fe39a5bbe4a79ddad9334accd7dff Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 20 Jul 2023 01:28:46 +0200 Subject: [PATCH 13/39] #10026 cleanup method signatures --- .github/workflows/ci.yml | 1 - dictionaries/CallMap.php | 7 +++++++ dictionaries/CallMap_83_delta.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f97b31396b3..fb54eec7b4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,7 +81,6 @@ jobs: fail-fast: false matrix: php-version: - - "7.4" - "8.0" - "8.1" - "8.2" diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 1ec37f85b60..79d13f4dfae 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -491,6 +491,7 @@ 'atan' => ['float', 'num'=>'float'], 'atan2' => ['float', 'y'=>'float', 'x'=>'float'], 'atanh' => ['float', 'num'=>'float'], +'BadFunctionCallException::__clone' => ['void'], 'BadFunctionCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'BadFunctionCallException::__toString' => ['string'], 'BadFunctionCallException::getCode' => ['int'], @@ -7651,6 +7652,7 @@ 'mt_getrandmax' => ['int'], 'mt_rand' => ['int', 'min'=>'int', 'max'=>'int'], 'mt_rand\'1' => ['int'], +'mt_srand' => ['void', 'seed='=>'?int', 'mode='=>'int'], 'MultipleIterator::__construct' => ['void', 'flags='=>'int'], 'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'info='=>'string|int|null'], 'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'Iterator'], @@ -8127,6 +8129,8 @@ 'MysqlndUhPreparedStatement::__construct' => ['void'], 'MysqlndUhPreparedStatement::execute' => ['bool', 'statement'=>'mysqlnd_prepared_statement'], 'MysqlndUhPreparedStatement::prepare' => ['bool', 'statement'=>'mysqlnd_prepared_statement', 'query'=>'string'], +'natcasesort' => ['true', '&rw_array'=>'array'], +'natsort' => ['true', '&rw_array'=>'array'], 'net_get_interfaces' => ['array>|false'], 'newrelic_add_custom_parameter' => ['bool', 'key'=>'string', 'value'=>'bool|float|int|string'], 'newrelic_add_custom_tracer' => ['bool', 'function_name'=>'string'], @@ -9396,6 +9400,7 @@ 'posix_getppid' => ['int'], 'posix_getpwnam' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'username'=>'string'], 'posix_getpwuid' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'user_id'=>'int'], +'posix_getrlimit' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false', 'resource=' => '?int'], 'posix_getsid' => ['int|false', 'process_id'=>'int'], 'posix_getuid' => ['int'], 'posix_initgroups' => ['bool', 'username'=>'string', 'group_id'=>'int'], @@ -10846,6 +10851,7 @@ 'RRDGraph::setOptions' => ['void', 'options'=>'array'], 'RRDUpdater::__construct' => ['void', 'path'=>'string'], 'RRDUpdater::update' => ['bool', 'values'=>'array', 'time='=>'string'], +'rsort' => ['true', '&rw_array'=>'array', 'flags='=>'int'], 'rtrim' => ['string', 'string'=>'string', 'characters='=>'string'], 'runkit7_constant_add' => ['bool', 'constant_name'=>'string', 'value'=>'mixed', 'new_visibility='=>'int'], 'runkit7_constant_redefine' => ['bool', 'constant_name'=>'string', 'value'=>'mixed', 'new_visibility='=>'?int'], @@ -12752,6 +12758,7 @@ 'sqlsrv_send_stream_data' => ['bool', 'stmt'=>'resource'], 'sqlsrv_server_info' => ['array', 'conn'=>'resource'], 'sqrt' => ['float', 'num'=>'float'], +'srand' => ['void', 'seed='=>'?int', 'mode='=>'int'], 'sscanf' => ['list|int|null', 'string'=>'string', 'format'=>'string', '&...w_vars='=>'string|int|float|null'], 'ssdeep_fuzzy_compare' => ['int', 'signature1'=>'string', 'signature2'=>'string'], 'ssdeep_fuzzy_hash' => ['string', 'to_hash'=>'string'], diff --git a/dictionaries/CallMap_83_delta.php b/dictionaries/CallMap_83_delta.php index 1c813f3131a..9e242943323 100644 --- a/dictionaries/CallMap_83_delta.php +++ b/dictionaries/CallMap_83_delta.php @@ -35,7 +35,7 @@ ], 'posix_getrlimit' => [ 'old' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], - 'new' => ['array{"resource": string, "soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], + 'new' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false', 'resource=' => '?int'], ], 'natcasesort' => [ 'old' => ['bool', '&rw_array'=>'array'], From 741d19682a988e380c7f2fca241c57aab95e7e25 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 20 Jul 2023 01:43:31 +0200 Subject: [PATCH 14/39] #10026 cleanup signature for hash_pbkdf2() --- .github/workflows/ci.yml | 2 +- dictionaries/CallMap_83_delta.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb54eec7b4b..21436d3f397 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: echo "chunks=$(php -r 'echo json_encode(range(1, ${{ env.CHUNK_COUNT }} ));')" >> $GITHUB_OUTPUT tests: - name: "Tests - PHP ${{ matrix.php-version }} ${{ matrix.chunk }}/${{ matrix.count }}" + name: "Unit Tests - PHP ${{ matrix.php-version }} ${{ matrix.chunk }}/${{ matrix.count }}" runs-on: ubuntu-latest needs: diff --git a/dictionaries/CallMap_83_delta.php b/dictionaries/CallMap_83_delta.php index 9e242943323..eee4d2d2d3b 100644 --- a/dictionaries/CallMap_83_delta.php +++ b/dictionaries/CallMap_83_delta.php @@ -49,6 +49,10 @@ 'old' => ['bool', '&rw_array'=>'array', 'flags='=>'int'], 'new' => ['true', '&rw_array'=>'array', 'flags='=>'int'], ], + 'hash_pbkdf2' => [ + 'old' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool'], + 'new' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool', 'options=' => 'array'], + ], ], 'removed' => [ From 51058eb45e5f17229ab561e9d1f70ed10d14853c Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 20 Jul 2023 02:02:28 +0200 Subject: [PATCH 15/39] #10026 cleanup --- .github/workflows/ci.yml | 1 - dictionaries/CallMap.php | 2 +- tests/DateTimeTest.php | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21436d3f397..8649a7189d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,6 @@ jobs: needs: - chunk-matrix - strategy: fail-fast: false matrix: diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 79d13f4dfae..b4bf5fed75b 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -3925,7 +3925,7 @@ 'hash_hmac_algos' => ['list'], 'hash_hmac_file' => ['non-empty-string', 'algo'=>'string', 'filename'=>'string', 'key'=>'string', 'binary='=>'bool'], 'hash_init' => ['HashContext', 'algo'=>'string', 'flags='=>'int', 'key='=>'string', 'options='=>'array{seed:scalar}'], -'hash_pbkdf2' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool'], +'hash_pbkdf2' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool', 'options=' => 'array'], 'hash_update' => ['bool', 'context'=>'HashContext', 'data'=>'string'], 'hash_update_file' => ['bool', 'context'=>'HashContext', 'filename'=>'string', 'stream_context='=>'?resource'], 'hash_update_stream' => ['int', 'context'=>'HashContext', 'stream'=>'resource', 'length='=>'int'], diff --git a/tests/DateTimeTest.php b/tests/DateTimeTest.php index 424bf596d24..741530de70e 100644 --- a/tests/DateTimeTest.php +++ b/tests/DateTimeTest.php @@ -14,7 +14,7 @@ public function testModifyWithInvalidConstant(): void $context = new Context(); if (version_compare(PHP_VERSION, '8.3', '>')) { - $this->expectException(\DateMalformedStringException::class); + $this->expectException(\Exception::class); $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (foo) at position 0 (f)'); } @@ -47,7 +47,7 @@ public function testModifyWithBothConstant(): void $context = new Context(); if (version_compare(PHP_VERSION, '8.3', '>')) { - $this->expectException(\DateMalformedStringException::class); + $this->expectException(\Exception::class); $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (bar) at position 0 (b)'); } From 5affc125e8c36dcb6e336f1c106196fc25c8bf67 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 21:48:28 +0200 Subject: [PATCH 16/39] #10026 run phpcs and phar-build with php 8.0 in circle-ci (php 7.4 is out of official support) --- .circleci/config.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fb7edfe00e2..9ac95561065 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,5 @@ # Use the latest 2.1 version of CircleCI pipeline processing engine, see https://circleci.com/docs/2.0/configuration-reference/ +# See https://app.circleci.com/pipelines/github/vimeo/psalm version: 2.1 executors: php-74: @@ -12,7 +13,7 @@ executors: - image: thecodingmachine/php:8.1-v4-cli jobs: "Code Style Analysis": - executor: php-74 + executor: php-80 steps: - checkout @@ -40,7 +41,7 @@ jobs: command: vendor/bin/phpcs -d memory_limit=512M phar-build: - executor: php-74 + executor: php-80 steps: - attach_workspace: at: /home/docker/project/ From 4307bc49e3602bff0d3bb33b3c02e793b8eea151 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 21:49:07 +0200 Subject: [PATCH 17/39] #10026 run buuld-phar with php 8.0 in github actions (php 7.4 is out of official support) --- .github/workflows/build-phar.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-phar.yml b/.github/workflows/build-phar.yml index 8d75dfe63c3..b042b090497 100644 --- a/.github/workflows/build-phar.yml +++ b/.github/workflows/build-phar.yml @@ -38,7 +38,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.0' tools: composer:v2 coverage: none env: From fc1d3e09abb761a1df54c2a8d59255a86dd47d47 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 21:49:36 +0200 Subject: [PATCH 18/39] #10026 added code style analysis with phpcs to github actions --- .github/workflows/ci.yml | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8649a7189d9..4ad2fd3b7ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.0' tools: composer:v2 coverage: none env: @@ -49,6 +49,51 @@ jobs: - run: | git ls-files | grep \\\.php$ | grep -v ^dictionaries/scripts/* | ./vendor/bin/parallel-lint --stdin + + code-style: + name: Code Style Analysis + runs-on: ubuntu-latest + steps: + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + tools: composer:v2 + coverage: none + env: + fail-fast: true + + - uses: actions/checkout@v3 + + - name: Get Composer Cache Directories + id: composer-cache + run: | + echo "files_cache=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + echo "vcs_cache=$(composer config cache-vcs-dir)" >> $GITHUB_OUTPUT + + - name: Generate composer.lock + run: composer update --no-install + env: + COMPOSER_ROOT_VERSION: dev-master + + - name: Cache composer cache + uses: actions/cache@v3 + with: + path: | + ${{ steps.composer-cache.outputs.files_cache }} + ${{ steps.composer-cache.outputs.vcs_cache }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Run composer install + run: composer install -o + env: + COMPOSER_ROOT_VERSION: dev-master + + - name: Code Style Analysis with PHPCS + run: vendor/bin/phpcs -d memory_limit=512M + chunk-matrix: permissions: contents: none From 9590279c6213b1457e2d156f96de8adc65ba00ed Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 21:50:30 +0200 Subject: [PATCH 19/39] #10026 added style changes to pass phpcs check --- tests/DateTimeTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/DateTimeTest.php b/tests/DateTimeTest.php index 741530de70e..b6fbe2ef2e8 100644 --- a/tests/DateTimeTest.php +++ b/tests/DateTimeTest.php @@ -2,9 +2,14 @@ namespace Psalm\Tests; +use Exception; use Psalm\Context; use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait; +use function version_compare; + +use const PHP_VERSION; + class DateTimeTest extends TestCase { use ValidCodeAnalysisTestTrait; @@ -14,7 +19,7 @@ public function testModifyWithInvalidConstant(): void $context = new Context(); if (version_compare(PHP_VERSION, '8.3', '>')) { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (foo) at position 0 (f)'); } @@ -47,7 +52,7 @@ public function testModifyWithBothConstant(): void $context = new Context(); if (version_compare(PHP_VERSION, '8.3', '>')) { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (bar) at position 0 (b)'); } From 7f39dab07bc0d44f569138f00b0b2600b34f506d Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 22:26:05 +0200 Subject: [PATCH 20/39] #10026 moved some more signature changes to CallMap_83_delta --- dictionaries/CallMap.php | 56 ++++-------- dictionaries/CallMap_83_delta.php | 88 +++++++++++++++++++ .../Codebase/InternalCallMapHandlerTest.php | 41 --------- 3 files changed, 104 insertions(+), 81 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index b4bf5fed75b..7d8c477b1a1 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -337,7 +337,6 @@ 'AppendIterator::next' => ['void'], 'AppendIterator::rewind' => ['void'], 'AppendIterator::valid' => ['bool'], -'ArgumentCountError::__clone' => ['void'], 'ArgumentCountError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'ArgumentCountError::__toString' => ['string'], 'ArgumentCountError::__wakeup' => ['void'], @@ -348,7 +347,6 @@ 'ArgumentCountError::getPrevious' => ['?Throwable'], 'ArgumentCountError::getTrace' => ['list\',args?:array}>'], 'ArgumentCountError::getTraceAsString' => ['string'], -'ArithmeticError::__clone' => ['void'], 'ArithmeticError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'ArithmeticError::__toString' => ['string'], 'ArithmeticError::__wakeup' => ['void'], @@ -491,7 +489,6 @@ 'atan' => ['float', 'num'=>'float'], 'atan2' => ['float', 'y'=>'float', 'x'=>'float'], 'atanh' => ['float', 'num'=>'float'], -'BadFunctionCallException::__clone' => ['void'], 'BadFunctionCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'BadFunctionCallException::__toString' => ['string'], 'BadFunctionCallException::getCode' => ['int'], @@ -501,7 +498,6 @@ 'BadFunctionCallException::getPrevious' => ['?Throwable'], 'BadFunctionCallException::getTrace' => ['list\',args?:array}>'], 'BadFunctionCallException::getTraceAsString' => ['string'], -'BadMethodCallException::__clone' => ['void'], 'BadMethodCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'BadMethodCallException::__toString' => ['string'], 'BadMethodCallException::getCode' => ['int'], @@ -661,7 +657,6 @@ 'clearstatcache' => ['void', 'clear_realpath_cache='=>'bool', 'filename='=>'string'], 'cli_get_process_title' => ['?string'], 'cli_set_process_title' => ['bool', 'title'=>'string'], -'ClosedGeneratorException::__clone' => ['void'], 'ClosedGeneratorException::__toString' => ['string'], 'ClosedGeneratorException::getCode' => ['int'], 'ClosedGeneratorException::getFile' => ['string'], @@ -1352,7 +1347,7 @@ 'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'IntlCalendar|int|null'], 'datefmt_set_lenient' => ['void', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'], 'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'], -'datefmt_set_timezone' => ['false|null', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], +'datefmt_set_timezone' => ['bool', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], 'DateInterval::__construct' => ['void', 'duration'=>'string'], 'DateInterval::__set_state' => ['DateInterval', 'array'=>'array'], 'DateInterval::__wakeup' => ['void'], @@ -1633,7 +1628,6 @@ 'dom_xpath_query' => ['DOMNodeList', 'expr'=>'string', 'context'=>'DOMNode', 'registernodens'=>'bool'], 'dom_xpath_register_ns' => ['bool', 'prefix'=>'string', 'uri'=>'string'], 'dom_xpath_register_php_functions' => [''], -'DomainException::__clone' => ['void'], 'DomainException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'DomainException::__toString' => ['string'], 'DomainException::__wakeup' => ['void'], @@ -2093,7 +2087,6 @@ 'error_get_last' => ['?array{type:int,message:string,file:string,line:int}'], 'error_log' => ['bool', 'message'=>'string', 'message_type='=>'int', 'destination='=>'?string', 'additional_headers='=>'?string'], 'error_reporting' => ['int', 'error_level='=>'?int'], -'ErrorException::__clone' => ['void'], 'ErrorException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'severity='=>'int', 'filename='=>'?string', 'line='=>'?int', 'previous='=>'?Throwable'], 'ErrorException::__toString' => ['string'], 'ErrorException::getCode' => ['int'], @@ -5581,21 +5574,21 @@ 'imap_body' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'], 'imap_bodystruct' => ['stdClass|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'section'=>'string'], 'imap_check' => ['stdClass|false', 'imap'=>'IMAP\Connection'], -'imap_clearflag_full' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'], -'imap_close' => ['bool', 'imap'=>'IMAP\Connection', 'flags='=>'int'], +'imap_clearflag_full' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'], +'imap_close' => ['true', 'imap'=>'IMAP\Connection', 'flags='=>'int'], 'imap_create' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], 'imap_createmailbox' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], -'imap_delete' => ['bool', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'], +'imap_delete' => ['true', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'], 'imap_deletemailbox' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], 'imap_errors' => ['array|false'], -'imap_expunge' => ['bool', 'imap'=>'IMAP\Connection'], +'imap_expunge' => ['true', 'imap'=>'IMAP\Connection'], 'imap_fetch_overview' => ['array|false', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flags='=>'int'], 'imap_fetchbody' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'section'=>'string', 'flags='=>'int'], 'imap_fetchheader' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'], 'imap_fetchmime' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'section'=>'string', 'flags='=>'int'], 'imap_fetchstructure' => ['stdClass|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'], 'imap_fetchtext' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'], -'imap_gc' => ['bool', 'imap'=>'IMAP\Connection', 'flags'=>'int'], +'imap_gc' => ['true', 'imap'=>'IMAP\Connection', 'flags'=>'int'], 'imap_get_quota' => ['array|false', 'imap'=>'IMAP\Connection', 'quota_root'=>'string'], 'imap_get_quotaroot' => ['array|false', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], 'imap_getacl' => ['array|false', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], @@ -5636,14 +5629,14 @@ 'imap_search' => ['array|false', 'imap'=>'IMAP\Connection', 'criteria'=>'string', 'flags='=>'int', 'charset='=>'string'], 'imap_set_quota' => ['bool', 'imap'=>'IMAP\Connection', 'quota_root'=>'string', 'mailbox_size'=>'int'], 'imap_setacl' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string', 'user_id'=>'string', 'rights'=>'string'], -'imap_setflag_full' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'], +'imap_setflag_full' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'], 'imap_sort' => ['array|false', 'imap'=>'IMAP\Connection', 'criteria'=>'int', 'reverse'=>'bool', 'flags='=>'int', 'search_criteria='=>'?string', 'charset='=>'?string'], 'imap_status' => ['stdClass|false', 'imap'=>'IMAP\Connection', 'mailbox'=>'string', 'flags'=>'int'], 'imap_subscribe' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], 'imap_thread' => ['array|false', 'imap'=>'IMAP\Connection', 'flags='=>'int'], 'imap_timeout' => ['int|bool', 'timeout_type'=>'int', 'timeout='=>'int'], 'imap_uid' => ['int|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int'], -'imap_undelete' => ['bool', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'], +'imap_undelete' => ['true', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'], 'imap_unsubscribe' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], 'imap_utf7_decode' => ['string|false', 'string'=>'string'], 'imap_utf7_encode' => ['string', 'string'=>'string'], @@ -5738,11 +5731,11 @@ 'IntlBreakIterator::next' => ['int', 'offset='=>'?int'], 'IntlBreakIterator::preceding' => ['int', 'offset'=>'int'], 'IntlBreakIterator::previous' => ['int'], -'IntlBreakIterator::setText' => ['?bool', 'text'=>'string'], +'IntlBreakIterator::setText' => ['bool', 'text'=>'string'], 'intlcal_add' => ['bool', 'calendar'=>'IntlCalendar', 'field'=>'int', 'value'=>'int'], 'intlcal_after' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'], 'intlcal_before' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'], -'intlcal_clear' => ['bool', 'calendar'=>'IntlCalendar', 'field='=>'?int'], +'intlcal_clear' => ['true', 'calendar'=>'IntlCalendar', 'field='=>'?int'], 'intlcal_create_instance' => ['?IntlCalendar', 'timezone='=>'mixed', 'locale='=>'?string'], 'intlcal_equals' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'], 'intlcal_field_difference' => ['int|false', 'calendar'=>'IntlCalendar', 'timestamp'=>'float', 'field'=>'int'], @@ -5775,8 +5768,8 @@ 'intlcal_roll' => ['bool', 'calendar'=>'IntlCalendar', 'field'=>'int', 'value'=>'mixed'], 'intlcal_set' => ['bool', 'calendar'=>'IntlCalendar', 'year'=>'int', 'month'=>'int'], 'intlcal_set\'1' => ['bool', 'calendar'=>'IntlCalendar', 'year'=>'int', 'month'=>'int', 'dayOfMonth='=>'int', 'hour='=>'int', 'minute='=>'int', 'second='=>'int'], -'intlcal_set_first_day_of_week' => ['bool', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'int'], -'intlcal_set_lenient' => ['bool', 'calendar'=>'IntlCalendar', 'lenient'=>'bool'], +'intlcal_set_first_day_of_week' => ['true', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'int'], +'intlcal_set_lenient' => ['true', 'calendar'=>'IntlCalendar', 'lenient'=>'bool'], 'intlcal_set_repeated_wall_time_option' => ['true', 'calendar'=>'IntlCalendar', 'option'=>'int'], 'intlcal_set_skipped_wall_time_option' => ['true', 'calendar'=>'IntlCalendar', 'option'=>'int'], 'intlcal_set_time' => ['bool', 'calendar'=>'IntlCalendar', 'timestamp'=>'float'], @@ -5838,7 +5831,7 @@ 'IntlChar::charType' => ['?int', 'codepoint'=>'int|string'], 'IntlChar::chr' => ['?string', 'codepoint'=>'int|string'], 'IntlChar::digit' => ['int|false|null', 'codepoint'=>'int|string', 'base='=>'int'], -'IntlChar::enumCharNames' => ['?bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'], +'IntlChar::enumCharNames' => ['bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'], 'IntlChar::enumCharTypes' => ['void', 'callback'=>'callable(int,int,int):void'], 'IntlChar::foldCase' => ['int|string|null', 'codepoint'=>'int|string', 'options='=>'int'], 'IntlChar::forDigit' => ['int', 'digit'=>'int', 'base='=>'int'], @@ -5888,7 +5881,6 @@ 'IntlChar::tolower' => ['int|string|null', 'codepoint'=>'int|string'], 'IntlChar::totitle' => ['int|string|null', 'codepoint'=>'int|string'], 'IntlChar::toupper' => ['int|string|null', 'codepoint'=>'int|string'], -'IntlCodePointBreakIterator::__construct' => ['void'], 'IntlCodePointBreakIterator::createCharacterInstance' => ['?IntlRuleBasedBreakIterator', 'locale='=>'?string'], 'IntlCodePointBreakIterator::createCodePointInstance' => ['IntlCodePointBreakIterator'], 'IntlCodePointBreakIterator::createLineInstance' => ['?IntlRuleBasedBreakIterator', 'locale='=>'?string'], @@ -5909,7 +5901,7 @@ 'IntlCodePointBreakIterator::next' => ['int', 'offset='=>'?int'], 'IntlCodePointBreakIterator::preceding' => ['int', 'offset'=>'int'], 'IntlCodePointBreakIterator::previous' => ['int'], -'IntlCodePointBreakIterator::setText' => ['?bool', 'text'=>'string'], +'IntlCodePointBreakIterator::setText' => ['bool', 'text'=>'string'], 'IntlDateFormatter::__construct' => ['void', 'locale'=>'?string', 'dateType='=>'int', 'timeType='=>'int', 'timezone='=>'IntlTimeZone|DateTimeZone|string|null', 'calendar='=>'IntlCalendar|int|null', 'pattern='=>'?string'], 'IntlDateFormatter::create' => ['?IntlDateFormatter', 'locale'=>'?string', 'dateType='=>'int', 'timeType='=>'int', 'timezone='=>'IntlTimeZone|DateTimeZone|string|null', 'calendar='=>'IntlCalendar|int|null', 'pattern='=>'?string'], 'IntlDateFormatter::format' => ['string|false', 'datetime'=>'IntlCalendar|DateTimeInterface|array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int}|array{tm_sec: int, tm_min: int, tm_hour: int, tm_mday: int, tm_mon: int, tm_year: int, tm_wday: int, tm_yday: int, tm_isdst: int}|string|int|float'], @@ -5930,8 +5922,7 @@ 'IntlDateFormatter::setCalendar' => ['bool', 'calendar'=>'IntlCalendar|int|null'], 'IntlDateFormatter::setLenient' => ['void', 'lenient'=>'bool'], 'IntlDateFormatter::setPattern' => ['bool', 'pattern'=>'string'], -'IntlDateFormatter::setTimeZone' => ['null|false', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], -'IntlException::__clone' => ['void'], +'IntlDateFormatter::setTimeZone' => ['bool', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], 'IntlException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'IntlException::__toString' => ['string'], 'IntlException::__wakeup' => ['void'], @@ -6027,7 +6018,7 @@ 'IntlRuleBasedBreakIterator::next' => ['int', 'offset='=>'?int'], 'IntlRuleBasedBreakIterator::preceding' => ['int', 'offset'=>'int'], 'IntlRuleBasedBreakIterator::previous' => ['int'], -'IntlRuleBasedBreakIterator::setText' => ['?bool', 'text'=>'string'], +'IntlRuleBasedBreakIterator::setText' => ['bool', 'text'=>'string'], 'IntlTimeZone::countEquivalentIDs' => ['int|false', 'timezoneId'=>'string'], 'IntlTimeZone::createDefault' => ['IntlTimeZone'], 'IntlTimeZone::createEnumeration' => ['IntlIterator|false', 'countryOrRawOffset='=>'IntlTimeZone|string|int|float|null'], @@ -6072,7 +6063,6 @@ 'intltz_use_daylight_time' => ['bool', 'timezone'=>'IntlTimeZone'], 'intlz_create_default' => ['IntlTimeZone'], 'intval' => ['int', 'value'=>'mixed', 'base='=>'int'], -'InvalidArgumentException::__clone' => ['void'], 'InvalidArgumentException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'InvalidArgumentException::__toString' => ['string'], 'InvalidArgumentException::getCode' => ['int'], @@ -6158,7 +6148,6 @@ 'json_last_error' => ['int'], 'json_last_error_msg' => ['string'], 'json_validate' => ['bool', 'json'=>'string', 'depth='=>'positive-int', 'flags='=>'int'], -'JsonException::__clone' => ['void'], 'JsonException::__construct' => ['void', "message="=>"string", 'code='=>'int', 'previous='=>'?Throwable'], 'JsonException::__toString' => ['string'], 'JsonException::__wakeup' => ['void'], @@ -6385,7 +6374,6 @@ 'legendObj::free' => ['void'], 'legendObj::set' => ['int', 'property_name'=>'string', 'new_value'=>''], 'legendObj::updateFromString' => ['int', 'snippet'=>'string'], -'LengthException::__clone' => ['void'], 'LengthException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'LengthException::__toString' => ['string'], 'LengthException::getCode' => ['int'], @@ -6498,7 +6486,6 @@ 'log' => ['float', 'num'=>'float', 'base='=>'float'], 'log10' => ['float', 'num'=>'float'], 'log1p' => ['float', 'num'=>'float'], -'LogicException::__clone' => ['void'], 'LogicException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'LogicException::__toString' => ['string'], 'LogicException::getCode' => ['int'], @@ -8539,7 +8526,6 @@ 'OuterIterator::next' => ['void'], 'OuterIterator::rewind' => ['void'], 'OuterIterator::valid' => ['bool'], -'OutOfBoundsException::__clone' => ['void'], 'OutOfBoundsException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'OutOfBoundsException::__toString' => ['string'], 'OutOfBoundsException::getCode' => ['int'], @@ -8549,7 +8535,6 @@ 'OutOfBoundsException::getPrevious' => ['?Throwable'], 'OutOfBoundsException::getTrace' => ['list\',args?:array}>'], 'OutOfBoundsException::getTraceAsString' => ['string'], -'OutOfRangeException::__clone' => ['void'], 'OutOfRangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'OutOfRangeException::__toString' => ['string'], 'OutOfRangeException::getCode' => ['int'], @@ -8576,7 +8561,6 @@ 'outputformatObj::set' => ['int', 'property_name'=>'string', 'new_value'=>''], 'outputformatObj::setOption' => ['void', 'property_name'=>'string', 'new_value'=>'string'], 'outputformatObj::validate' => ['int'], -'OverflowException::__clone' => ['void'], 'OverflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'OverflowException::__toString' => ['string'], 'OverflowException::getCode' => ['int'], @@ -8668,7 +8652,6 @@ 'parse_ini_string' => ['array|false', 'ini_string'=>'string', 'process_sections='=>'bool', 'scanner_mode='=>'int'], 'parse_str' => ['void', 'string'=>'string', '&w_result'=>'array'], 'parse_url' => ['int|string|array|null|false', 'url'=>'string', 'component='=>'int'], -'ParseError::__clone' => ['void'], 'ParseError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'ParseError::__toString' => ['string'], 'ParseError::getCode' => ['int'], @@ -9661,7 +9644,6 @@ 'random_bytes' => ['non-empty-string', 'length'=>'positive-int'], 'random_int' => ['int', 'min'=>'int', 'max'=>'int'], 'range' => ['non-empty-array', 'start'=>'string|int|float', 'end'=>'string|int|float', 'step='=>'int<1, max>|float'], -'RangeException::__clone' => ['void'], 'RangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'RangeException::__toString' => ['string'], 'RangeException::getCode' => ['int'], @@ -10661,12 +10643,10 @@ 'ReflectionMethod::isVariadic' => ['bool'], 'ReflectionMethod::returnsReference' => ['bool'], 'ReflectionMethod::setAccessible' => ['void', 'accessible'=>'bool'], -'ReflectionNamedType::__clone' => ['void'], 'ReflectionNamedType::__toString' => ['string'], 'ReflectionNamedType::allowsNull' => ['bool'], 'ReflectionNamedType::getName' => ['string'], 'ReflectionNamedType::isBuiltin' => ['bool'], -'ReflectionObject::__clone' => ['void'], 'ReflectionObject::__construct' => ['void', 'object'=>'object'], 'ReflectionObject::__toString' => ['string'], 'ReflectionObject::getConstant' => ['mixed', 'name'=>'string'], @@ -10898,7 +10878,6 @@ 'Runkit_Sandbox_Parent::__construct' => ['void'], 'runkit_superglobals' => ['array'], 'runkit_zval_inspect' => ['array', 'value'=>'mixed'], -'RuntimeException::__clone' => ['void'], 'RuntimeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'RuntimeException::__toString' => ['string'], 'RuntimeException::getCode' => ['int'], @@ -14010,7 +13989,6 @@ 'transliterator_transliterate' => ['string|false', 'transliterator'=>'Transliterator|string', 'string'=>'string', 'start='=>'int', 'end='=>'int'], 'trigger_error' => ['bool', 'message'=>'string', 'error_level='=>'256|512|1024|16384'], 'trim' => ['string', 'string'=>'string', 'characters='=>'string'], -'TypeError::__clone' => ['void'], 'TypeError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'TypeError::__toString' => ['string'], 'TypeError::getCode' => ['int'], @@ -14233,7 +14211,6 @@ 'ui\window::setTitle' => ['', 'title'=>'string'], 'uksort' => ['true', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'], 'umask' => ['int', 'mask='=>'?int'], -'UnderflowException::__clone' => ['void'], 'UnderflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'UnderflowException::__toString' => ['string'], 'UnderflowException::getCode' => ['int'], @@ -14243,7 +14220,6 @@ 'UnderflowException::getPrevious' => ['?Throwable'], 'UnderflowException::getTrace' => ['list\',args?:array}>'], 'UnderflowException::getTraceAsString' => ['string'], -'UnexpectedValueException::__clone' => ['void'], 'UnexpectedValueException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'UnexpectedValueException::__toString' => ['string'], 'UnexpectedValueException::getCode' => ['int'], diff --git a/dictionaries/CallMap_83_delta.php b/dictionaries/CallMap_83_delta.php index eee4d2d2d3b..c5706151853 100644 --- a/dictionaries/CallMap_83_delta.php +++ b/dictionaries/CallMap_83_delta.php @@ -53,8 +53,96 @@ 'old' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool'], 'new' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool', 'options=' => 'array'], ], + 'imap_setflag_full' => [ + 'old' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'], + 'new' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'], + ], + 'imap_expunge' => [ + 'old' => ['bool', 'imap'=>'IMAP\Connection'], + 'new' => ['true', 'imap'=>'IMAP\Connection'], + ], + 'imap_gc' => [ + 'old' => ['bool', 'imap'=>'IMAP\Connection', 'flags'=>'int'], + 'new' => ['true', 'imap'=>'IMAP\Connection', 'flags'=>'int'], + ], + 'imap_undelete' => [ + 'old' => ['bool', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'], + 'new' => ['true', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'], + ], + 'imap_delete' => [ + 'old' => ['bool', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'], + 'new' => ['true', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'], + ], + 'imap_clearflag_full' => [ + 'old' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'], + 'new' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'], + ], + 'imap_close' => [ + 'old' => ['bool', 'imap'=>'IMAP\Connection', 'flags='=>'int'], + 'new' => ['true', 'imap'=>'IMAP\Connection', 'flags='=>'int'], + ], + 'intlcal_clear' => [ + 'old' => ['bool', 'calendar'=>'IntlCalendar', 'field='=>'?int'], + 'new' => ['true', 'calendar'=>'IntlCalendar', 'field='=>'?int'], + ], + 'intlcal_set_lenient' => [ + 'old' => ['bool', 'calendar'=>'IntlCalendar', 'lenient'=>'bool'], + 'new' => ['true', 'calendar'=>'IntlCalendar', 'lenient'=>'bool'], + ], + 'intlcal_set_first_day_of_week' => [ + 'old' => ['bool', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'int'], + 'new' => ['true', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'int'], + ], + 'datefmt_set_timezone' => [ + 'old' => ['false|null', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], + 'new' => ['bool', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], + ], + 'intlrulebasedbreakiterator::settext' => [ + 'old' => ['?bool', 'text'=>'string'], + 'new' => ['bool', 'text'=>'string'], + ], + 'intlcodepointbreakiterator::settext' => [ + 'old' => ['?bool', 'text'=>'string'], + 'new' => ['bool', 'text'=>'string'], + ], + 'intldateformatter::settimezone' => [ + 'old' => ['null|false', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], + 'new' => ['bool', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], + ], + 'intlchar::enumcharnames' => [ + 'old' => ['?bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'], + 'new' => ['bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'], + ], + 'intlbreakiterator::settext' => [ + 'old' => ['?bool', 'text'=>'string'], + 'new' => ['bool', 'text'=>'string'], + ], ], 'removed' => [ + 'OutOfBoundsException::__clone' => ['void'], + 'ArgumentCountError::__clone' => ['void'], + 'ArithmeticError::__clone' => ['void'], + 'BadFunctionCallException::__clone' => ['void'], + 'BadMethodCallException::__clone' => ['void'], + 'ClosedGeneratorException::__clone' => ['void'], + 'DomainException::__clone' => ['void'], + 'ErrorException::__clone' => ['void'], + 'IntlException::__clone' => ['void'], + 'InvalidArgumentException::__clone' => ['void'], + 'JsonException::__clone' => ['void'], + 'LengthException::__clone' => ['void'], + 'LogicException::__clone' => ['void'], + 'OutOfRangeException::__clone' => ['void'], + 'OverflowException::__clone' => ['void'], + 'ParseError::__clone' => ['void'], + 'RangeException::__clone' => ['void'], + 'ReflectionNamedType::__clone' => ['void'], + 'ReflectionObject::__clone' => ['void'], + 'RuntimeException::__clone' => ['void'], + 'TypeError::__clone' => ['void'], + 'UnderflowException::__clone' => ['void'], + 'UnexpectedValueException::__clone' => ['void'], + 'IntlCodePointBreakIterator::__construct' => ['void'], ], ]; diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 03c60132459..e3e99991c13 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -73,28 +73,15 @@ class InternalCallMapHandlerTest extends TestCase * @var array> */ private static array $ignoredFunctions = [ - 'argumentcounterror::__clone' => ['8.3'], - 'arithmeticerror::__clone' => ['8.3'], 'array_multisort', - 'badfunctioncallexception::__clone' => ['8.3'], - 'badmethodcallexception::__clone' => ['8.3'], - 'closedgeneratorexception::__clone' => ['8.3'], 'datefmt_create' => ['8.0'], - 'domainexception::__clone' => ['8.3'], - 'errorexception::__clone' => ['8.3'], 'fiber::start', 'imagefilledpolygon', 'imagegd', 'imagegd2', 'imageopenpolygon', 'imagepolygon', - 'intlcodepointbreakiterator::__construct' => ['8.3'], - 'intlexception::__clone' => ['8.3'], 'intlgregoriancalendar::__construct', - 'invalidargumentexception::__clone' => ['8.3'], - 'jsonexception::__clone' => ['8.3'], - 'lengthexception::__clone' => ['8.3'], - 'logicexception::__clone' => ['8.3'], 'lzf_compress', 'lzf_decompress', 'mailparse_msg_extract_part', @@ -153,15 +140,7 @@ class InternalCallMapHandlerTest extends TestCase 'oci_result', 'ocigetbufferinglob', 'ocisetbufferinglob', - 'outofboundsexception::__clone' => ['8.3'], - 'outofrangeexception::__clone' => ['8.3'], - 'overflowexception::__clone' => ['8.3'], - 'parseerror::__clone' => ['8.3'], - 'rangeexception::__clone' => ['8.3'], 'recursiveiteratoriterator::__construct', // Class used in CallMap does not exist: recursiveiterator - 'reflectionnamedtype::__clone' => ['8.3'], - 'reflectionobject::__clone' => ['8.3'], - 'runtimeexception::__clone' => ['8.3'], 'sqlsrv_fetch_array', 'sqlsrv_fetch_object', 'sqlsrv_get_field', @@ -169,9 +148,6 @@ class InternalCallMapHandlerTest extends TestCase 'sqlsrv_query', 'sqlsrv_server_info', 'ssh2_forward_accept', - 'typeerror::__clone' => ['8.3'], - 'underflowexception::__clone' => ['8.3'], - 'unexpectedvalueexception::__clone' => ['8.3'], 'xdiff_file_bdiff', 'xdiff_file_bdiff_size', 'xdiff_file_diff', @@ -251,23 +227,6 @@ class InternalCallMapHandlerTest extends TestCase 'splfileobject::fscanf' => ['8.1', '8.2', '8.3'], 'spltempfileobject::fscanf' => ['8.1', '8.2', '8.3'], 'xsltprocessor::transformtoxml' => ['8.1', '8.2', '8.3'], - 'intlcal_clear' => ['8.3'], - 'intlrulebasedbreakiterator::settext' => ['8.3'], - 'intlcodepointbreakiterator::__construct' => ['8.3'], - 'intlcodepointbreakiterator::settext' => ['8.3'], - 'intldateformatter::settimezone' => ['8.3'], - 'intlchar::enumcharnames' => ['8.3'], - 'intlbreakiterator::settext' => ['8.3'], - 'intlcal_set_lenient' => ['8.3'], - 'intlcal_set_first_day_of_week' => ['8.3'], - 'datefmt_set_timezone' => ['8.3'], - 'imap_setflag_full' => ['8.3'], - 'imap_expunge' => ['8.3'], - 'imap_gc' => ['8.3'], - 'imap_undelete' => ['8.3'], - 'imap_delete' => ['8.3'], - 'imap_clearflag_full' => ['8.3'], - 'imap_close' => ['8.3'], ]; /** From fda483a98b2621755bd23d6d3d09b6f329918ddb Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 22:42:27 +0200 Subject: [PATCH 21/39] #10026 converted version_compare() to direct comparison with PHP_VERSION_ID --- tests/DateTimeTest.php | 2 +- tests/Traits/ValidCodeAnalysisTestTrait.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/DateTimeTest.php b/tests/DateTimeTest.php index b6fbe2ef2e8..97ad20e6f9c 100644 --- a/tests/DateTimeTest.php +++ b/tests/DateTimeTest.php @@ -18,7 +18,7 @@ public function testModifyWithInvalidConstant(): void { $context = new Context(); - if (version_compare(PHP_VERSION, '8.3', '>')) { + if (PHP_VERSION_ID >= 8_03_00) { $this->expectException(Exception::class); $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (foo) at position 0 (f)'); } diff --git a/tests/Traits/ValidCodeAnalysisTestTrait.php b/tests/Traits/ValidCodeAnalysisTestTrait.php index 6f8b2f18aea..23a44a3d0c3 100644 --- a/tests/Traits/ValidCodeAnalysisTestTrait.php +++ b/tests/Traits/ValidCodeAnalysisTestTrait.php @@ -77,7 +77,7 @@ public function testValidCode( $codebase->config->visitPreloadedStubFiles($codebase); // avoid MethodSignatureMismatch for __unserialize/() when extending DateTime - if (version_compare(PHP_VERSION, '8.2', '>')) { + if (PHP_VERSION_ID >= 8_02_00) { $this->addStubFile( 'stubOne.phpstub', ' Date: Fri, 21 Jul 2023 22:46:46 +0200 Subject: [PATCH 22/39] #10026 keep running phpcs and php lint with 7.4 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ad2fd3b7ba..cf9fbf7b147 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '7.4' tools: composer:v2 coverage: none env: @@ -57,7 +57,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '7.4' tools: composer:v2 coverage: none env: From 1e1ffe164fabd1ac75f731845ae07d5f4e4f3e36 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 22:50:24 +0200 Subject: [PATCH 23/39] #10026 keep running phpcs and php lint with 7.4 --- .circleci/config.yml | 4 ++-- .github/workflows/build-phar.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9ac95561065..0c4d5026289 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,7 @@ executors: - image: thecodingmachine/php:8.1-v4-cli jobs: "Code Style Analysis": - executor: php-80 + executor: php-74 steps: - checkout @@ -41,7 +41,7 @@ jobs: command: vendor/bin/phpcs -d memory_limit=512M phar-build: - executor: php-80 + executor: php-74 steps: - attach_workspace: at: /home/docker/project/ diff --git a/.github/workflows/build-phar.yml b/.github/workflows/build-phar.yml index b042b090497..8d75dfe63c3 100644 --- a/.github/workflows/build-phar.yml +++ b/.github/workflows/build-phar.yml @@ -38,7 +38,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '7.4' tools: composer:v2 coverage: none env: From 603dfa2e5a819ef1adf98afdccf785f6a133829e Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 22:57:49 +0200 Subject: [PATCH 24/39] #10026 cleanup phpcs --- tests/DateTimeTest.php | 6 ++---- tests/Traits/ValidCodeAnalysisTestTrait.php | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/DateTimeTest.php b/tests/DateTimeTest.php index 97ad20e6f9c..d8c60882079 100644 --- a/tests/DateTimeTest.php +++ b/tests/DateTimeTest.php @@ -6,9 +6,7 @@ use Psalm\Context; use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait; -use function version_compare; - -use const PHP_VERSION; +use const PHP_VERSION_ID; class DateTimeTest extends TestCase { @@ -51,7 +49,7 @@ public function testModifyWithBothConstant(): void { $context = new Context(); - if (version_compare(PHP_VERSION, '8.3', '>')) { + if (PHP_VERSION_ID >= 8_03_00) { $this->expectException(Exception::class); $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (bar) at position 0 (b)'); } diff --git a/tests/Traits/ValidCodeAnalysisTestTrait.php b/tests/Traits/ValidCodeAnalysisTestTrait.php index 23a44a3d0c3..b37acced357 100644 --- a/tests/Traits/ValidCodeAnalysisTestTrait.php +++ b/tests/Traits/ValidCodeAnalysisTestTrait.php @@ -14,6 +14,7 @@ use const PHP_OS; use const PHP_VERSION; +use const PHP_VERSION_ID; trait ValidCodeAnalysisTestTrait { From 637dcc4425c809793854dd28fc9b45a0bb872d37 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Fri, 21 Jul 2023 23:11:20 +0200 Subject: [PATCH 25/39] #10026 fixed case mismatches in CallMap_83_delta --- dictionaries/CallMap_83_delta.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dictionaries/CallMap_83_delta.php b/dictionaries/CallMap_83_delta.php index c5706151853..9bcf76deece 100644 --- a/dictionaries/CallMap_83_delta.php +++ b/dictionaries/CallMap_83_delta.php @@ -97,23 +97,23 @@ 'old' => ['false|null', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], 'new' => ['bool', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], ], - 'intlrulebasedbreakiterator::settext' => [ + 'IntlRuleBasedBreakIterator::setText' => [ 'old' => ['?bool', 'text'=>'string'], 'new' => ['bool', 'text'=>'string'], ], - 'intlcodepointbreakiterator::settext' => [ + 'IntlCodePointBreakIterator::setText' => [ 'old' => ['?bool', 'text'=>'string'], 'new' => ['bool', 'text'=>'string'], ], - 'intldateformatter::settimezone' => [ + 'IntlDateFormatter::setTimeZone' => [ 'old' => ['null|false', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], 'new' => ['bool', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], ], - 'intlchar::enumcharnames' => [ + 'IntlChar::enumCharNames' => [ 'old' => ['?bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'], 'new' => ['bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'], ], - 'intlbreakiterator::settext' => [ + 'IntlBreakIterator::setText' => [ 'old' => ['?bool', 'text'=>'string'], 'new' => ['bool', 'text'=>'string'], ], From 902f90b19b5a55a0c703a701b6b339d28eb43f99 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 25 Jul 2023 21:56:52 +0200 Subject: [PATCH 26/39] Modernize LSP progress reporting This will use `$/progress` when available and fall back to old telemetry-based reporting otherwise --- .../Client/Progress/LegacyProgress.php | 60 +++++++++++ .../Client/Progress/Progress.php | 102 ++++++++++++++++++ .../Client/Progress/ProgressInterface.php | 16 +++ .../LanguageServer/LanguageClient.php | 12 +++ .../LanguageServer/LanguageServer.php | 18 ++-- 5 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php create mode 100644 src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php create mode 100644 src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php diff --git a/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php b/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php new file mode 100644 index 00000000000..8bbc1541002 --- /dev/null +++ b/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php @@ -0,0 +1,60 @@ +handler = $handler; + } + + public function begin(string $title, ?string $message = null, ?int $percentage = null): void + { + if ($this->title !== null) { + throw new LogicException('Progress has already been started'); + } + + $this->title = $title; + + $this->notify($message); + } + + public function update(?string $message = null, ?int $percentage = null): void + { + if ($this->title === null) { + throw new LogicException('The progress has not been started yet'); + } + + $this->notify($message); + } + + public function end(?string $message = null): void + { + if ($this->title === null) { + throw new LogicException('The progress has not been started yet'); + } + + $this->notify($message); + } + + private function notify(?string $message): void + { + $this->handler->notify( + 'telemetry/event', + new LogMessage( + MessageType::INFO, + $this->title . (empty($message) ? '' : (': ' . $message)), + ), + ); + } +} diff --git a/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php b/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php new file mode 100644 index 00000000000..edb6428e4ee --- /dev/null +++ b/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php @@ -0,0 +1,102 @@ +handler = $handler; + $this->token = $token; + } + + public function begin( + string $title, + ?string $message = null, + ?int $percentage = null + ): void { + if ($this->finished) { + throw new LogicException('Progress has already been finished'); + } + + $notification = [ + 'token' => $this->token, + 'value' => [ + 'kind' => 'begin', + 'title' => $title, + ], + ]; + + if ($message !== null) { + $notification['value']['message'] = $message; + } + + if ($percentage !== null) { + $notification['value']['percentage'] = $percentage; + $this->withPercentage = true; + } + + $this->handler->notify('$/progress', $notification); + } + + public function end(?string $message = null): void + { + if ($this->finished) { + throw new LogicException('Progress has already been finished'); + } + + $notification = [ + 'token' => $this->token, + 'value' => [ + 'kind' => 'end', + ], + ]; + + if ($message !== null) { + $notification['value']['message'] = $message; + } + + $this->handler->notify('$/progress', $notification); + + $this->finished = true; + } + + public function update(?string $message = null, ?int $percentage = null): void + { + if ($this->finished) { + throw new LogicException('Progress has already been finished'); + } + + $notification = [ + 'token' => $this->token, + 'value' => [ + 'kind' => 'report', + ], + ]; + + if ($message !== null) { + $notification['value']['message'] = $message; + } + + if ($percentage !== null) { + if (!$this->withPercentage) { + throw new LogicException( + 'Cannot update percentage for progress ' + . 'that was started without percentage', + ); + } + $notification['value']['percentage'] = $percentage; + } + + $this->handler->notify('$/progress', $notification); + } +} diff --git a/src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php b/src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php new file mode 100644 index 00000000000..78f045c2c53 --- /dev/null +++ b/src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php @@ -0,0 +1,16 @@ +server->clientCapabilities->window->workDoneProgress ?? false) { + return new Progress($this->handler, $token); + } else { + return new LegacyProgress($this->handler); + } + } + /** * Configuration Refreshed from Client * diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index 3fa4261c8d3..995e9b21065 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -88,6 +88,7 @@ use function strpos; use function substr; use function trim; +use function uniqid; use function urldecode; use const JSON_PRETTY_PRINT; @@ -398,7 +399,8 @@ public function initialize( ?string $rootPath = null, ?string $rootUri = null, $initializationOptions = null, - ?string $trace = null + ?string $trace = null, + ?string $workdDoneToken = null //?array $workspaceFolders = null //error in json-dispatcher ): Promise { $this->clientInfo = $clientInfo; @@ -412,9 +414,11 @@ public function initialize( return call( /** @return Generator */ - function () { + function () use ($workdDoneToken) { + $progress = $this->client->makeProgress($workdDoneToken ?? uniqid('tkn', true)); + $this->logInfo("Initializing..."); - $this->clientStatus('initializing'); + $progress->begin('Initialization', 'Starting'); // Eventually, this might block on something. Leave it as a generator. /** @psalm-suppress TypeDoesNotContainType */ @@ -425,14 +429,14 @@ function () { $this->project_analyzer->serverMode($this); $this->logInfo("Initializing: Getting code base..."); - $this->clientStatus('initializing', 'getting code base'); + $progress->update('Getting code base'); $this->logInfo("Initializing: Scanning files ({$this->project_analyzer->threads} Threads)..."); - $this->clientStatus('initializing', 'scanning files'); + $progress->update('Scanning files'); $this->codebase->scanFiles($this->project_analyzer->threads); $this->logInfo("Initializing: Registering stub files..."); - $this->clientStatus('initializing', 'registering stub files'); + $progress->update('Registering stub files'); $this->codebase->config->visitStubFiles($this->codebase, $this->project_analyzer->progress); if ($this->textDocument === null) { @@ -572,7 +576,7 @@ function () { } $this->logInfo("Initializing: Complete."); - $this->clientStatus('initialized'); + $progress->end('Initialized'); /** * Information about the server. From 5f828c75d1a3eae6307fbf4e524485bb0fb66c57 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 25 Jul 2023 23:10:16 +0200 Subject: [PATCH 27/39] Make sure we get the token Name does matter, as is the presence of docblock --- src/Psalm/Internal/LanguageServer/LanguageServer.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index 995e9b21065..3c09b091adc 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -388,6 +388,7 @@ public static function run( * @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open. * @param mixed $initializationOptions * @param string|null $trace The initial trace setting. If omitted trace is disabled ('off'). + * @param string|null $workDoneToken The token to be used to report progress during init. * @psalm-return Promise * @psalm-suppress PossiblyUnusedParam */ @@ -400,7 +401,7 @@ public function initialize( ?string $rootUri = null, $initializationOptions = null, ?string $trace = null, - ?string $workdDoneToken = null + ?string $workDoneToken = null //?array $workspaceFolders = null //error in json-dispatcher ): Promise { $this->clientInfo = $clientInfo; @@ -414,8 +415,8 @@ public function initialize( return call( /** @return Generator */ - function () use ($workdDoneToken) { - $progress = $this->client->makeProgress($workdDoneToken ?? uniqid('tkn', true)); + function () use ($workDoneToken) { + $progress = $this->client->makeProgress($workDoneToken ?? uniqid('tkn', true)); $this->logInfo("Initializing..."); $progress->begin('Initialization', 'Starting'); From 85b2af83daffbe1b18ecb51ef77c52b6f3c39658 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 25 Jul 2023 23:13:31 +0200 Subject: [PATCH 28/39] Cosmetic changes for VSCode --- src/Psalm/Internal/LanguageServer/LanguageServer.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index 3c09b091adc..af1a2a7b116 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -419,7 +419,7 @@ function () use ($workDoneToken) { $progress = $this->client->makeProgress($workDoneToken ?? uniqid('tkn', true)); $this->logInfo("Initializing..."); - $progress->begin('Initialization', 'Starting'); + $progress->begin('Psalm', 'initializing'); // Eventually, this might block on something. Leave it as a generator. /** @psalm-suppress TypeDoesNotContainType */ @@ -430,14 +430,14 @@ function () use ($workDoneToken) { $this->project_analyzer->serverMode($this); $this->logInfo("Initializing: Getting code base..."); - $progress->update('Getting code base'); + $progress->update('getting code base'); $this->logInfo("Initializing: Scanning files ({$this->project_analyzer->threads} Threads)..."); - $progress->update('Scanning files'); + $progress->update('scanning files'); $this->codebase->scanFiles($this->project_analyzer->threads); $this->logInfo("Initializing: Registering stub files..."); - $progress->update('Registering stub files'); + $progress->update('registering stub files'); $this->codebase->config->visitStubFiles($this->codebase, $this->project_analyzer->progress); if ($this->textDocument === null) { @@ -577,7 +577,7 @@ function () use ($workDoneToken) { } $this->logInfo("Initializing: Complete."); - $progress->end('Initialized'); + $progress->end('initialized'); /** * Information about the server. From 4f6fc3585b650956255f91633bec121de3604c70 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 25 Jul 2023 23:50:07 +0200 Subject: [PATCH 29/39] Update tests to account for new progress protocol --- tests/LanguageServer/DiagnosticTest.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/LanguageServer/DiagnosticTest.php b/tests/LanguageServer/DiagnosticTest.php index b40ef38ace3..d8814ec3633 100644 --- a/tests/LanguageServer/DiagnosticTest.php +++ b/tests/LanguageServer/DiagnosticTest.php @@ -91,10 +91,21 @@ public function testSnippetSupportDisabled(): void ); $write->on('message', function (Message $message) use ($deferred, $server): void { - /** @psalm-suppress PossiblyNullPropertyFetch,UndefinedPropertyFetch,MixedPropertyFetch */ - if ($message->body->method === 'telemetry/event' && $message->body->params->message === 'initialized') { + /** @psalm-suppress NullPropertyFetch,PossiblyNullPropertyFetch,UndefinedPropertyFetch */ + if ($message->body->method === 'telemetry/event' && ($message->body->params->message ?? null) === 'initialized') { $this->assertFalse($server->clientCapabilities->textDocument->completion->completionItem->snippetSupport); $deferred->resolve(null); + return; + } + + /** @psalm-suppress NullPropertyFetch,PossiblyNullPropertyFetch */ + if ($message->body->method === '$/progress' + && ($message->body->params->value->kind ?? null) === 'end' + && ($message->body->params->value->message ?? null) === 'initialized' + ) { + $this->assertFalse($server->clientCapabilities->textDocument->completion->completionItem->snippetSupport); + $deferred->resolve(null); + return; } }); From c2a05c2e9002b6e4854194b713171f14941cc268 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 26 Jul 2023 03:59:59 +0200 Subject: [PATCH 30/39] Ensure correct method call order --- .../Client/Progress/LegacyProgress.php | 33 ++++++++++++++++--- .../Client/Progress/Progress.php | 29 +++++++++++++--- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php b/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php index 8bbc1541002..13eba280c7e 100644 --- a/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php +++ b/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php @@ -10,6 +10,12 @@ /** @internal */ final class LegacyProgress implements ProgressInterface { + private const STATUS_INACTIVE = 'inactive'; + private const STATUS_ACTIVE = 'active'; + private const STATUS_FINISHED = 'finished'; + + private string $status = self::STATUS_INACTIVE; + private ClientHandler $handler; private ?string $title = null; @@ -20,19 +26,30 @@ public function __construct(ClientHandler $handler) public function begin(string $title, ?string $message = null, ?int $percentage = null): void { - if ($this->title !== null) { + + if ($this->status === self::STATUS_ACTIVE) { throw new LogicException('Progress has already been started'); } + if ($this->status === self::STATUS_FINISHED) { + throw new LogicException('Progress has already been finished'); + } + $this->title = $title; $this->notify($message); + + $this->status = self::STATUS_ACTIVE; } public function update(?string $message = null, ?int $percentage = null): void { - if ($this->title === null) { - throw new LogicException('The progress has not been started yet'); + if ($this->status === self::STATUS_FINISHED) { + throw new LogicException('Progress has already been finished'); + } + + if ($this->status === self::STATUS_INACTIVE) { + throw new LogicException('Progress has not been started yet'); } $this->notify($message); @@ -40,11 +57,17 @@ public function update(?string $message = null, ?int $percentage = null): void public function end(?string $message = null): void { - if ($this->title === null) { - throw new LogicException('The progress has not been started yet'); + if ($this->status === self::STATUS_FINISHED) { + throw new LogicException('Progress has already been finished'); + } + + if ($this->status === self::STATUS_INACTIVE) { + throw new LogicException('Progress has not been started yet'); } $this->notify($message); + + $this->status = self::STATUS_FINISHED; } private function notify(?string $message): void diff --git a/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php b/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php index edb6428e4ee..8a1ca5da3a9 100644 --- a/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php +++ b/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php @@ -8,10 +8,15 @@ /** @internal */ final class Progress implements ProgressInterface { + private const STATUS_INACTIVE = 'inactive'; + private const STATUS_ACTIVE = 'active'; + private const STATUS_FINISHED = 'finished'; + + private string $status = self::STATUS_INACTIVE; + private ClientHandler $handler; private string $token; private bool $withPercentage = false; - private bool $finished = false; public function __construct(ClientHandler $handler, string $token) { @@ -24,7 +29,11 @@ public function begin( ?string $message = null, ?int $percentage = null ): void { - if ($this->finished) { + if ($this->status === self::STATUS_ACTIVE) { + throw new LogicException('Progress has already been started'); + } + + if ($this->status === self::STATUS_FINISHED) { throw new LogicException('Progress has already been finished'); } @@ -46,14 +55,20 @@ public function begin( } $this->handler->notify('$/progress', $notification); + + $this->status = self::STATUS_ACTIVE; } public function end(?string $message = null): void { - if ($this->finished) { + if ($this->status === self::STATUS_FINISHED) { throw new LogicException('Progress has already been finished'); } + if ($this->status === self::STATUS_INACTIVE) { + throw new LogicException('Progress has not been started yet'); + } + $notification = [ 'token' => $this->token, 'value' => [ @@ -67,15 +82,19 @@ public function end(?string $message = null): void $this->handler->notify('$/progress', $notification); - $this->finished = true; + $this->status = self::STATUS_FINISHED; } public function update(?string $message = null, ?int $percentage = null): void { - if ($this->finished) { + if ($this->status === self::STATUS_FINISHED) { throw new LogicException('Progress has already been finished'); } + if ($this->status === self::STATUS_INACTIVE) { + throw new LogicException('Progress has not been started yet'); + } + $notification = [ 'token' => $this->token, 'value' => [ From a34222aa76b4bbeeccd8d58e501297808b0702ba Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 26 Jul 2023 04:19:46 +0200 Subject: [PATCH 31/39] Drop unused parameters for RPC methods Parameters are marshalled using their names and docblock tags, so it's safe to do. --- src/Psalm/Internal/LanguageServer/LanguageServer.php | 12 ------------ .../Internal/LanguageServer/Server/TextDocument.php | 4 +--- .../Internal/LanguageServer/Server/Workspace.php | 5 ++--- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index af1a2a7b116..cc358753644 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -378,31 +378,19 @@ public static function run( * The initialize request is sent as the first request from the client to the server. * * @param ClientCapabilities $capabilities The capabilities provided by the client (editor) - * @param int|null $processId The process Id of the parent process that started the server. * Is null if the process has not been started by another process. If the parent process is * not alive then the server should exit (see exit notification) its process. * @param ClientInfo|null $clientInfo Information about the client - * @param string|null $locale The locale the client is currently showing the user interface - * in. This must not necessarily be the locale of the operating - * system. - * @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open. - * @param mixed $initializationOptions * @param string|null $trace The initial trace setting. If omitted trace is disabled ('off'). * @param string|null $workDoneToken The token to be used to report progress during init. * @psalm-return Promise - * @psalm-suppress PossiblyUnusedParam */ public function initialize( ClientCapabilities $capabilities, - ?int $processId = null, ?ClientInfo $clientInfo = null, - ?string $locale = null, - ?string $rootPath = null, ?string $rootUri = null, - $initializationOptions = null, ?string $trace = null, ?string $workDoneToken = null - //?array $workspaceFolders = null //error in json-dispatcher ): Promise { $this->clientInfo = $clientInfo; $this->clientCapabilities = $capabilities; diff --git a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index b24862e7460..508af5aeb9f 100644 --- a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -398,10 +398,8 @@ public function signatureHelp(TextDocumentIdentifier $textDocument, Position $po * The code action request is sent from the client to the server to compute commands * for a given text document and range. These commands are typically code fixes to * either fix problems or to beautify/refactor code. - * - * @psalm-suppress PossiblyUnusedParam */ - public function codeAction(TextDocumentIdentifier $textDocument, Range $range, CodeActionContext $context): Promise + public function codeAction(TextDocumentIdentifier $textDocument, CodeActionContext $context): Promise { if (!$this->server->client->clientConfiguration->provideCodeActions) { return new Success(null); diff --git a/src/Psalm/Internal/LanguageServer/Server/Workspace.php b/src/Psalm/Internal/LanguageServer/Server/Workspace.php index 113a8f17974..6d2e1622575 100644 --- a/src/Psalm/Internal/LanguageServer/Server/Workspace.php +++ b/src/Psalm/Internal/LanguageServer/Server/Workspace.php @@ -108,10 +108,9 @@ public function didChangeWatchedFiles(array $changes): void /** * A notification sent from the client to the server to signal the change of configuration settings. * - * @param mixed $settings - * @psalm-suppress PossiblyUnusedMethod, PossiblyUnusedParam + * @psalm-suppress PossiblyUnusedMethod */ - public function didChangeConfiguration($settings): void + public function didChangeConfiguration(): void { $this->server->logDebug( 'workspace/didChangeConfiguration', From fc74ae83e66ef2ce329269aa4d123690f6319312 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 27 Jul 2023 00:40:26 +0200 Subject: [PATCH 32/39] #9974 added return type detection for PDOStatement::fetchAll, extended return type detection for PDOStatement::fetch --- .../PdoStatementReturnTypeProvider.php | 311 ++++++++++++++---- tests/MethodCallTest.php | 111 ++++++- 2 files changed, 351 insertions(+), 71 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index 0e09aa85a7c..3f1addedc85 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -27,88 +27,261 @@ public static function getClassLikeNames(): array public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union { $config = Config::getInstance(); + $method_name_lowercase = $event->getMethodNameLowercase(); + + if (!$config->php_extensions["pdo"]) { + return null; + } + + if ($method_name_lowercase === 'setfetchmode') { + return self::handleSetFetchMode($event); + } + + if ($method_name_lowercase === 'fetch') { + return self::handleFetch($event); + } + + if ($method_name_lowercase === 'fetchall') { + return self::handleFetchAll($event); + } + + return null; + } + + private static function handleSetFetchMode(MethodReturnTypeProviderEvent $event) + { $source = $event->getSource(); $call_args = $event->getCallArgs(); - $method_name_lowercase = $event->getMethodNameLowercase(); - if ($method_name_lowercase === 'fetch' - && $config->php_extensions["pdo"] - && isset($call_args[0]) + $context = $event->getContext(); + + if (isset($call_args[0]) + && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value)) + && $first_arg_type->isSingleIntLiteral() + ) { + $context->references_in_scope['fetch_mode'] = $first_arg_type->getSingleIntLiteral()->value; + } + + if (isset($call_args[1]) + && ($second_arg_type = $source->getNodeTypeProvider()->getType($call_args[1]->value)) + && $second_arg_type->isSingleStringLiteral() + ) { + $context->references_in_scope['fetch_class'] = $second_arg_type->getSingleStringLiteral()->value; + } + + return null; + } + + private static function handleFetch(MethodReturnTypeProviderEvent $event): ?Union + { + $source = $event->getSource(); + $call_args = $event->getCallArgs(); + $context = $event->getContext(); + + $fetch_mode = $context->references_in_scope['fetch_mode'] ?? null; + + if (isset($call_args[0]) && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value)) && $first_arg_type->isSingleIntLiteral() ) { $fetch_mode = $first_arg_type->getSingleIntLiteral()->value; + } - switch ($fetch_mode) { - case 2: // PDO::FETCH_ASSOC - array|false - return new Union([ - new TArray([ - Type::getString(), - new Union([ - new TScalar(), - new TNull(), - ]), + $fetch_class_name = $context->references_in_scope['fetch_class'] ?? null; + + switch ($fetch_mode) { + case 2: // PDO::FETCH_ASSOC - array|false + return new Union([ + new TArray([ + Type::getString(), + new Union([ + new TScalar(), + new TNull(), ]), - new TFalse(), - ]); - - case 4: // PDO::FETCH_BOTH - array|false - return new Union([ - new TArray([ - Type::getArrayKey(), - new Union([ - new TScalar(), - new TNull(), + ]), + new TFalse(), + ]); + + case 4: // PDO::FETCH_BOTH - array|false + return new Union([ + new TArray([ + Type::getArrayKey(), + new Union([ + new TScalar(), + new TNull(), + ]), + ]), + new TFalse(), + ]); + + case 6: // PDO::FETCH_BOUND - bool + return Type::getBool(); + + case 8: // PDO::FETCH_CLASS - object|false + return new Union([ + $fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject(), + new TFalse(), + ]); + + case 1: // PDO::FETCH_LAZY - object|false + // This actually returns a PDORow object, but that class is + // undocumented, and its attributes are all dynamic anyway + return new Union([ + new TObject(), + new TFalse(), + ]); + + case 11: // PDO::FETCH_NAMED - array>|false + return new Union([ + new TArray([ + Type::getString(), + new Union([ + new TScalar(), + Type::getListAtomic(Type::getScalar()), + ]), + ]), + new TFalse(), + ]); + + case 3: // PDO::FETCH_NUM - list|false + return new Union([ + Type::getListAtomic( + new Union([ + new TScalar(), + new TNull(), + ]), + ), + new TFalse(), + ]); + + case 5: // PDO::FETCH_OBJ - stdClass|false + return new Union([ + new TNamedObject('stdClass'), + new TFalse(), + ]); + } + + return null; + } + + private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?Union + { + $source = $event->getSource(); + $call_args = $event->getCallArgs(); + $context = $event->getContext(); + + $fetch_mode = $context->references_in_scope['fetch_mode'] ?? null; + + if (isset($call_args[0]) + && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value)) + && $first_arg_type->isSingleIntLiteral() + ) { + $fetch_mode = $first_arg_type->getSingleIntLiteral()->value; + } + + $fetch_class_name = $context->references_in_scope['fetch_class'] ?? null; + + if (isset($call_args[1]) + && ($second_arg_type = $source->getNodeTypeProvider()->getType($call_args[1]->value)) + && $second_arg_type->isSingleStringLiteral() + ) { + $fetch_class_name = $second_arg_type->getSingleStringLiteral()->value; + } + + switch ($fetch_mode) { + case 2: // PDO::FETCH_ASSOC - list> + return new Union([ + Type::getListAtomic( + new Union([ + new TArray([ + Type::getString(), + new Union([ + new TScalar(), + new TNull(), + ]), ]), ]), - new TFalse(), - ]); - - case 6: // PDO::FETCH_BOUND - bool - return Type::getBool(); - - case 8: // PDO::FETCH_CLASS - object|false - return new Union([ - new TObject(), - new TFalse(), - ]); - - case 1: // PDO::FETCH_LAZY - object|false - // This actually returns a PDORow object, but that class is - // undocumented, and its attributes are all dynamic anyway - return new Union([ - new TObject(), - new TFalse(), - ]); - - case 11: // PDO::FETCH_NAMED - array>|false - return new Union([ - new TArray([ - Type::getString(), - new Union([ - new TScalar(), - Type::getListAtomic(Type::getScalar()), + ), + ]); + + case 4: // PDO::FETCH_BOTH - list> + return new Union([ + Type::getListAtomic( + new Union([ + new TArray([ + Type::getArrayKey(), + new Union([ + new TScalar(), + new TNull(), + ]), ]), ]), - new TFalse(), - ]); - - case 3: // PDO::FETCH_NUM - list|false - return new Union([ - Type::getListAtomic( - new Union([ - new TScalar(), - new TNull(), + ), + ]); + + case 6: // PDO::FETCH_BOUND - list + return new Union([ + Type::getListAtomic( + Type::getBool() + ), + ]); + + case 8: // PDO::FETCH_CLASS - list + return new Union([ + Type::getListAtomic( + new Union([ + $fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject() + ]), + ), + ]); + + case 1: // PDO::FETCH_LAZY - list + // This actually returns a PDORow object, but that class is + // undocumented, and its attributes are all dynamic anyway + return new Union([ + Type::getListAtomic( + new Union([ + new TObject() + ]), + ), + ]); + + case 11: // PDO::FETCH_NAMED - list>> + return new Union([ + Type::getListAtomic( + new Union([ + new TArray([ + Type::getString(), + new Union([ + new TScalar(), + Type::getListAtomic(Type::getScalar()), + ]), ]), - ), - new TFalse(), - ]); - - case 5: // PDO::FETCH_OBJ - stdClass|false - return new Union([ - new TNamedObject('stdClass'), - new TFalse(), - ]); - } + ]), + ), + ]); + + case 3: // PDO::FETCH_NUM - list> + return new Union([ + Type::getListAtomic( + new Union([ + Type::getListAtomic( + new Union([ + new TScalar(), + new TNull(), + ]), + ), + ]), + ), + ]); + + case 5: // PDO::FETCH_OBJ - list + return new Union([ + Type::getListAtomic( + new Union([ + new TNamedObject('stdClass') + ]), + ), + ]); } return null; diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 3b656752bcf..760d93b65b5 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -503,14 +503,29 @@ class A { /** @var ?string */ public $a; } + class B extends A {} $db = new PDO("sqlite::memory:"); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt = $db->prepare("select \"a\" as a"); $stmt->setFetchMode(PDO::FETCH_CLASS, A::class); $stmt->execute(); - /** @psalm-suppress MixedAssignment */ - $a = $stmt->fetch();', + $a = $stmt->fetch(); + $b = $stmt->fetchAll(); + $c = $stmt->fetch(PDO::FETCH_CLASS); + $d = $stmt->fetchAll(PDO::FETCH_CLASS); + $e = $stmt->fetchAll(PDO::FETCH_CLASS, B::class); + $f = $stmt->fetch(PDO::FETCH_ASSOC); + $g = $stmt->fetchAll(PDO::FETCH_ASSOC);', + 'assertions' => [ + '$a' => 'A|false', + '$b' => 'list', + '$c' => 'A|false', + '$d' => 'list', + '$e' => 'list', + '$f' => 'array|false', + '$g' => 'list>', + ], ], 'datePeriodConstructor' => [ 'code' => 'fetch(PDO::FETCH_ASSOC); }', ], + 'pdoStatementFetchAllAssoc' => [ + 'code' => '> */ + function fetch_assoc() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_ASSOC); + }', + ], 'pdoStatementFetchBoth' => [ 'code' => '|false */ @@ -627,6 +652,16 @@ function fetch_both() { return $sth->fetch(PDO::FETCH_BOTH); }', ], + 'pdoStatementFetchAllBoth' => [ + 'code' => '> */ + function fetch_both() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_BOTH); + }', + ], 'pdoStatementFetchBound' => [ 'code' => 'fetch(PDO::FETCH_BOUND); }', ], + 'pdoStatementFetchAllBound' => [ + 'code' => ' */ + function fetch_both() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_BOUND); + }', + ], 'pdoStatementFetchClass' => [ 'code' => 'fetch(PDO::FETCH_CLASS); }', ], + 'pdoStatementFetchAllClass' => [ + 'code' => ' */ + function fetch_class() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_CLASS); + }', + ], + 'pdoStatementFetchAllNamedClass' => [ + 'code' => ' */ + function fetch_class() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_CLASS, Foo::class); + }', + ], 'pdoStatementFetchLazy' => [ 'code' => 'fetch(PDO::FETCH_LAZY); }', ], + 'pdoStatementFetchAllLazy' => [ + 'code' => ' */ + function fetch_lazy() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_LAZY); + }', + ], 'pdoStatementFetchNamed' => [ 'code' => '>|false */ @@ -667,6 +744,16 @@ function fetch_named() { return $sth->fetch(PDO::FETCH_NAMED); }', ], + 'pdoStatementFetchAllNamed' => [ + 'code' => '>> */ + function fetch_named() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_NAMED); + }', + ], 'pdoStatementFetchNum' => [ 'code' => '|false */ @@ -677,6 +764,16 @@ function fetch_named() { return $sth->fetch(PDO::FETCH_NUM); }', ], + 'pdoStatementFetchAllNum' => [ + 'code' => '> */ + function fetch_named() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_NUM); + }', + ], 'pdoStatementFetchObj' => [ 'code' => 'fetch(PDO::FETCH_OBJ); }', ], + 'pdoStatementFetchAllObj' => [ + 'code' => ' */ + function fetch_named() : array { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_OBJ); + }', + ], 'dateTimeSecondArg' => [ 'code' => ' Date: Thu, 27 Jul 2023 01:39:21 +0200 Subject: [PATCH 33/39] #9974 extended return type detection for PDOStatement::fetchAll/fetch with FETCH_COLUMN and FETCH_KEY_PAIR --- .../PdoStatementReturnTypeProvider.php | 77 ++++++++++++++----- tests/MethodCallTest.php | 54 ++++++++++--- 2 files changed, 101 insertions(+), 30 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index 3f1addedc85..2c245caa157 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -116,6 +116,13 @@ private static function handleFetch(MethodReturnTypeProviderEvent $event): ?Unio case 6: // PDO::FETCH_BOUND - bool return Type::getBool(); + case 7: // PDO::FETCH_COLUMN - scalar|null|false + return new Union([ + new TScalar(), + new TNull(), + new TFalse(), + ]); + case 8: // PDO::FETCH_CLASS - object|false return new Union([ $fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject(), @@ -130,18 +137,35 @@ private static function handleFetch(MethodReturnTypeProviderEvent $event): ?Unio new TFalse(), ]); - case 11: // PDO::FETCH_NAMED - array>|false + case 11: // PDO::FETCH_NAMED - array>|false return new Union([ new TArray([ Type::getString(), new Union([ new TScalar(), - Type::getListAtomic(Type::getScalar()), + new TNull(), + Type::getListAtomic( + new Union([ + new TScalar(), + new TNull(), + ]) + ), ]), ]), new TFalse(), ]); + case 12: // PDO::FETCH_KEY_PAIR - array + return new Union([ + new TArray([ + Type::getArrayKey(), + new Union([ + new TScalar(), + new TNull(), + ]), + ]), + ]); + case 3: // PDO::FETCH_NUM - list|false return new Union([ Type::getListAtomic( @@ -199,7 +223,7 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U new TNull(), ]), ]), - ]), + ]) ), ]); @@ -214,7 +238,7 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U new TNull(), ]), ]), - ]), + ]) ), ]); @@ -225,27 +249,27 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U ), ]); - case 8: // PDO::FETCH_CLASS - list + case 7: // PDO::FETCH_COLUMN - scalar|null|false return new Union([ Type::getListAtomic( new Union([ - $fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject() - ]), + new TScalar(), + new TNull(), + new TFalse(), + ]) ), ]); - case 1: // PDO::FETCH_LAZY - list - // This actually returns a PDORow object, but that class is - // undocumented, and its attributes are all dynamic anyway + case 8: // PDO::FETCH_CLASS - list return new Union([ Type::getListAtomic( new Union([ - new TObject() - ]), + $fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject() + ]) ), ]); - case 11: // PDO::FETCH_NAMED - list>> + case 11: // PDO::FETCH_NAMED - list>> return new Union([ Type::getListAtomic( new Union([ @@ -253,13 +277,30 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U Type::getString(), new Union([ new TScalar(), - Type::getListAtomic(Type::getScalar()), + new TNull(), + Type::getListAtomic( + new Union([ + new TScalar(), + new TNull(), + ]) + ), ]), ]), - ]), + ]) ), ]); + case 12: // PDO::FETCH_KEY_PAIR - array + return new Union([ + new TArray([ + Type::getArrayKey(), + new Union([ + new TScalar(), + new TNull(), + ]), + ]), + ]); + case 3: // PDO::FETCH_NUM - list> return new Union([ Type::getListAtomic( @@ -268,9 +309,9 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U new Union([ new TScalar(), new TNull(), - ]), + ]) ), - ]), + ]) ), ]); @@ -279,7 +320,7 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U Type::getListAtomic( new Union([ new TNamedObject('stdClass') - ]), + ]) ), ]); } diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 760d93b65b5..f15f55ab489 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -622,6 +622,46 @@ function foo(string $foo): string { return $foo; }', ], + 'pdoStatementFetchColumn' => [ + 'code' => 'prepare("SELECT 1"); + $sth->execute(); + return $sth->fetch(PDO::FETCH_COLUMN); + }', + ], + 'pdoStatementFetchAllColumn' => [ + 'code' => ' */ + function fetch_column() { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_COLUMN); + }', + ], + 'pdoStatementFetchKeyPair' => [ + 'code' => ' */ + function fetch_column() { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetch(PDO::FETCH_KEY_PAIR); + }', + ], + 'pdoStatementFetchAllKeyPair' => [ + 'code' => ' */ + function fetch_column() { + $p = new PDO("sqlite::memory:"); + $sth = $p->prepare("SELECT 1"); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_KEY_PAIR); + }', + ], 'pdoStatementFetchAssoc' => [ 'code' => '|false */ @@ -724,19 +764,9 @@ function fetch_lazy() { return $sth->fetch(PDO::FETCH_LAZY); }', ], - 'pdoStatementFetchAllLazy' => [ - 'code' => ' */ - function fetch_lazy() : array { - $p = new PDO("sqlite::memory:"); - $sth = $p->prepare("SELECT 1"); - $sth->execute(); - return $sth->fetchAll(PDO::FETCH_LAZY); - }', - ], 'pdoStatementFetchNamed' => [ 'code' => '>|false */ + /** @return array>|false */ function fetch_named() { $p = new PDO("sqlite::memory:"); $sth = $p->prepare("SELECT 1"); @@ -746,7 +776,7 @@ function fetch_named() { ], 'pdoStatementFetchAllNamed' => [ 'code' => '>> */ + /** @return list>> */ function fetch_named() : array { $p = new PDO("sqlite::memory:"); $sth = $p->prepare("SELECT 1"); From 8a6774ba82f8e8b952b8d89f00ce56ac03a19fcb Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 27 Jul 2023 01:45:21 +0200 Subject: [PATCH 34/39] #9974 fixed coding styles --- .../PdoStatementReturnTypeProvider.php | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index 2c245caa157..11c49f42ee0 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -48,7 +48,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event) return null; } - private static function handleSetFetchMode(MethodReturnTypeProviderEvent $event) + private static function handleSetFetchMode(MethodReturnTypeProviderEvent $event): ?Union { $source = $event->getSource(); $call_args = $event->getCallArgs(); @@ -148,7 +148,7 @@ private static function handleFetch(MethodReturnTypeProviderEvent $event): ?Unio new Union([ new TScalar(), new TNull(), - ]) + ]), ), ]), ]), @@ -223,7 +223,7 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U new TNull(), ]), ]), - ]) + ]), ), ]); @@ -238,14 +238,14 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U new TNull(), ]), ]), - ]) + ]), ), ]); case 6: // PDO::FETCH_BOUND - list return new Union([ Type::getListAtomic( - Type::getBool() + Type::getBool(), ), ]); @@ -256,7 +256,7 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U new TScalar(), new TNull(), new TFalse(), - ]) + ]), ), ]); @@ -264,8 +264,8 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U return new Union([ Type::getListAtomic( new Union([ - $fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject() - ]) + $fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject(), + ]), ), ]); @@ -282,11 +282,11 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U new Union([ new TScalar(), new TNull(), - ]) + ]), ), ]), ]), - ]) + ]), ), ]); @@ -309,9 +309,9 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U new Union([ new TScalar(), new TNull(), - ]) + ]), ), - ]) + ]), ), ]); @@ -319,8 +319,8 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U return new Union([ Type::getListAtomic( new Union([ - new TNamedObject('stdClass') - ]) + new TNamedObject('stdClass'), + ]), ), ]); } From 3d525cbd0eef1a4f57e90788228acfe059b0ab5e Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 27 Jul 2023 01:48:00 +0200 Subject: [PATCH 35/39] #9974 cleanup --- .../ReturnTypeProvider/PdoStatementReturnTypeProvider.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index 11c49f42ee0..e4236b24e5a 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -54,6 +54,9 @@ private static function handleSetFetchMode(MethodReturnTypeProviderEvent $event) $call_args = $event->getCallArgs(); $context = $event->getContext(); + $context->references_in_scope['fetch_mode'] = null; + $context->references_in_scope['fetch_class'] = null; + if (isset($call_args[0]) && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value)) && $first_arg_type->isSingleIntLiteral() From 13d53ecbf10c31492e17bd87fda662e43ed4a920 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 27 Jul 2023 01:59:06 +0200 Subject: [PATCH 36/39] #9974 cleanup --- .../ReturnTypeProvider/PdoStatementReturnTypeProvider.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index e4236b24e5a..a1c5963d798 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -54,14 +54,14 @@ private static function handleSetFetchMode(MethodReturnTypeProviderEvent $event) $call_args = $event->getCallArgs(); $context = $event->getContext(); - $context->references_in_scope['fetch_mode'] = null; - $context->references_in_scope['fetch_class'] = null; + unset($context->references_in_scope['fetch_mode']); + unset($context->references_in_scope['fetch_class']); if (isset($call_args[0]) && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value)) && $first_arg_type->isSingleIntLiteral() ) { - $context->references_in_scope['fetch_mode'] = $first_arg_type->getSingleIntLiteral()->value; + $context->references_in_scope['fetch_mode'] = (string) $first_arg_type->getSingleIntLiteral()->value; } if (isset($call_args[1]) From d600705b0c8ab0424fff0be3cb9872fc97778519 Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 27 Jul 2023 15:53:05 +0200 Subject: [PATCH 37/39] #9974 removed references_in_scope for setFetchMode --- .../PdoStatementReturnTypeProvider.php | 44 ++----------------- tests/MethodCallTest.php | 23 +++++++--- 2 files changed, 22 insertions(+), 45 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index a1c5963d798..593be23ce1d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -33,10 +33,6 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event) return null; } - if ($method_name_lowercase === 'setfetchmode') { - return self::handleSetFetchMode($event); - } - if ($method_name_lowercase === 'fetch') { return self::handleFetch($event); } @@ -48,39 +44,11 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event) return null; } - private static function handleSetFetchMode(MethodReturnTypeProviderEvent $event): ?Union - { - $source = $event->getSource(); - $call_args = $event->getCallArgs(); - $context = $event->getContext(); - - unset($context->references_in_scope['fetch_mode']); - unset($context->references_in_scope['fetch_class']); - - if (isset($call_args[0]) - && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value)) - && $first_arg_type->isSingleIntLiteral() - ) { - $context->references_in_scope['fetch_mode'] = (string) $first_arg_type->getSingleIntLiteral()->value; - } - - if (isset($call_args[1]) - && ($second_arg_type = $source->getNodeTypeProvider()->getType($call_args[1]->value)) - && $second_arg_type->isSingleStringLiteral() - ) { - $context->references_in_scope['fetch_class'] = $second_arg_type->getSingleStringLiteral()->value; - } - - return null; - } - private static function handleFetch(MethodReturnTypeProviderEvent $event): ?Union { $source = $event->getSource(); $call_args = $event->getCallArgs(); - $context = $event->getContext(); - - $fetch_mode = $context->references_in_scope['fetch_mode'] ?? null; + $fetch_mode = 0; if (isset($call_args[0]) && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value)) @@ -89,8 +57,6 @@ private static function handleFetch(MethodReturnTypeProviderEvent $event): ?Unio $fetch_mode = $first_arg_type->getSingleIntLiteral()->value; } - $fetch_class_name = $context->references_in_scope['fetch_class'] ?? null; - switch ($fetch_mode) { case 2: // PDO::FETCH_ASSOC - array|false return new Union([ @@ -128,7 +94,7 @@ private static function handleFetch(MethodReturnTypeProviderEvent $event): ?Unio case 8: // PDO::FETCH_CLASS - object|false return new Union([ - $fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject(), + new TObject(), new TFalse(), ]); @@ -194,9 +160,7 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U { $source = $event->getSource(); $call_args = $event->getCallArgs(); - $context = $event->getContext(); - - $fetch_mode = $context->references_in_scope['fetch_mode'] ?? null; + $fetch_mode = 0; if (isset($call_args[0]) && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value)) @@ -205,7 +169,7 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U $fetch_mode = $first_arg_type->getSingleIntLiteral()->value; } - $fetch_class_name = $context->references_in_scope['fetch_class'] ?? null; + $fetch_class_name = null; if (isset($call_args[1]) && ($second_arg_type = $source->getNodeTypeProvider()->getType($call_args[1]->value)) diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index f15f55ab489..4b8ce13ea94 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -509,22 +509,35 @@ class B extends A {} $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt = $db->prepare("select \"a\" as a"); $stmt->setFetchMode(PDO::FETCH_CLASS, A::class); + $stmt2 = $db->prepare("select \"a\" as a"); + $stmt2->setFetchMode(PDO::FETCH_ASSOC); $stmt->execute(); + $stmt2->execute(); + /** @psalm-suppress MixedAssignment */ $a = $stmt->fetch(); $b = $stmt->fetchAll(); $c = $stmt->fetch(PDO::FETCH_CLASS); $d = $stmt->fetchAll(PDO::FETCH_CLASS); $e = $stmt->fetchAll(PDO::FETCH_CLASS, B::class); $f = $stmt->fetch(PDO::FETCH_ASSOC); - $g = $stmt->fetchAll(PDO::FETCH_ASSOC);', + $g = $stmt->fetchAll(PDO::FETCH_ASSOC); + /** @psalm-suppress MixedAssignment */ + $h = $stmt2->fetch(); + $i = $stmt2->fetchAll(); + $j = $stmt2->fetch(PDO::FETCH_BOTH); + $k = $stmt2->fetchAll(PDO::FETCH_BOTH);', 'assertions' => [ - '$a' => 'A|false', - '$b' => 'list', - '$c' => 'A|false', - '$d' => 'list', + '$a' => 'mixed', + '$b' => 'array|false', + '$c' => 'false|object', + '$d' => 'list', '$e' => 'list', '$f' => 'array|false', '$g' => 'list>', + '$h' => 'mixed', + '$i' => 'array|false', + '$j' => 'array|false', + '$k' => 'list>', ], ], 'datePeriodConstructor' => [ From ee505259aab463c39b8b802c6db27a4f6c04c94f Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 27 Jul 2023 15:59:31 +0200 Subject: [PATCH 38/39] #9974 extended test with PDO::ATTR_DEFAULT_FETCH_MODE for future implementation --- tests/MethodCallTest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 4b8ce13ea94..f6cb3e75099 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -507,10 +507,13 @@ class B extends A {} $db = new PDO("sqlite::memory:"); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $stmt = $db->prepare("select \"a\" as a"); $stmt->setFetchMode(PDO::FETCH_CLASS, A::class); $stmt2 = $db->prepare("select \"a\" as a"); $stmt2->setFetchMode(PDO::FETCH_ASSOC); + $stmt3 = $db->prepare("select \"a\" as a"); + $stmt3->setFetchMode(PDO::ATTR_DEFAULT_FETCH_MODE); $stmt->execute(); $stmt2->execute(); /** @psalm-suppress MixedAssignment */ @@ -525,7 +528,9 @@ class B extends A {} $h = $stmt2->fetch(); $i = $stmt2->fetchAll(); $j = $stmt2->fetch(PDO::FETCH_BOTH); - $k = $stmt2->fetchAll(PDO::FETCH_BOTH);', + $k = $stmt2->fetchAll(PDO::FETCH_BOTH); + /** @psalm-suppress MixedAssignment */ + $l = $stmt3->fetch();', 'assertions' => [ '$a' => 'mixed', '$b' => 'array|false', @@ -538,6 +543,7 @@ class B extends A {} '$i' => 'array|false', '$j' => 'array|false', '$k' => 'list>', + '$l' => 'mixed', ], ], 'datePeriodConstructor' => [ From 88ddb8976305ceae7b6cdb609270393195b8435e Mon Sep 17 00:00:00 2001 From: Thomas Bley Date: Thu, 27 Jul 2023 16:11:32 +0200 Subject: [PATCH 39/39] #9974 fixed return type for FETCH_COLUMN --- .../ReturnTypeProvider/PdoStatementReturnTypeProvider.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index 593be23ce1d..3edc2342bd1 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -216,13 +216,12 @@ private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?U ), ]); - case 7: // PDO::FETCH_COLUMN - scalar|null|false + case 7: // PDO::FETCH_COLUMN - list return new Union([ Type::getListAtomic( new Union([ new TScalar(), new TNull(), - new TFalse(), ]), ), ]);